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_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; }
/* Returns 0 == success, -1 == failure */ static int write_manifest_plain(struct manifest *manifest) { GList *includes; GList *list; struct file *file; FILE *out = NULL; char *base = NULL, *dir; char *conf = config_output_dir(); char *filename = NULL; char *submanifest_filename = NULL; int ret = -1; if (conf == NULL) { assert(0); } string_or_die(&filename, "%s/%i/Manifest.%s", conf, manifest->version, manifest->component); base = strdup(filename); if (base == NULL) { assert(0); } dir = dirname(base); if (g_mkdir_with_parents(dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) { assert(0); } out = fopen(filename, "w"); if (out == NULL) { printf("Failed to open %s for write\n", filename); goto exit; } fprintf(out, "MANIFEST\t%llu\n", format); fprintf(out, "version:\t%i\n", manifest->version); fprintf(out, "previous:\t%i\n", manifest->prevversion); fprintf(out, "filecount:\t%i\n", manifest->count); fprintf(out, "timestamp:\t%i\n", (int)time(NULL)); compute_content_size(manifest); fprintf(out, "contentsize:\t%llu\n", (long long unsigned int)manifest->contentsize); includes = manifest->includes; while (includes) { struct manifest *sub = includes->data; includes = g_list_next(includes); fprintf(out, "includes:\t%s\n", sub->component); } fprintf(out, "\n"); list = g_list_first(manifest->files); while (list) { file = list->data; list = g_list_next(list); fprintf(out, "%s\t%s\t%i\t%s\n", file_type_to_string(file), file->hash, file->last_change, file->filename); } list = g_list_first(manifest->manifests); while (list) { file = list->data; list = g_list_next(list); string_or_die(&submanifest_filename, "%s/%i/Manifest.%s", conf, file->last_change, file->filename); populate_file_struct(file, submanifest_filename); ret = compute_hash(file, submanifest_filename); if (ret != 0) { printf("Hash computation failed\n"); assert(0); } fprintf(out, "%s\t%s\t%i\t%s\n", file_type_to_string(file), file->hash, file->last_change, file->filename); free(submanifest_filename); } ret = 0; exit: if (out) { fclose(out); } free(conf); free(base); free(filename); return ret; }