static void erase_range(uint32_t start, uint32_t size, bool will_program) { uint32_t done = 0; int rc; printf("About to erase 0x%08x..0x%08x !\n", start, start + size); check_confirm(); if (dummy_run) { printf("skipped (dummy)\n"); return; } printf("Erasing...\n"); progress_init(size >> 8); while(size) { /* If aligned to 64k and at least 64k, use 64k erase */ if ((start & 0xffff) == 0 && size >= 0x10000) { rc = blocklevel_erase(bl, start, 0x10000); if (rc) { fprintf(stderr, "Error %d erasing 0x%08x\n", rc, start); exit(1); } start += 0x10000; size -= 0x10000; done += 0x10000; } else { rc = blocklevel_erase(bl, start, 0x1000); if (rc) { fprintf(stderr, "Error %d erasing 0x%08x\n", rc, start); exit(1); } start += 0x1000; size -= 0x1000; done += 0x1000; } progress_tick(done >> 8); } progress_end(); /* If this is a flash partition, mark it empty if we aren't * going to program over it as well */ if (ffsh && ffs_index >= 0 && !will_program) { printf("Updating actual size in partition header...\n"); ffs_update_act_size(ffsh, ffs_index, 0); } }
int blocklevel_smart_erase(struct blocklevel_device *bl, uint64_t pos, uint64_t len) { uint64_t block_size; void *erase_buf; int rc; if (!bl) { errno = EINVAL; return FLASH_ERR_PARM_ERROR; } FL_DBG("%s: 0x%" PRIx64 "\t0x%" PRIx64 "\n", __func__, pos, len); /* Nothing smart needs to be done, pos and len are aligned */ if ((pos & bl->erase_mask) == 0 && (len & bl->erase_mask) == 0) { FL_DBG("%s: Skipping smarts everything is aligned 0x%" PRIx64 " 0x%" PRIx64 "to 0x%08x\n", __func__, pos, len, bl->erase_mask); return blocklevel_erase(bl, pos, len); } block_size = bl->erase_mask + 1; erase_buf = malloc(block_size); if (!erase_buf) { errno = ENOMEM; return FLASH_ERR_MALLOC_FAILED; } rc = reacquire(bl); if (rc) { free(erase_buf); return rc; } if (pos & bl->erase_mask) { /* * base_pos and base_len are the values in the first erase * block that we need to preserve: the region up to pos. */ uint64_t base_pos = pos & ~(bl->erase_mask); uint64_t base_len = pos - base_pos; FL_DBG("%s: preserving 0x%" PRIx64 "..0x%" PRIx64 "\n", __func__, base_pos, base_pos + base_len); /* * Read the entire block in case this is the ONLY block we're * modifying, we may need the end chunk of it later */ rc = bl->read(bl, base_pos, erase_buf, block_size); if (rc) goto out; rc = bl->erase(bl, base_pos, block_size); if (rc) goto out; rc = bl->write(bl, base_pos, erase_buf, base_len); if (rc) goto out; /* * The requested erase fits entirely into this erase block and * so we need to write back the chunk at the end of the block */ if (base_pos + base_len + len < base_pos + block_size) { rc = bl->write(bl, pos + len, erase_buf + base_len + len, block_size - base_len - len); FL_DBG("%s: Early exit, everything was in one erase block\n", __func__); goto out; } pos += block_size - base_len; len -= block_size - base_len; } /* Now we should be aligned, best to double check */ if (pos & bl->erase_mask) { FL_DBG("%s:pos 0x%" PRIx64 " isn't erase_mask 0x%08x aligned\n", __func__, pos, bl->erase_mask); rc = FLASH_ERR_PARM_ERROR; goto out; } if (len & ~(bl->erase_mask)) { rc = bl->erase(bl, pos, len & ~(bl->erase_mask)); if (rc) goto out; pos += len & ~(bl->erase_mask); len -= len & ~(bl->erase_mask); } /* Length should be less than a block now */ if (len > block_size) { FL_DBG("%s: len 0x%" PRIx64 " is still exceeds block_size 0x%" PRIx64 "\n", __func__, len, block_size); rc = FLASH_ERR_PARM_ERROR; goto out; } if (len & bl->erase_mask) { /* * top_pos is the first byte that must be preserved and * top_len is the length from top_pos to the end of the erase * block: the region that must be preserved */ uint64_t top_pos = pos + len; uint64_t top_len = block_size - len; FL_DBG("%s: preserving 0x%" PRIx64 "..0x%" PRIx64 "\n", __func__, top_pos, top_pos + top_len); rc = bl->read(bl, top_pos, erase_buf, top_len); if (rc) goto out; rc = bl->erase(bl, pos, block_size); if (rc) goto out; rc = bl->write(bl, top_pos, erase_buf, top_len); if (rc) goto out; } out: free(erase_buf); release(bl); 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; }