/* * Copyright (c) International Business Machines Corp., 2006 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Oliver Lohmann * * Convert a PFI file (partial flash image) into a plain binary file. * This tool can be used to prepare the data to be burned into flash * chips in a manufacturing step where the flashes are written before * being soldered onto the hardware. For NAND images another step is * required to add the right OOB data to the binary image. * * 1.3 Removed argp because we want to use uClibc. * 1.4 Minor cleanups */ #include <stdlib.h> #include <stdint.h> #include <getopt.h> #include <stdio.h> #include <string.h> #include <assert.h> #include <errno.h> #include <ubigen.h> #include <mtd/ubi-media.h> #include <mtd_swab.h> #include "config.h" #include "list.h" #include "error.h" #include "reader.h" #include "peb.h" #include "crc32.h" #define PROGRAM_VERSION "1.4" #define MAX_FNAME 255 #define DEFAULT_ERASE_COUNT 0 /* Hmmm.... Perhaps */ #define ERR_BUF_SIZE 1024 #define MIN(a,b) ((a) < (b) ? (a) : (b)) static uint32_t crc32_table[256]; static char err_buf[ERR_BUF_SIZE]; /* * Data used to buffer raw blocks which have to be * located at a specific point inside the generated RAW file */ typedef enum action_t { ACT_NOTHING = 0x00000000, ACT_RAW = 0x00000001, } action_t; static const char copyright [] __attribute__((unused)) = "(c) Copyright IBM Corp 2006\n"; static char doc[] = "\nVersion: " PROGRAM_VERSION "\n" "pfi2bin - a tool to convert PFI files into binary images.\n"; static const char *optionsstr = " Common settings:\n" " -c, --copyright\n" " -v, --verbose Print more information.\n" "\n" " Input:\n" " -j, --platform=pdd-file PDD information which contains the card settings.\n" "\n" " Output:\n" " -o, --output=filename Outputfile, default: stdout.\n" "\n" " -?, --help Give this help list\n" " --usage Give a short usage message\n" " -V, --version Print program version\n"; static const char *usage = "Usage: pfi2bin [-cv?V] [-j pdd-file] [-o filename] [--copyright]\n" " [--verbose] [--platform=pdd-file] [--output=filename] [--help]\n" " [--usage] [--version] pfifile\n"; struct option long_options[] = { { .name = "copyright", .has_arg = 0, .flag = NULL, .val = 'c' }, { .name = "verbose", .has_arg = 0, .flag = NULL, .val = 'v' }, { .name = "platform", .has_arg = 1, .flag = NULL, .val = 'j' }, { .name = "output", .has_arg = 1, .flag = NULL, .val = 'o' }, { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' }, { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 }, { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' }, { NULL, 0, NULL, 0} }; typedef struct io { FILE* fp_pdd; /* a FilePointer to the PDD data */ FILE* fp_pfi; /* a FilePointer to the PFI input stream */ FILE* fp_out; /* a FilePointer to the output stream */ } *io_t; typedef struct myargs { /* common settings */ action_t action; int verbose; const char *f_in_pfi; const char *f_in_pdd; const char *f_out; /* special stuff needed to get additional arguments */ char *arg1; char **options; /* [STRING...] */ } myargs; static int parse_opt(int argc, char **argv, myargs *args) { while (1) { int key; key = getopt_long(argc, argv, "cvj:o:?V", long_options, NULL); if (key == -1) break; switch (key) { /* common settings */ case 'v': /* --verbose=<level> */ args->verbose = 1; break; case 'c': /* --copyright */ fprintf(stderr, "%s\n", copyright); exit(0); break; case 'j': /* --platform */ args->f_in_pdd = optarg; break; case 'o': /* --output */ args->f_out = optarg; break; case '?': /* help */ printf("pfi2bin [OPTION...] pfifile\n"); printf("%s", doc); printf("%s", optionsstr); printf("\nReport bugs to %s\n", PACKAGE_BUGREPORT); exit(0); break; case 'V': printf("%s\n", PROGRAM_VERSION); exit(0); break; default: printf("%s", usage); exit(-1); } } if (optind < argc) args->f_in_pfi = argv[optind++]; return 0; } static size_t byte_to_blk(size_t byte, size_t blk_size) { return (byte % blk_size) == 0 ? byte / blk_size : byte / blk_size + 1; } /** * @precondition IO: File stream points to first byte of RAW data. * @postcondition IO: File stream points to first byte of next * or EOF. */ static int memorize_raw_eb(pfi_raw_t pfi_raw, pdd_data_t pdd, list_t *raw_pebs, io_t io) { int rc = 0; uint32_t i; size_t read, to_read, eb_num; size_t bytes_left; list_t pebs = *raw_pebs; peb_t peb = NULL; long old_file_pos = ftell(io->fp_pfi); for (i = 0; i < pfi_raw->starts_size; i++) { bytes_left = pfi_raw->data_size; rc = fseek(io->fp_pfi, old_file_pos, SEEK_SET); if (rc != 0) goto err; eb_num = byte_to_blk(pfi_raw->starts[i], pdd->eb_size); while (bytes_left) { to_read = MIN(bytes_left, pdd->eb_size); rc = peb_new(eb_num++, pdd->eb_size, &peb); if (rc != 0) goto err; read = fread(peb->data, 1, to_read, io->fp_pfi); if (read != to_read) { rc = -EIO; goto err; } pebs = append_elem(peb, pebs); bytes_left -= read; } } *raw_pebs = pebs; return 0; err: pebs = remove_all((free_func_t)&peb_free, pebs); return rc; } static int convert_ubi_volume(pfi_ubi_t ubi, pdd_data_t pdd, list_t raw_pebs, struct ubi_vtbl_record *vol_tab, size_t *ebs_written, io_t io) { int rc = 0; uint32_t i, j; peb_t raw_peb; peb_t cmp_peb; ubi_info_t u; size_t leb_total = 0; uint8_t vol_type; switch (ubi->type) { case pfi_ubi_static: vol_type = UBI_VID_STATIC; break; case pfi_ubi_dynamic: vol_type = UBI_VID_DYNAMIC; break; default: vol_type = UBI_VID_DYNAMIC; } rc = peb_new(0, 0, &cmp_peb); if (rc != 0) goto err; long old_file_pos = ftell(io->fp_pfi); for (i = 0; i < ubi->ids_size; i++) { rc = fseek(io->fp_pfi, old_file_pos, SEEK_SET); if (rc != 0) goto err; rc = ubigen_create(&u, ubi->ids[i], vol_type, pdd->eb_size, DEFAULT_ERASE_COUNT, ubi->alignment, UBI_VERSION, pdd->vid_hdr_offset, 0, ubi->data_size, io->fp_pfi, io->fp_out); if (rc != 0) goto err; rc = ubigen_get_leb_total(u, &leb_total); if (rc != 0) goto err; j = 0; while(j < leb_total) { cmp_peb->num = *ebs_written; raw_peb = is_in((cmp_func_t)peb_cmp, cmp_peb, raw_pebs); if (raw_peb) { rc = peb_write(io->fp_out, raw_peb); } else { rc = ubigen_write_leb(u, NO_ERROR); j++; } if (rc != 0) goto err; (*ebs_written)++; } /* memorize volume table entry */ rc = ubigen_set_lvol_rec(u, ubi->size, ubi->names[i], (void*) &vol_tab[ubi->ids[i]]); if (rc != 0) goto err; ubigen_destroy(&u); } peb_free(&cmp_peb); return 0; err: peb_free(&cmp_peb); ubigen_destroy(&u); return rc; } static FILE* my_fmemopen (void *buf, size_t size, const char *opentype) { FILE* f; size_t ret; assert(strcmp(opentype, "r") == 0); f = tmpfile(); ret = fwrite(buf, 1, size, f); rewind(f); return f; } /** * @brief Builds a UBI volume table from a volume entry list. * @return 0 On success. * else Error. */ static int write_ubi_volume_table(pdd_data_t pdd, list_t raw_pebs, struct ubi_vtbl_record *vol_tab, size_t vol_tab_size, size_t *ebs_written, io_t io) { int rc = 0; ubi_info_t u; peb_t raw_peb; peb_t cmp_peb; size_t leb_size, leb_total, j = 0; uint8_t *ptr = NULL; FILE* fp_leb = NULL; int vt_slots; size_t vol_tab_size_limit; rc = peb_new(0, 0, &cmp_peb); if (rc != 0) goto err; /* @FIXME: Artem creates one volume with 2 LEBs. * IMO 2 volumes would be more convenient. In order * to get 2 reserved LEBs from ubigen, I have to * introduce this stupid mechanism. Until no final * decision of the VTAB structure is made... Good enough. */ rc = ubigen_create(&u, UBI_LAYOUT_VOLUME_ID, UBI_VID_DYNAMIC, pdd->eb_size, DEFAULT_ERASE_COUNT, 1, UBI_VERSION, pdd->vid_hdr_offset, UBI_COMPAT_REJECT, vol_tab_size, stdin, io->fp_out); /* @FIXME stdin for fp_in is a hack */ if (rc != 0) goto err; rc = ubigen_get_leb_size(u, &leb_size); if (rc != 0) goto err; ubigen_destroy(&u); /* * The number of supported volumes is restricted by the eraseblock size * and by the UBI_MAX_VOLUMES constant. */ vt_slots = leb_size / UBI_VTBL_RECORD_SIZE; if (vt_slots > UBI_MAX_VOLUMES) vt_slots = UBI_MAX_VOLUMES; vol_tab_size_limit = vt_slots * UBI_VTBL_RECORD_SIZE; ptr = (uint8_t*) malloc(leb_size * sizeof(uint8_t)); if (ptr == NULL) goto err; memset(ptr, 0xff, leb_size); memcpy(ptr, vol_tab, vol_tab_size_limit); fp_leb = my_fmemopen(ptr, leb_size, "r"); rc = ubigen_create(&u, UBI_LAYOUT_VOLUME_ID, UBI_VID_DYNAMIC, pdd->eb_size, DEFAULT_ERASE_COUNT, 1, UBI_VERSION, pdd->vid_hdr_offset, UBI_COMPAT_REJECT, leb_size * UBI_LAYOUT_VOLUME_EBS, fp_leb, io->fp_out); if (rc != 0) goto err; rc = ubigen_get_leb_total(u, &leb_total); if (rc != 0) goto err; long old_file_pos = ftell(fp_leb); while(j < leb_total) { rc = fseek(fp_leb, old_file_pos, SEEK_SET); if (rc != 0) goto err; cmp_peb->num = *ebs_written; raw_peb = is_in((cmp_func_t)peb_cmp, cmp_peb, raw_pebs); if (raw_peb) { rc = peb_write(io->fp_out, raw_peb); } else { rc = ubigen_write_leb(u, NO_ERROR); j++; } if (rc != 0) goto err; (*ebs_written)++; } err: free(ptr); peb_free(&cmp_peb); ubigen_destroy(&u); fclose(fp_leb); return rc; } static int write_remaining_raw_ebs(pdd_data_t pdd, list_t raw_blocks, size_t *ebs_written, FILE* fp_out) { int rc = 0; uint32_t j, delta; list_t ptr; peb_t empty_eb, peb; /* create an empty 0xff EB (for padding) */ rc = peb_new(0, pdd->eb_size, &empty_eb); foreach(peb, ptr, raw_blocks) { if (peb->num < *ebs_written) { continue; /* omit blocks which are already passed */ } if (peb->num < *ebs_written) { err_msg("eb_num: %d\n", peb->num); err_msg("Bug: This should never happen. %d %s", __LINE__, __FILE__); goto err; } delta = peb->num - *ebs_written; if (((delta + *ebs_written) * pdd->eb_size) > pdd->flash_size) { err_msg("RAW block outside of flash_size."); goto err; } for (j = 0; j < delta; j++) { rc = peb_write(fp_out, empty_eb); if (rc != 0) goto err; (*ebs_written)++; } rc = peb_write(fp_out, peb); if (rc != 0) goto err; (*ebs_written)++; } err: peb_free(&empty_eb); return rc; } static int init_vol_tab(struct ubi_vtbl_record **vol_tab, size_t *vol_tab_size) { uint32_t crc; size_t i; struct ubi_vtbl_record* res = NULL; *vol_tab_size = UBI_MAX_VOLUMES * UBI_VTBL_RECORD_SIZE; res = (struct ubi_vtbl_record*) calloc(1, *vol_tab_size); if (vol_tab == NULL) { return -ENOMEM; } for (i = 0; i < UBI_MAX_VOLUMES; i++) { crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &(res[i]), UBI_VTBL_RECORD_SIZE_CRC); res[i].crc = cpu_to_be32(crc); } *vol_tab = res; return 0; } static int create_raw(io_t io) { int rc = 0; size_t ebs_written = 0; /* eraseblocks written already... */ size_t vol_tab_size; list_t ptr; list_t pfi_raws = mk_empty(); /* list of raw sections from a pfi */ list_t pfi_ubis = mk_empty(); /* list of ubi sections from a pfi */ list_t raw_pebs = mk_empty(); /* list of raw eraseblocks */ struct ubi_vtbl_record *vol_tab = NULL; pdd_data_t pdd = NULL; rc = init_vol_tab (&vol_tab, &vol_tab_size); if (rc != 0) { err_msg("Cannot initialize volume table."); goto err; } rc = read_pdd_data(io->fp_pdd, &pdd, err_buf, ERR_BUF_SIZE); if (rc != 0) { err_msg("Cannot read necessary pdd_data: %s rc: %d", err_buf, rc); goto err; } rc = read_pfi_headers(&pfi_raws, &pfi_ubis, io->fp_pfi, err_buf, ERR_BUF_SIZE); if (rc != 0) { err_msg("Cannot read pfi header: %s rc: %d", err_buf, rc); goto err; } pfi_raw_t pfi_raw; foreach(pfi_raw, ptr, pfi_raws) { rc = memorize_raw_eb(pfi_raw, pdd, &raw_pebs, io); if (rc != 0) { err_msg("Cannot create raw_block in mem. rc: %d\n", rc); goto err; } } pfi_ubi_t pfi_ubi; foreach(pfi_ubi, ptr, pfi_ubis) { rc = convert_ubi_volume(pfi_ubi, pdd, raw_pebs, vol_tab, &ebs_written, io); if (rc != 0) { err_msg("Cannot convert UBI volume. rc: %d\n", rc); goto err; } } rc = write_ubi_volume_table(pdd, raw_pebs, vol_tab, vol_tab_size, &ebs_written, io); if (rc != 0) { err_msg("Cannot write UBI volume table. rc: %d\n", rc); goto err; } rc = write_remaining_raw_ebs(pdd, raw_pebs, &ebs_written, io->fp_out); if (rc != 0) goto err; if (io->fp_out != stdout) info_msg("Physical eraseblocks written: %8d\n", ebs_written); err: free(vol_tab); pfi_raws = remove_all((free_func_t)&free_pfi_raw, pfi_raws); pfi_ubis = remove_all((free_func_t)&free_pfi_ubi, pfi_ubis); raw_pebs = remove_all((free_func_t)&peb_free, raw_pebs); free_pdd_data(&pdd); return rc; } /* ------------------------------------------------------------------------- */ static void open_io_handle(myargs *args, io_t io) { /* set PDD input */ io->fp_pdd = fopen(args->f_in_pdd, "r"); if (io->fp_pdd == NULL) { err_sys("Cannot open: %s", args->f_in_pdd); } /* set PFI input */ io->fp_pfi = fopen(args->f_in_pfi, "r"); if (io->fp_pfi == NULL) { err_sys("Cannot open PFI input file: %s", args->f_in_pfi); } /* set output prefix */ if (strcmp(args->f_out,"") == 0) io->fp_out = stdout; else { io->fp_out = fopen(args->f_out, "wb"); if (io->fp_out == NULL) { err_sys("Cannot open output file: %s", args->f_out); } } } static void close_io_handle(io_t io) { if (fclose(io->fp_pdd) != 0) { err_sys("Cannot close PDD file."); } if (fclose(io->fp_pfi) != 0) { err_sys("Cannot close PFI file."); } if (io->fp_out != stdout) { if (fclose(io->fp_out) != 0) { err_sys("Cannot close output file."); } } io->fp_pdd = NULL; io->fp_pfi = NULL; io->fp_out = NULL; } int main(int argc, char *argv[]) { int rc = 0; ubigen_init(); init_crc32_table(crc32_table); struct io io = {NULL, NULL, NULL}; myargs args = { .action = ACT_RAW, .verbose = 0, .f_in_pfi = "", .f_in_pdd = "", .f_out = "", /* arguments */ .arg1 = NULL, .options = NULL, }; /* parse arguments */ parse_opt(argc, argv, &args); if (strcmp(args.f_in_pfi, "") == 0) { err_quit("No PFI input file specified!"); } if (strcmp(args.f_in_pdd, "") == 0) { err_quit("No PDD input file specified!"); } open_io_handle(&args, &io); info_msg("[ Creating RAW..."); rc = create_raw(&io); if (rc != 0) { err_msg("Creating RAW failed."); goto err; } err: close_io_handle(&io); if (rc != 0) { remove(args.f_out); } return rc; }