476 lines
12 KiB
C
476 lines
12 KiB
C
/******************************************************************************
|
|
* 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;
|
|
}
|