Example #1
0
static int tegra_bbu_emmc_handler(struct bbu_handler *handler,
                                  struct bbu_data *data)
{

    int fd, ret;

    if (file_detect_type(data->image + 0x4000, data->len) !=
            filetype_arm_barebox &&
            !bbu_force(data, "Not an ARM barebox image"))
        return -EINVAL;

    ret = bbu_confirm(data);
    if (ret)
        return ret;

    fd = open(data->devicefile, O_WRONLY);
    if (fd < 0)
        return fd;

    ret = write(fd, data->image, data->len);
    if (ret < 0) {
        pr_err("writing update to %s failed with %s\n",
               data->devicefile, strerror(-ret));
        goto err_close;
    }

    ret = 0;

err_close:
    close(fd);

    return ret;
}
/*
 * Update barebox on a v1 type internal boot (i.MX25, i.MX35, i.MX51)
 *
 * This constructs a DCD header, adds the specific DCD data and writes
 * the resulting image to the device. Currently this handles MMC/SD
 * devices.
 */
static int imx_bbu_internal_v1_update(struct bbu_handler *handler, struct bbu_data *data)
{
	struct imx_internal_bbu_handler *imx_handler =
		container_of(handler, struct imx_internal_bbu_handler, handler);
	struct imx_flash_header *flash_header;
	unsigned long flash_header_offset = imx_handler->flash_header_offset;
	u32 *dcd_image_size;
	void *imx_pre_image;
	int imx_pre_image_size = 0x2000;
	int ret, image_len;
	void *buf;

	if (file_detect_type(data->image, data->len) != filetype_arm_barebox) {
		if (!bbu_force(data, "Not an ARM barebox image"))
			return -EINVAL;
	}

	ret = bbu_confirm(data);
	if (ret)
		return ret;

	printf("updating to %s\n", data->devicefile);

	imx_pre_image = xzalloc(imx_pre_image_size);
	flash_header = imx_pre_image + flash_header_offset;

	flash_header->app_code_jump_vector = imx_handler->app_dest + 0x1000;
	flash_header->app_code_barker = APP_CODE_BARKER;
	flash_header->app_code_csf = 0;
	flash_header->dcd_ptr_ptr = imx_handler->app_dest + flash_header_offset +
		offsetof(struct imx_flash_header, dcd);
	flash_header->super_root_key = 0;
	flash_header->dcd = imx_handler->app_dest + flash_header_offset +
		offsetof(struct imx_flash_header, dcd_barker);
	flash_header->app_dest = imx_handler->app_dest;
	flash_header->dcd_barker = DCD_BARKER;
	flash_header->dcd_block_len = imx_handler->dcdsize;

	memcpy((void *)flash_header + sizeof(*flash_header), imx_handler->dcd, imx_handler->dcdsize);

	dcd_image_size = (imx_pre_image + flash_header_offset + sizeof(*flash_header) + imx_handler->dcdsize);

	*dcd_image_size = ALIGN(imx_pre_image_size + data->len, 4096);

	/* Create a buffer containing header and image data */
	image_len = data->len + imx_pre_image_size;
	buf = xzalloc(image_len);
	memcpy(buf, imx_pre_image, imx_pre_image_size);
	memcpy(buf + imx_pre_image_size, data->image, data->len);

	ret = imx_bbu_write_device(imx_handler, data, buf, image_len);

	free(buf);

	free(imx_pre_image);

	return ret;
}
static int imx_bbu_check_prereq(struct imx_internal_bbu_handler *imx_handler,
				const char *devicefile, struct bbu_data *data,
				enum filetype expected_type)
{
	int ret;
	const void *blob;
	size_t len;
	enum filetype type;

	type = file_detect_type(data->image, data->len);

	switch (type) {
	case filetype_arm_barebox:
		/*
		 * Specifying expected_type as unknown will disable the
		 * inner image type check.
		 *
		 * The only user of this code is
		 * imx_bbu_external_nor_register_handler() used by
		 * i.MX27.
		 */
		if (expected_type == filetype_unknown)
			break;

		blob = data->image + imx_handler->flash_header_offset;
		len  = data->len   - imx_handler->flash_header_offset;
		type = file_detect_type(blob, len);

		if (type != expected_type) {
			pr_err("Expected image type: %s, "
			       "detected image type: %s\n",
			       file_type_to_string(expected_type),
			       file_type_to_string(type));
			return -EINVAL;
		}
		break;
	default:
		if (!bbu_force(data, "Not an ARM barebox image"))
			return -EINVAL;
	}

	ret = bbu_confirm(data);
	if (ret)
		return ret;

	device_detect_by_name(devpath_to_name(devicefile));

	return 0;
}
Example #4
0
static int imx_bbu_check_prereq(struct bbu_data *data)
{
	int ret;

	if (file_detect_type(data->image, data->len) != filetype_arm_barebox) {
		if (!bbu_force(data, "Not an ARM barebox image"))
			return -EINVAL;
	}

	ret = bbu_confirm(data);
	if (ret)
		return ret;

	return 0;
}
static int imx_bbu_external_nand_update(struct bbu_handler *handler, struct bbu_data *data)
{
	struct mtd_info_user meminfo;
	int fd;
	struct stat s;
	int size_available, size_need;
	int ret;
	uint32_t num_bb = 0, bbt = 0;
	uint64_t offset = 0;
	int block = 0, len, now, blocksize;
	void *image = data->image;

	ret = stat(data->devicefile, &s);
	if (ret)
		return ret;

	size_available = s.st_size;

	fd = open(data->devicefile, O_RDWR);
	if (fd < 0)
		return fd;

	ret = ioctl(fd, MEMGETINFO, &meminfo);
	if (ret)
		goto out;

	blocksize = meminfo.erasesize;

	size_need = data->len;

	/*
	 * Collect bad blocks and construct BBT
	 */
	while (size_need > 0) {
		ret = ioctl(fd, MEMGETBADBLOCK, &offset);
		if (ret < 0)
			goto out;

		if (ret) {
			if (!offset) {
				printf("1st block is bad. This is not supported\n");
				ret = -EINVAL;
				goto out;
			}

			debug("bad block at 0x%08llx\n", offset);
			num_bb++;
			bbt |= (1 << block);
			offset += blocksize;
			block++;
			continue;
		}
		size_need -= blocksize;
		size_available -= blocksize;
		offset += blocksize;
		block++;

		if (size_available < 0) {
			printf("device is too small.\n"
					"raw partition size: 0x%08llx\n"
					"partition size w/o bad blocks: 0x%08llx\n"
					"size needed: 0x%08x\n",
					s.st_size,
					s.st_size - num_bb * blocksize,
					data->len);
			ret = -ENOSPC;
			goto out;
		}
	}

	debug("total image size: 0x%08x. Space needed including bad blocks: 0x%08x\n",
			data->len, data->len + num_bb * blocksize);

	if (meminfo.writesize >= 2048) {
		uint32_t *image_bbt = image + ARM_HEAD_SPARE_OFS;

		debug(">= 2k nand detected. Creating in-image bbt\n");

		if (*image_bbt != ARM_HEAD_SPARE_MARKER) {
			if (!bbu_force(data, "Cannot find space for the BBT")) {
				ret = -EINVAL;
				goto out;
			}
		} else {
			*image_bbt++ = IMX_NAND_BBT_MAGIC;
			*image_bbt++ = bbt;
		}
	}

	if (data->len + num_bb * blocksize > s.st_size) {
		printf("needed space (0x%08x) exceeds partition space (0x%08llx)\n",
				data->len + num_bb * blocksize, s.st_size);
		ret = -ENOSPC;
		goto out;
	}

	len = data->len;
	offset = 0;

	/* last chance before erasing the flash */
	ret = bbu_confirm(data);
	if (ret)
		return ret;

	/*
	 * Write image to NAND skipping bad blocks
	 */
	while (len > 0) {
		now = min(len, blocksize);

		ret = ioctl(fd, MEMGETBADBLOCK, &offset);
		if (ret < 0)
			goto out;

		if (ret) {
			ret = lseek(fd, offset + blocksize, SEEK_SET);
			if (ret < 0)
				goto out;
			offset += blocksize;
			continue;
		}

		debug("writing %d bytes at 0x%08llx\n", now, offset);

		ret = erase(fd, blocksize, offset);
		if (ret)
			goto out;

		ret = write(fd, image, now);
		if (ret < 0)
			goto out;

		len -= now;
		image += now;
		offset += now;
	}

	ret = 0;

out:
	close(fd);

	return ret;
}
Example #6
0
/*
 * AM35xx, AM33xx chips use big endian MLO for SPI NOR flash
 * This handler converting MLO to big endian and write to SPI NOR
 */
static int spi_nor_mlo_handler(struct bbu_handler *handler,
					struct bbu_data *data)
{
	int dstfd = 0;
	int ret = 0;
	uint32_t readbuf;
	int size = data->len;
	void *image = data->image;
	uint32_t *header;
	int swap = 0;
	struct stat s;

	header = data->image;

	if (header[5] == 0x43485345 || header[10] == 0x62617265) {
		swap = 0;
	} else if (header[5] == 0x45534843 || header[10] == 0x65726142) {
		swap = 1;
	} else {
		if (!bbu_force(data, "Not a MLO image"))
			return -EINVAL;
	}

	ret = stat(data->devicefile, &s);
	if (ret) {
		printf("could not open %s: %s", data->devicefile, errno_str());
		return ret;
	}

	if (size > s.st_size) {
		printf("Image too big, need %d, have %lld\n", size, s.st_size);
		return -ENOSPC;
	}

	ret = bbu_confirm(data);
	if (ret != 0)
		return ret;

	dstfd = open(data->devicefile, O_WRONLY);
	if (dstfd < 0) {
		printf("could not open %s: %s", data->devicefile, errno_str());
		ret = dstfd;
		goto out;
	}

	ret = erase(dstfd, ERASE_SIZE_ALL, 0);
	if (ret < 0) {
		printf("could not erase %s: %s", data->devicefile, errno_str());
		goto out1;
	}

	for (; size >= 0; size -= 4) {
		memcpy((char *)&readbuf, image, 4);

		if (swap)
			readbuf = cpu_to_be32(readbuf);
		ret = write(dstfd, &readbuf, 4);
		if (ret < 0) {
			perror("write");
			goto out1;
		}

		image = image + 4;
	}

	ret = 0;
out1:
	close(dstfd);
out:
	return ret;
}
Example #7
0
static int imx_bbu_nand_update(struct bbu_handler *handler, struct bbu_data *data)
{
	struct imx_nand_fcb_bbu_handler *imx_handler =
		container_of(handler, struct imx_nand_fcb_bbu_handler, handler);
	struct cdev *bcb_cdev;
	struct mtd_info *mtd;
	int ret, i;
	struct fcb_block *fcb = NULL;
	void *fw = NULL, *fw_orig = NULL;
	unsigned fw_size, partition_size;
	enum filetype filetype;
	unsigned num_blocks_fw;
	int pages_per_block;
	int used = 0;
	int fw_orig_len;
	int used_refresh = 0, unused_refresh = 0;

	if (data->image) {
		filetype = file_detect_type(data->image, data->len);

		if (filetype != imx_handler->filetype &&
			!bbu_force(data, "Image is not of type %s but of type %s",
				file_type_to_string(imx_handler->filetype),
				file_type_to_string(filetype)))
			return -EINVAL;
	}

	bcb_cdev = cdev_by_name(handler->devicefile);
	if (!bcb_cdev) {
		pr_err("%s: No FCB device!\n", __func__);
		return -ENODEV;
	}

	mtd = bcb_cdev->mtd;
	partition_size = mtd->size;
	pages_per_block = mtd->erasesize / mtd->writesize;

	for (i = 0; i < 4; i++) {
		read_fcb(mtd, i, &fcb);
		if (fcb)
			break;
	}

	/*
	 * This code uses the following layout in the Nand flash:
	 *
	 * fwmaxsize = (n_blocks - 4) / 2
	 *
	 * block
	 *
	 * 0              ----------------------
	 *                | FCB/DBBT 0         |
	 * 1              ----------------------
	 *                | FCB/DBBT 1         |
	 * 2              ----------------------
	 *                | FCB/DBBT 2         |
	 * 3              ----------------------
	 *                | FCB/DBBT 3         |
	 * 4              ----------------------
	 *                | Firmware slot 0    |
	 * 4 + fwmaxsize  ----------------------
	 *                | Firmware slot 1    |
	 *                ----------------------
	 *
	 * We want a robust update in which a power failure may occur
	 * everytime without bricking the board, so here's the strategy:
	 *
	 * The FCBs contain pointers to the firmware slots in the
	 * Firmware1_startingPage and Firmware2_startingPage fields. Note that
	 * Firmware1_startingPage doesn't necessarily point to slot 0. We
	 * exchange the pointers during update to atomically switch between the
	 * old and the new firmware.
	 *
	 * - We read the first valid FCB and the firmware slots.
	 * - We check which firmware slot is currently used by the ROM:
	 *    - if no FCB is found or its layout differs from the above layout,
	 *      continue without robust update
	 *   - if only one firmware slot is readable, the ROM uses it
	 *   - if both slots are readable, the ROM will use slot 0
	 * - Step 1: erase/update the slot currently unused by the ROM
	 * - Step 2: Update FCBs/DBBTs, thereby letting Firmware1_startingPage
	 *           point to the slot we just updated. From this moment
	 *           on the new firmware will be used and running a
	 *           refresh/repair after a power failure after this
	 *           step will complete the update.
	 * - Step 3: erase/update the other firmwre slot
	 * - Step 4: Eventually write FCBs/DBBTs again. This may become
	 *           necessary when step 3 revealed new bad blocks.
	 *
	 * This robust update only works when the original FCBs on the device
	 * uses the same layout as this code does. In other cases update will
	 * also work, but it won't be robust against power failures.
	 *
	 * Refreshing the firmware which is needed when blocks become unreadable
	 * due to read disturbance works the same way, only that the new firmware
	 * is the same as the old firmware and that it will only be written when
	 * reading from the device returns -EUCLEAN indicating that a block needs
	 * to be rewritten.
	 */
	if (fcb)
		read_firmware_all(mtd, fcb, &fw_orig, &fw_orig_len,
				  &used_refresh, &unused_refresh, &used);

	if (data->image) {
		/*
		 * We have to write one additional page to make the ROM happy.
		 * Maybe the PagesInFirmwarex fields are really the number of pages - 1.
		 * kobs-ng has the same.
		 */
		fw_size = ALIGN(data->len + mtd->writesize, mtd->writesize);
		fw = xzalloc(fw_size);
		memcpy(fw, data->image, data->len);
		free(fw_orig);
		used_refresh = 1;
		unused_refresh = 1;

		free(fcb);
		fcb = xzalloc(sizeof(*fcb));
		fcb->Firmware1_startingPage = imx_bbu_firmware_start_block(mtd, !used) * pages_per_block;
		fcb->Firmware2_startingPage = imx_bbu_firmware_start_block(mtd, used) * pages_per_block;
		fcb->PagesInFirmware1 = fw_size / mtd->writesize;
		fcb->PagesInFirmware2 = fcb->PagesInFirmware1;

		fcb_create(imx_handler, fcb, mtd);
	} else {
		if (!fcb) {
			pr_err("No FCB found on device, cannot refresh\n");
			ret = -EINVAL;
			goto out;
		}

		if (!fw_orig) {
			pr_err("No firmware found on device, cannot refresh\n");
			ret = -EINVAL;
			goto out;
		}

		fw = fw_orig;
		fw_size = fw_orig_len;
		pr_info("Refreshing existing firmware\n");
	}

	num_blocks_fw = imx_bbu_firmware_max_blocks(mtd);

	if (num_blocks_fw * mtd->erasesize < fw_size) {
		pr_err("Not enough space for update\n");
		return -ENOSPC;
	}

	ret = bbu_confirm(data);
	if (ret)
		goto out;

	/* Step 1: write firmware which is currently unused by the ROM */
	if (unused_refresh) {
		pr_info("%sing slot %d\n", data->image ? "updat" : "refresh", !used);
		ret = imx_bbu_write_firmware(mtd, !used, fw, fw_size);
		if (ret < 0)
			goto out;
	} else {
		pr_info("firmware slot %d still ok, nothing to do\n", !used);
	}

	/*
	 * Step 2: Write FCBs/DBBTs. This will use the firmware we have
	 * just written as primary firmware. From now on the new
	 * firmware will be booted.
	 */
	ret = imx_bbu_write_fcbs_dbbts(mtd, fcb);
	if (ret < 0)
		goto out;

	/* Step 3: Write the secondary firmware */
	if (used_refresh) {
		pr_info("%sing slot %d\n", data->image ? "updat" : "refresh", used);
		ret = imx_bbu_write_firmware(mtd, used, fw, fw_size);
		if (ret < 0)
			goto out;
	} else {
		pr_info("firmware slot %d still ok, nothing to do\n", used);
	}

	/*
	 * Step 4: If writing the secondary firmware discovered new bad
	 * blocks, write the FCBs/DBBTs again with updated bad block
	 * information.
	 */
	if (ret > 0) {
		pr_info("New bad blocks detected, writing FCBs/DBBTs again\n");
		ret = imx_bbu_write_fcbs_dbbts(mtd, fcb);
		if (ret < 0)
			goto out;
	}

out:
	free(fw);
	free(fcb);

	return ret;
}
/*
 * Update barebox on a v2 type internal boot (i.MX53)
 *
 * This constructs a DCD header, adds the specific DCD data and writes
 * the resulting image to the device. Currently this handles MMC/SD
 * and NAND devices.
 */
static int imx_bbu_internal_v2_update(struct bbu_handler *handler, struct bbu_data *data)
{
	struct imx_internal_bbu_handler *imx_handler =
		container_of(handler, struct imx_internal_bbu_handler, handler);
	struct imx_flash_header_v2 *flash_header;
	unsigned long flash_header_offset = imx_handler->flash_header_offset;
	void *imx_pre_image;
	int imx_pre_image_size;
	int ret, image_len;
	void *buf;

	if (file_detect_type(data->image, data->len) != filetype_arm_barebox) {
		if (!bbu_force(data, "Not an ARM barebox image"))
			return -EINVAL;
	}

	ret = bbu_confirm(data);
	if (ret)
		return ret;

	printf("updating to %s\n", data->devicefile);

	if (imx_handler->flags & IMX_INTERNAL_FLAG_NAND)
		/* NAND needs additional space for the DBBT */
		imx_pre_image_size = 0x8000;
	else
		imx_pre_image_size = 0x2000;

	imx_pre_image = xzalloc(imx_pre_image_size);
	flash_header = imx_pre_image + flash_header_offset;

	flash_header->header.tag = IVT_HEADER_TAG;
	flash_header->header.length = cpu_to_be16(32);
	flash_header->header.version = IVT_VERSION;

	flash_header->entry = imx_handler->app_dest + imx_pre_image_size;
	if (imx_handler->dcdsize)
		flash_header->dcd_ptr = imx_handler->app_dest + flash_header_offset +
			offsetof(struct imx_flash_header_v2, dcd);
	flash_header->boot_data_ptr = imx_handler->app_dest +
		flash_header_offset + offsetof(struct imx_flash_header_v2, boot_data);
	flash_header->self = imx_handler->app_dest + flash_header_offset;

	flash_header->boot_data.start = imx_handler->app_dest;
	flash_header->boot_data.size = ALIGN(imx_pre_image_size + data->len, 4096);;

	if (imx_handler->dcdsize) {
		flash_header->dcd.header.tag = DCD_HEADER_TAG;
		flash_header->dcd.header.length = cpu_to_be16(sizeof(struct imx_dcd) +
				imx_handler->dcdsize);
		flash_header->dcd.header.version = DCD_VERSION;
	}

	/* Add dcd data */
	memcpy((void *)flash_header + sizeof(*flash_header), imx_handler->dcd, imx_handler->dcdsize);

	/* Create a buffer containing header and image data */
	image_len = data->len + imx_pre_image_size;
	buf = xzalloc(image_len);
	memcpy(buf, imx_pre_image, imx_pre_image_size);
	memcpy(buf + imx_pre_image_size, data->image, data->len);

	if (imx_handler->flags & IMX_INTERNAL_FLAG_NAND) {
		ret = imx_bbu_internal_v2_write_nand_dbbt(imx_handler, data, buf,
				image_len);
		goto out_free_buf;
	}

	ret = imx_bbu_write_device(imx_handler, data, buf, image_len);

out_free_buf:
	free(buf);

	free(imx_pre_image);
	return ret;
}