| /****************************************************************************** |
| * Copyright (c) 2004, 2008 IBM Corporation |
| * All rights reserved. |
| * This program and the accompanying materials |
| * are made available under the terms of the BSD License |
| * which accompanies this distribution, and is available at |
| * http://www.opensource.org/licenses/bsd-license.php |
| * |
| * Contributors: |
| * IBM Corporation - initial implementation |
| *****************************************************************************/ |
| |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include <cfgparse.h> |
| #include <createcrc.h> |
| |
| #define FFS_TARGET_HEADER_SIZE (4 * 8) |
| |
| extern int verbose; |
| |
| #define pad8_num(x) (((x) + 7) & ~7) |
| |
| static int |
| file_exist(const char *name, int errdisp) |
| { |
| struct stat fileinfo; |
| |
| memset((void *) &fileinfo, 0, sizeof(struct stat)); |
| if (stat(name, &fileinfo) != 0) { |
| if (0 != errdisp) { |
| perror(name); |
| } |
| return 0; |
| } |
| if (S_ISREG(fileinfo.st_mode)) { |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int |
| file_getsize(const char *name) |
| { |
| int rc; |
| struct stat fi; |
| |
| rc = stat(name, &fi); |
| if (rc != 0) |
| return -1; |
| return fi.st_size; |
| } |
| |
| static int |
| ffshdr_compare(const void *_a, const void *_b) |
| { |
| const struct ffs_header_t *a = *(struct ffs_header_t * const *) _a; |
| const struct ffs_header_t *b = *(struct ffs_header_t * const *) _b; |
| |
| if (a->romaddr == b->romaddr) |
| return 0; |
| if (a->romaddr > b->romaddr) |
| return 1; |
| return -1; |
| } |
| |
| static void |
| hdr_print(struct ffs_header_t *hdr) |
| { |
| printf("hdr: %p\n", hdr); |
| printf("\taddr: %08llx token: %s\n" |
| "\tflags: %08llx romaddr: %08llx image_len: %08x\n" |
| "\tsave_len: %08llx ffsize: %08x hdrsize: %08x\n" |
| "\ttokensize: %08x\n", |
| hdr->addr, hdr->token, hdr->flags, hdr->romaddr, |
| hdr->imagefile_length, hdr->save_data_len, |
| hdr->ffsize, hdr->hdrsize, hdr->tokensize); |
| } |
| |
| int |
| reorder_ffs_chain(struct ffs_chain_t *fs) |
| { |
| int i, j; |
| int free_space; |
| unsigned long long addr; |
| struct ffs_header_t *hdr; |
| int fix, flx, res, tab_size = fs->count; |
| struct ffs_header_t *fix_tab[tab_size]; /* fixed offset */ |
| struct ffs_header_t *flx_tab[tab_size]; /* flexible offset */ |
| struct ffs_header_t *res_tab[tab_size]; /* result */ |
| |
| /* determine size data to be able to do the reordering */ |
| for (hdr = fs->first; hdr; hdr = hdr->next) { |
| if (hdr->linked_to) |
| hdr->imagefile_length = 0; |
| else |
| hdr->imagefile_length = file_getsize(hdr->imagefile); |
| if (hdr->imagefile_length == -1) |
| return -1; |
| |
| hdr->tokensize = pad8_num(strlen(hdr->token) + 1); |
| hdr->hdrsize = FFS_TARGET_HEADER_SIZE + hdr->tokensize; |
| hdr->ffsize = |
| hdr->hdrsize + pad8_num(hdr->imagefile_length) + 8; |
| } |
| |
| memset(res_tab, 0, tab_size * sizeof(struct ffs_header_t *)); |
| memset(fix_tab, 0, tab_size * sizeof(struct ffs_header_t *)); |
| memset(flx_tab, 0, tab_size * sizeof(struct ffs_header_t *)); |
| |
| /* now start with entries having fixed offs, reorder if needed */ |
| for (fix = 0, flx = 0, hdr = fs->first; hdr; hdr = hdr->next) |
| if (needs_fix_offset(hdr)) |
| fix_tab[fix++] = hdr; |
| else |
| flx_tab[flx++] = hdr; |
| qsort(fix_tab, fix, sizeof(struct ffs_header_t *), ffshdr_compare); |
| |
| /* |
| * for fixed files we need to also remove the hdrsize from the |
| * free space because it placed in front of the romaddr |
| */ |
| for (addr = 0, res = 0, i = 0, j = 0; i < fix; i++) { |
| fix_tab[i]->addr = fix_tab[i]->romaddr - fix_tab[i]->hdrsize; |
| free_space = fix_tab[i]->addr - addr; |
| |
| /* insert as many flexible files as possible */ |
| for (; free_space > 0 && j < flx; j++) { |
| if (flx_tab[j]->ffsize <= free_space) { /* fits */ |
| flx_tab[j]->addr = addr; |
| free_space -= flx_tab[j]->ffsize; |
| addr += flx_tab[j]->ffsize; |
| res_tab[res++] = flx_tab[j]; |
| } else |
| break; |
| } |
| res_tab[res++] = fix_tab[i]; |
| addr = fix_tab[i]->romaddr + fix_tab[i]->ffsize - |
| fix_tab[i]->hdrsize; |
| } |
| /* at the end fill up the table with remaining flx entries */ |
| for (; j < flx; j++) { |
| flx_tab[j]->addr = addr; |
| addr += flx_tab[j]->ffsize; |
| res_tab[res++] = flx_tab[j]; |
| } |
| |
| if (verbose) { |
| printf("--- resulting order ---\n"); |
| for (i = 0; i < tab_size; i++) |
| hdr_print(res_tab[i]); |
| } |
| |
| /* to check if the requested romfs images is greater than |
| * the specified romfs_size it is necessary to add 8 for |
| * the CRC to the totalsize */ |
| addr += 8; |
| |
| /* sanity checking if user specified maximum romfs size */ |
| if ((fs->romfs_size != 0) && addr > fs->romfs_size) { |
| fprintf(stderr, "[build_romfs] romfs_size specified as %d " |
| "bytes, but %lld bytes need to be written.\n", |
| fs->romfs_size, addr); |
| return 1; |
| } |
| |
| /* resort result list */ |
| for (i = 0; i < tab_size - 1; i++) |
| res_tab[i]->next = res_tab[i + 1]; |
| res_tab[i]->next = NULL; |
| fs->first = res_tab[0]; |
| return 0; |
| } |
| |
| /** |
| * allocate memory for a romfs file including header |
| */ |
| static unsigned char * |
| malloc_file(int hdrsz, int datasz, int *ffsz) |
| { |
| void *tmp; |
| |
| /* complete file size is: |
| * header + 8byte aligned(data) + end of file marker (-1) */ |
| *ffsz = hdrsz + pad8_num(datasz) + 8; |
| /* get the mem */ |
| tmp = malloc(*ffsz); |
| |
| if (!tmp) |
| return NULL; |
| |
| memset(tmp, 0, *ffsz); |
| |
| return (unsigned char *) tmp; |
| } |
| |
| static int |
| copy_file(struct ffs_header_t *hdr, unsigned char *ffile, int datasize, |
| int ffile_offset, int ffsize) |
| { |
| int cnt = 0; |
| int imgfd; |
| int i; |
| |
| if (!file_exist(hdr->imagefile, 1)) { |
| printf("access error to file: %s\n", hdr->imagefile); |
| free(ffile); |
| return -1; |
| } |
| |
| imgfd = open(hdr->imagefile, O_RDONLY); |
| if (0 >= imgfd) { |
| perror(hdr->imagefile); |
| free(ffile); |
| return -1; |
| } |
| |
| /* now copy file to file buffer */ |
| /* FIXME using fread might be a good idea so |
| that we do not need to deal with shortened |
| reads/writes. Also error handling looks |
| broken to me. Are we sure that all data is |
| read when exiting this loop? */ |
| while (1) { |
| i = read(imgfd, ffile + ffile_offset, ffsize - ffile_offset); |
| if (i <= 0) |
| break; |
| ffile_offset += i; |
| cnt += i; |
| } |
| |
| /* sanity check */ |
| if (cnt != datasize) { |
| printf("BUG!!! copy error on image file [%s](e%d, g%d)\n", |
| hdr->imagefile, datasize, cnt); |
| close(imgfd); |
| free(ffile); |
| return -1; |
| } |
| |
| close(imgfd); |
| |
| return cnt; |
| } |
| |
| static uint64_t |
| next_file_offset(struct ffs_header_t *hdr, int rom_pos, int ffsize) |
| { |
| uint64_t tmp; |
| |
| /* no next file; end of filesystem */ |
| if (hdr->next == NULL) |
| return 0; |
| |
| if (hdr->next->romaddr > 0) { |
| /* the next file does not follow directly after the |
| * current file because it requested to be |
| * placed at a special address; |
| * we need to calculate the offset of the |
| * next file; |
| * the next file starts at hdr->next->romaddr which |
| * is the address requested by the user */ |
| tmp = hdr->next->romaddr; |
| /* the next file starts, however, a bit earlier; |
| * we need to point at the header of the next file; |
| * therefore it is necessary to subtract the header size |
| * of the _next_ file */ |
| tmp -= FFS_TARGET_HEADER_SIZE; |
| /* also remove the length of the filename of the _next_ |
| * file */ |
| tmp -= pad8_num(strlen(hdr->next->token) + 1); |
| /* and it needs to be relative to the current file */ |
| tmp -= rom_pos; |
| return tmp; |
| } |
| |
| /* if no special treatment is required the next file just |
| * follows after the current file; |
| * therefore just return the complete filesize as offset */ |
| return ffsize; |
| } |
| |
| static int |
| next_file_address(struct ffs_header_t *hdr, unsigned int rom_pos, int hdrsize, |
| unsigned int num_files) |
| { |
| /* check if file wants a specific address */ |
| void *tmp; |
| |
| if ((hdr->flags & FLAG_LLFW) == 0) |
| /* flag to get a specific address has been set */ |
| return rom_pos; |
| |
| if (hdr->romaddr == 0) |
| /* if the requested address is 0 then |
| * something is not right; ignore the flag */ |
| return rom_pos; |
| |
| /* check if romaddress is below current position */ |
| if (hdr->romaddr < (rom_pos + hdrsize)) { |
| printf("[%s] ERROR: requested impossible " "romaddr of %llx\n", |
| hdr->token, hdr->romaddr); |
| return -1; |
| } |
| |
| /* spin offset to new position */ |
| if (pad8_num(hdr->romaddr) != hdr->romaddr) { |
| printf("BUG!!!! pad8_num(hdr->romaddr) != hdr->romaddr\n"); |
| return -1; |
| } |
| |
| tmp = malloc(hdr->romaddr - rom_pos - hdrsize); |
| |
| if (!tmp) |
| return -1; |
| |
| memset(tmp, 0, hdr->romaddr - rom_pos - hdrsize); |
| if (buildDataStream(tmp, hdr->romaddr - rom_pos - hdrsize)) { |
| free(tmp); |
| printf("write failed\n"); |
| return -1; |
| } |
| |
| free(tmp); |
| |
| if (!num_files) |
| printf("\nWARNING: The filesystem will have no entry header!\n" |
| " It is still usable but you need to find\n" |
| " the FS by yourself in the image.\n\n"); |
| |
| return hdr->romaddr - hdrsize; |
| } |
| |
| int |
| build_ffs(struct ffs_chain_t *fs, const char *outfile, int notime) |
| { |
| int ofdCRC; |
| int ffsize, datasize, i; |
| int tokensize, hdrsize, ffile_offset, hdrbegin; |
| struct ffs_header_t *hdr; |
| unsigned char *ffile; |
| unsigned int rom_pos = 0; |
| unsigned int num_files = 0; |
| uint64_t tmp; |
| |
| if (NULL == fs->first) { |
| return 1; |
| } |
| hdr = fs->first; |
| |
| /* check output file and open it for creation */ |
| if (file_exist(outfile, 0)) { |
| printf("Output file (%s) will be overwritten\n", outfile); |
| } |
| |
| while (hdr) { |
| |
| if (hdr->linked_to) { |
| printf("\nBUG!!! links not supported anymore\n"); |
| return 1; |
| } |
| |
| /* add +1 to strlen for zero termination */ |
| tokensize = pad8_num(strlen(hdr->token) + 1); |
| hdrsize = FFS_TARGET_HEADER_SIZE + tokensize; |
| datasize = file_getsize(hdr->imagefile); |
| |
| if (datasize == -1) { |
| perror(hdr->imagefile); |
| return 1; |
| } |
| |
| ffile_offset = 0; |
| ffile = malloc_file(hdrsize, datasize, &ffsize); |
| |
| if (NULL == ffile) { |
| perror("alloc mem for ffile"); |
| return 1; |
| } |
| |
| /* check if file wants a specific address */ |
| rom_pos = next_file_address(hdr, rom_pos, hdrsize, num_files); |
| hdrbegin = rom_pos; |
| |
| if (hdrbegin == -1) { |
| /* something went wrong */ |
| free(ffile); |
| return 1; |
| } |
| |
| /* write header ******************************************* */ |
| /* next addr ********************************************** */ |
| tmp = next_file_offset(hdr, rom_pos, ffsize); |
| |
| *(uint64_t *) (ffile + ffile_offset) = cpu_to_be64(tmp); |
| rom_pos += 8; |
| ffile_offset += 8; |
| |
| /* length ************************************************* */ |
| hdr->save_data_len = datasize; |
| |
| *(uint64_t *) (ffile + ffile_offset) = cpu_to_be64(datasize); |
| rom_pos += 8; |
| ffile_offset += 8; |
| |
| /* flags ************************************************** */ |
| *(uint64_t *) (ffile + ffile_offset) = cpu_to_be64(hdr->flags); |
| rom_pos += 8; |
| ffile_offset += 8; |
| |
| /* datapointer ******************************************** */ |
| |
| //save-data pointer is relative to rombase |
| hdr->save_data = hdrbegin + hdrsize; |
| hdr->save_data_valid = 1; |
| //changed pointers to be relative to file: |
| tmp = hdr->save_data - hdrbegin; |
| |
| *(uint64_t *) (ffile + ffile_offset) = cpu_to_be64(tmp); |
| rom_pos += 8; |
| ffile_offset += 8; |
| |
| /* name (token) ******************************************* */ |
| memset(ffile + ffile_offset, 0, tokensize); |
| strcpy((char *) ffile + ffile_offset, hdr->token); |
| rom_pos += tokensize; |
| ffile_offset += tokensize; |
| |
| /* image file ********************************************* */ |
| i = copy_file(hdr, ffile, datasize, ffile_offset, ffsize); |
| |
| if (i == -1) |
| return 1; |
| |
| /* pad file */ |
| rom_pos += i + pad8_num(datasize) - datasize; |
| ffile_offset += i + pad8_num(datasize) - datasize; |
| |
| /* limiter ************************************************ */ |
| *(uint64_t *) (ffile + ffile_offset) = -1; |
| rom_pos += 8; |
| ffile_offset += 8; |
| |
| if (buildDataStream(ffile, ffsize) != 0) { |
| printf |
| ("Failed while processing file '%s' (size = %d bytes)\n", |
| hdr->imagefile, datasize); |
| return 1; |
| } |
| free(ffile); |
| hdr = hdr->next; |
| num_files++; |
| } |
| |
| /* |
| * FIXME Current limination seems to be about 4MiB. |
| */ |
| ofdCRC = open(outfile, O_CREAT | O_WRONLY | O_TRUNC, 0666); |
| if (0 > ofdCRC) { |
| perror(outfile); |
| return 1; |
| } |
| i = writeDataStream(ofdCRC, notime); |
| close(ofdCRC); |
| |
| if (i) |
| return 1; |
| return 0; |
| } |