int blocklevel_write(struct blocklevel_device *bl, uint64_t pos, const void *buf, uint64_t len) { int rc; struct ecc64 *buffer; uint64_t ecc_len = ecc_buffer_size(len); if (!bl || !buf) { errno = EINVAL; return FLASH_ERR_PARM_ERROR; } if (!ecc_protected(bl, pos, len)) return blocklevel_raw_write(bl, pos, buf, len); buffer = malloc(ecc_len); if (!buffer) { errno = ENOMEM; rc = FLASH_ERR_MALLOC_FAILED; goto out; } 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_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; }
static int64_t opal_flash_op(enum flash_op op, uint64_t id, uint64_t offset, uint64_t buf, uint64_t size, uint64_t token) { struct flash *flash = NULL; int rc; if (!try_lock(&flash_lock)) return OPAL_BUSY; list_for_each(&flashes, flash, list) if (flash->id == id) break; if (flash->id != id) { /* Couldn't find the flash */ rc = OPAL_PARAMETER; goto err; } if (flash->busy) { rc = OPAL_BUSY; goto err; } if (size >= flash->size || offset >= flash->size || offset + size > flash->size) { rc = OPAL_PARAMETER; goto err; } /* * These ops intentionally have no smarts (ecc correction or erase * before write) to them. * Skiboot is simply exposing the PNOR flash to the host. * The host is expected to understand that this is a raw flash * device and treat it as such. */ switch (op) { case FLASH_OP_READ: rc = blocklevel_raw_read(flash->bl, offset, (void *)buf, size); break; case FLASH_OP_WRITE: rc = blocklevel_raw_write(flash->bl, offset, (void *)buf, size); break; case FLASH_OP_ERASE: rc = blocklevel_erase(flash->bl, offset, size); break; default: assert(0); } if (rc) { rc = OPAL_HARDWARE; goto err; } unlock(&flash_lock); opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL, token, rc); return OPAL_ASYNC_COMPLETION; err: unlock(&flash_lock); return rc; }