summaryrefslogtreecommitdiffstats
path: root/qemu/roms/SLOF/romfs/tools/build_ffs.c
blob: 218de75f01a44b9680bdbb07399705e80c61298d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
/******************************************************************************
 * 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;
}