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; }
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; }
/* * 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; }
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; }