int blocklevel_write(struct blocklevel_device *bl, uint32_t pos, const void *buf, uint32_t len) { int rc; struct ecc64 *buffer; uint32_t ecc_len = ecc_buffer_size(len); if (!bl || !bl->write || !buf) { errno = EINVAL; return FLASH_ERR_PARM_ERROR; } if (!ecc_protected(bl, pos, len)) { return bl->write(bl, pos, buf, len); } buffer = malloc(ecc_len); if (!buffer) { errno = ENOMEM; return FLASH_ERR_MALLOC_FAILED; } if (memcpy_to_ecc(buffer, buf, len)) { errno = EBADF; rc = FLASH_ERR_ECC_INVALID; goto out; } rc = bl->write(bl, pos, buffer, ecc_len); out: free(buffer); return rc; }
int flash_smart_write_corrected(struct blocklevel_device *bl, uint32_t dst, const void *src, uint32_t size, bool ecc) { struct ecc64 *buf; int rc; if (!ecc) return flash_smart_write(bl, dst, src, size); buf = malloc(ecc_buffer_size(size)); if (!buf) return FLASH_ERR_MALLOC_FAILED; rc = memcpy_to_ecc(buf, src, size); if (rc) { rc = FLASH_ERR_ECC_INVALID; goto out; } rc = flash_smart_write(bl, dst, buf, ecc_buffer_size(size)); out: free(buf); return rc; }
int flash_write_corrected(struct blocklevel_device *bl, uint32_t pos, const void *buf, uint32_t len, bool verify, bool ecc) { struct ecc64 *bufecc; uint32_t copylen, copylen_minus_ecc; int rc; uint8_t ret; if (!ecc) return flash_write(bl, pos, buf, len, verify); /* Copy the buffer in chunks */ bufecc = malloc(ecc_buffer_size(COPY_BUFFER_LENGTH)); if (!bufecc) return FLASH_ERR_MALLOC_FAILED; while (len > 0) { /* What's left to copy? */ copylen = MIN(len, COPY_BUFFER_LENGTH); copylen_minus_ecc = ecc_buffer_size_minus_ecc(copylen); /* Add the ecc byte to the data */ ret = memcpy_to_ecc(bufecc, buf, copylen_minus_ecc); if (ret) { rc = FLASH_ERR_ECC_INVALID; goto err; } /* Write ECCed data to the flash */ rc = flash_write(bl, pos, bufecc, copylen, verify); if (rc) goto err; /* Update for next copy */ len -= copylen_minus_ecc; buf = (uint8_t *)buf + copylen_minus_ecc; pos += copylen; } rc = 0; err: free(bufecc); return rc; }
int blocklevel_smart_write(struct blocklevel_device *bl, uint64_t pos, const void *buf, uint64_t len) { uint32_t erase_size; const void *write_buf = buf; void *write_buf_start = NULL; uint64_t ecc_start; void *erase_buf; int rc = 0; if (!write_buf || !bl) { errno = EINVAL; return FLASH_ERR_PARM_ERROR; } FL_DBG("%s: 0x%" PRIx64 "\t0x%" PRIx64 "\n", __func__, pos, len); if (!(bl->flags & WRITE_NEED_ERASE)) { FL_DBG("%s: backend doesn't need erase\n", __func__); return blocklevel_write(bl, pos, buf, len); } rc = blocklevel_get_info(bl, NULL, NULL, &erase_size); if (rc) return rc; if (ecc_protected(bl, pos, len, &ecc_start)) { FL_DBG("%s: region has ECC\n", __func__); len = ecc_buffer_size(len); write_buf_start = malloc(len); if (!write_buf_start) { errno = ENOMEM; return FLASH_ERR_MALLOC_FAILED; } if (memcpy_to_ecc(write_buf_start, buf, ecc_buffer_size_minus_ecc(len))) { free(write_buf_start); errno = EBADF; return FLASH_ERR_ECC_INVALID; } write_buf = write_buf_start; } erase_buf = malloc(erase_size); if (!erase_buf) { errno = ENOMEM; rc = FLASH_ERR_MALLOC_FAILED; goto out_free; } rc = reacquire(bl); if (rc) goto out_free; while (len > 0) { uint32_t erase_block = pos & ~(erase_size - 1); uint32_t block_offset = pos & (erase_size - 1); uint32_t size = erase_size > len ? len : erase_size; int cmp; /* Write crosses an erase boundary, shrink the write to the boundary */ if (erase_size < block_offset + size) { size = erase_size - block_offset; } rc = bl->read(bl, erase_block, erase_buf, erase_size); if (rc) goto out; cmp = blocklevel_flashcmp(erase_buf + block_offset, write_buf, size); FL_DBG("%s: region 0x%08x..0x%08x ", __func__, erase_block, erase_size); if (cmp != 0) { FL_DBG("needs "); if (cmp == -1) { FL_DBG("erase and "); bl->erase(bl, erase_block, erase_size); } FL_DBG("write\n"); memcpy(erase_buf + block_offset, write_buf, size); rc = bl->write(bl, erase_block, erase_buf, erase_size); if (rc) goto out; } len -= size; pos += size; write_buf += size; } out: release(bl); out_free: free(write_buf_start); free(erase_buf); return rc; }
int blocklevel_write(struct blocklevel_device *bl, uint64_t pos, const void *buf, uint64_t len) { int rc, ecc_protection; struct ecc64 *buffer; uint64_t ecc_len; uint64_t ecc_start, ecc_pos, ecc_diff; FL_DBG("%s: 0x%" PRIx64 "\t%p\t0x%" PRIx64 "\n", __func__, pos, buf, len); if (!bl || !buf) { errno = EINVAL; return FLASH_ERR_PARM_ERROR; } ecc_protection = ecc_protected(bl, pos, len, &ecc_start); FL_DBG("%s: 0x%" PRIx64 " for 0x%" PRIx64 " ecc=%s\n", __func__, pos, len, ecc_protection ? (ecc_protection == -1 ? "partial" : "yes") : "no"); if (!ecc_protection) return blocklevel_raw_write(bl, pos, buf, len); /* * The region we're writing to has both ecc protection and not. * Perhaps one day in the future blocklevel can cope with this. */ if (ecc_protection == -1) { FL_ERR("%s: Can't cope with partial ecc\n", __func__); errno = EINVAL; return FLASH_ERR_PARM_ERROR; } pos = with_ecc_pos(ecc_start, pos); ecc_pos = ecc_buffer_align(ecc_start, pos); ecc_diff = pos - ecc_pos; ecc_len = ecc_buffer_size(len + ecc_diff); FL_DBG("%s: adjusted_pos: 0x%" PRIx64 ", ecc_pos: 0x%" PRIx64 ", ecc_diff: 0x%" PRIx64 ", ecc_len: 0x%" PRIx64 "\n", __func__, pos, ecc_pos, ecc_diff, ecc_len); buffer = malloc(ecc_len); if (!buffer) { errno = ENOMEM; rc = FLASH_ERR_MALLOC_FAILED; goto out; } if (ecc_diff) { uint64_t start_chunk = ecc_diff; uint64_t end_chunk = BYTES_PER_ECC - ecc_diff; uint64_t end_len = ecc_len - end_chunk; /* * Read the start bytes that memcpy_to_ecc_unaligned() will need * to calculate the first ecc byte */ rc = blocklevel_raw_read(bl, ecc_pos, buffer, start_chunk); if (rc) { errno = EBADF; rc = FLASH_ERR_ECC_INVALID; goto out; } /* * Read the end bytes that memcpy_to_ecc_unaligned() will need * to calculate the last ecc byte */ rc = blocklevel_raw_read(bl, ecc_pos + end_len, ((char *)buffer) + end_len, end_chunk); if (rc) { errno = EBADF; rc = FLASH_ERR_ECC_INVALID; goto out; } if (memcpy_to_ecc_unaligned(buffer, buf, len, ecc_diff)) { errno = EBADF; rc = FLASH_ERR_ECC_INVALID; goto out; } } else { if (memcpy_to_ecc(buffer, buf, len)) { errno = EBADF; rc = FLASH_ERR_ECC_INVALID; goto out; } } rc = blocklevel_raw_write(bl, pos, buffer, ecc_len); out: free(buffer); return rc; }
int blocklevel_smart_write(struct blocklevel_device *bl, uint32_t pos, const void *buf, uint32_t len) { uint32_t erase_size; const void *write_buf = buf; void *write_buf_start = NULL; void *erase_buf; int rc = 0; if (!write_buf || !bl) { errno = EINVAL; return FLASH_ERR_PARM_ERROR; } if (!(bl->flags & WRITE_NEED_ERASE)) return blocklevel_write(bl, pos, buf, len); rc = blocklevel_get_info(bl, NULL, NULL, &erase_size); if (rc) return rc; if (ecc_protected(bl, pos, len)) { len = ecc_buffer_size(len); write_buf_start = malloc(len); if (!write_buf_start) { errno = ENOMEM; return FLASH_ERR_MALLOC_FAILED; } if (memcpy_to_ecc(write_buf_start, buf, ecc_buffer_size_minus_ecc(len))) { free(write_buf_start); errno = EBADF; return FLASH_ERR_ECC_INVALID; } write_buf = write_buf_start; } erase_buf = malloc(erase_size); if (!erase_buf) { errno = ENOMEM; rc = FLASH_ERR_MALLOC_FAILED; goto out; } while (len > 0) { uint32_t erase_block = pos & ~(erase_size - 1); uint32_t block_offset = pos & (erase_size - 1); uint32_t size = erase_size > len ? len : erase_size; int cmp; /* Write crosses an erase boundary, shrink the write to the boundary */ if (erase_size < block_offset + size) { size = erase_size - block_offset; } rc = bl->read(bl, erase_block, erase_buf, erase_size); if (rc) goto out; cmp = blocklevel_flashcmp(erase_buf + block_offset, write_buf, size); if (cmp != 0) { if (cmp == -1) bl->erase(bl, erase_block, erase_size); memcpy(erase_buf + block_offset, write_buf, size); rc = bl->write(bl, erase_block, erase_buf, erase_size); if (rc) goto out; } len -= size; pos += size; write_buf += size; } out: free(write_buf_start); free(erase_buf); return rc; }