Example #1
0
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);
    }
}
Example #2
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;
}
Example #3
0
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;
}