blob: 137aeb7bb8e9f59d5257ec28838d8eaa2256f7bf [file] [log] [blame]
Patrick Georgib7b56dd82009-09-14 13:29:27 +00001/*
2 * common utility functions for cbfstool
3 *
4 * Copyright (C) 2009 coresystems GmbH
5 * written by Patrick Georgi <patrick.georgi@coresystems.de>
David Hendricks90ca3b62012-11-16 14:48:22 -08006 * Copyright (C) 2012 Google, Inc.
Patrick Georgib7b56dd82009-09-14 13:29:27 +00007 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
20 */
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
Uwe Hermann942a40d2010-02-10 19:52:35 +000025#include <libgen.h>
Patrick Georgib7b56dd82009-09-14 13:29:27 +000026#include "common.h"
27#include "cbfs.h"
28#include "elf.h"
29
Stefan Reinauera1e48242011-10-21 14:24:57 -070030#define dprintf(x...)
Patrick Georgib7b56dd82009-09-14 13:29:27 +000031
Stefan Reinauer63217582012-10-29 16:52:36 -070032size_t getfilesize(const char *filename)
Patrick Georgi0da38dd2009-11-09 17:18:02 +000033{
Stefan Reinauer63217582012-10-29 16:52:36 -070034 size_t size;
Patrick Georgi0da38dd2009-11-09 17:18:02 +000035 FILE *file = fopen(filename, "rb");
Stefan Reinauer63217582012-10-29 16:52:36 -070036 if (file == NULL)
37 return -1;
38
Patrick Georgi0da38dd2009-11-09 17:18:02 +000039 fseek(file, 0, SEEK_END);
40 size = ftell(file);
41 fclose(file);
42 return size;
43}
44
Patrick Georgib7b56dd82009-09-14 13:29:27 +000045void *loadfile(const char *filename, uint32_t * romsize_p, void *content,
46 int place)
47{
48 FILE *file = fopen(filename, "rb");
Patrick Georgi45d8a832009-09-15 08:21:46 +000049 if (file == NULL)
50 return NULL;
Stefan Reinauer63217582012-10-29 16:52:36 -070051
Patrick Georgib7b56dd82009-09-14 13:29:27 +000052 fseek(file, 0, SEEK_END);
53 *romsize_p = ftell(file);
54 fseek(file, 0, SEEK_SET);
Stefan Reinauer853270a2009-09-22 15:55:01 +000055 if (!content) {
Patrick Georgib7b56dd82009-09-14 13:29:27 +000056 content = malloc(*romsize_p);
Stefan Reinauer853270a2009-09-22 15:55:01 +000057 if (!content) {
Stefan Reinauer0a977592012-11-30 11:21:05 -080058 fprintf(stderr, "E: Could not get %d bytes for file %s\n",
Patrick Georgi56f5fb72009-09-30 11:21:18 +000059 *romsize_p, filename);
Stefan Reinauer853270a2009-09-22 15:55:01 +000060 exit(1);
61 }
62 } else if (place == SEEK_END)
Patrick Georgib7b56dd82009-09-14 13:29:27 +000063 content -= *romsize_p;
Stefan Reinauer853270a2009-09-22 15:55:01 +000064
Patrick Georgib7b56dd82009-09-14 13:29:27 +000065 if (!fread(content, *romsize_p, 1, file)) {
Stefan Reinauer0a977592012-11-30 11:21:05 -080066 fprintf(stderr, "E: Failed to read %s\n", filename);
Patrick Georgib7b56dd82009-09-14 13:29:27 +000067 return NULL;
68 }
69 fclose(file);
70 return content;
71}
72
Stefan Reinauer63217582012-10-29 16:52:36 -070073static struct cbfs_header *master_header;
74static uint32_t phys_start, phys_end, align;
75uint32_t romsize;
Patrick Georgib7b56dd82009-09-14 13:29:27 +000076void *offset;
David Hendricks90ca3b62012-11-16 14:48:22 -080077uint32_t arch = CBFS_ARCHITECTURE_UNKNOWN;
78
79static struct {
80 uint32_t arch;
81 const char *name;
82} arch_names[] = {
83 { CBFS_ARCHITECTURE_ARMV7, "armv7" },
84 { CBFS_ARCHITECTURE_X86, "x86" },
85 { CBFS_ARCHITECTURE_UNKNOWN, "unknown" }
86};
87
88uint32_t string_to_arch(const char *arch_string)
89{
90 int i;
91 uint32_t ret = CBFS_ARCHITECTURE_UNKNOWN;
92
93 for (i = 0; i < ARRAY_SIZE(arch_names); i++) {
94 if (!strcasecmp(arch_string, arch_names[i].name)) {
95 ret = arch_names[i].arch;
96 break;
97 }
98 }
99
100 return ret;
101}
102
103const char *arch_to_string(uint32_t a)
104{
105 int i;
106 const char *ret = NULL;
107
108 for (i = 0; i < ARRAY_SIZE(arch_names); i++) {
109 if (a == arch_names[i].arch) {
110 ret = arch_names[i].name;
111 break;
112 }
113 }
114
115 return ret;
116
117}
118
119int find_master_header(void *romarea, size_t size)
120{
121 size_t offset;
122
123 if (master_header)
124 return 0;
125
126 for (offset = 0; offset < size - sizeof(struct cbfs_header); offset++) {
127 struct cbfs_header *tmp = romarea + offset;
128
129 if (tmp->magic == ntohl(CBFS_HEADER_MAGIC)) {
130 master_header = tmp;
131 break;
132 }
133 }
134
135 return master_header ? 0 : 1;
136}
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000137
138void recalculate_rom_geometry(void *romarea)
139{
David Hendricks90ca3b62012-11-16 14:48:22 -0800140 if (find_master_header(romarea, romsize)) {
141 fprintf(stderr, "E: Cannot find master header\n");
142 exit(1);
143 }
144
145 /* Update old headers */
Hung-Te Lin086842a2013-01-04 12:33:03 +0800146 if (master_header->version == CBFS_HEADER_VERSION1 &&
David Hendricks90ca3b62012-11-16 14:48:22 -0800147 ntohl(master_header->architecture) == CBFS_ARCHITECTURE_UNKNOWN) {
Stefan Reinauer0a977592012-11-30 11:21:05 -0800148 dprintf("Updating CBFS master header to version 2\n");
David Hendricks90ca3b62012-11-16 14:48:22 -0800149 master_header->architecture = htonl(CBFS_ARCHITECTURE_X86);
150 }
151
152 arch = ntohl(master_header->architecture);
153
154 switch (arch) {
155 case CBFS_ARCHITECTURE_ARMV7:
156 offset = romarea;
157 phys_start = (0 + ntohl(master_header->offset)) & 0xffffffff;
158 phys_end = romsize & 0xffffffff;
159 break;
160 case CBFS_ARCHITECTURE_X86:
161 offset = romarea + romsize - 0x100000000ULL;
162 phys_start = (0 - romsize + ntohl(master_header->offset)) &
163 0xffffffff;
164 phys_end = (0 - ntohl(master_header->bootblocksize) -
165 sizeof(struct cbfs_header)) & 0xffffffff;
166 break;
167 default:
168 fprintf(stderr, "E: Unknown architecture\n");
169 exit(1);
170 }
171
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000172 align = ntohl(master_header->align);
173}
174
175void *loadrom(const char *filename)
176{
177 void *romarea = loadfile(filename, &romsize, 0, SEEK_SET);
Patrick Georgi45d8a832009-09-15 08:21:46 +0000178 if (romarea == NULL)
179 return NULL;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000180 recalculate_rom_geometry(romarea);
181 return romarea;
182}
183
Stefan Reinauer9bb043852010-06-24 13:37:59 +0000184int writerom(const char *filename, void *start, uint32_t size)
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000185{
186 FILE *file = fopen(filename, "wb");
Stefan Reinauer9bb043852010-06-24 13:37:59 +0000187 if (!file) {
188 fprintf(stderr, "Could not open '%s' for writing: ", filename);
189 perror("");
190 return 1;
191 }
192
193 if (fwrite(start, size, 1, file) != 1) {
194 fprintf(stderr, "Could not write to '%s': ", filename);
195 perror("");
196 return 1;
197 }
198
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000199 fclose(file);
Stefan Reinauer9bb043852010-06-24 13:37:59 +0000200 return 0;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000201}
202
David Hendricks90ca3b62012-11-16 14:48:22 -0800203int cbfs_file_header(unsigned long physaddr)
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000204{
205 /* maybe improve this test */
206 return (strncmp(phys_to_virt(physaddr), "LARCHIVE", 8) == 0);
207}
208
209struct cbfs_file *cbfs_create_empty_file(uint32_t physaddr, uint32_t size)
210{
211 struct cbfs_file *nextfile = (struct cbfs_file *)phys_to_virt(physaddr);
Stefan Reinauera1e48242011-10-21 14:24:57 -0700212 strncpy((char *)(nextfile->magic), "LARCHIVE", 8);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000213 nextfile->len = htonl(size);
214 nextfile->type = htonl(0xffffffff);
215 nextfile->checksum = 0; // FIXME?
216 nextfile->offset = htonl(sizeof(struct cbfs_file) + 16);
217 memset(((void *)nextfile) + sizeof(struct cbfs_file), 0, 16);
218 return nextfile;
219}
220
221int iself(unsigned char *input)
222{
223 Elf32_Ehdr *ehdr = (Elf32_Ehdr *) input;
224 return !memcmp(ehdr->e_ident, ELFMAG, 4);
225}
226
Mathias Krause941158f2012-04-12 21:36:23 +0200227static struct filetypes_t {
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000228 uint32_t type;
229 const char *name;
230} filetypes[] = {
231 {CBFS_COMPONENT_STAGE, "stage"},
232 {CBFS_COMPONENT_PAYLOAD, "payload"},
233 {CBFS_COMPONENT_OPTIONROM, "optionrom"},
Stefan Reinauer800379f2010-03-01 08:34:19 +0000234 {CBFS_COMPONENT_BOOTSPLASH, "bootsplash"},
235 {CBFS_COMPONENT_RAW, "raw"},
236 {CBFS_COMPONENT_VSA, "vsa"},
237 {CBFS_COMPONENT_MBI, "mbi"},
238 {CBFS_COMPONENT_MICROCODE, "microcode"},
Patrick Georgia865b172011-01-14 07:40:24 +0000239 {CBFS_COMPONENT_CMOS_DEFAULT, "cmos default"},
Mathias Krause941158f2012-04-12 21:36:23 +0200240 {CBFS_COMPONENT_CMOS_LAYOUT, "cmos layout"},
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000241 {CBFS_COMPONENT_DELETED, "deleted"},
242 {CBFS_COMPONENT_NULL, "null"}
243};
244
Stefan Reinauer07040582010-04-24 21:24:06 +0000245void print_supported_filetypes(void)
246{
247 int i, number = ARRAY_SIZE(filetypes);
248
249 for (i=0; i<number; i++) {
250 printf(" %s%c", filetypes[i].name, (i==(number-1))?'\n':',');
251 if ((i%8) == 7)
252 printf("\n");
253 }
254}
255
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000256const char *strfiletype(uint32_t number)
257{
Mathias Krause41c229c2012-07-17 21:17:15 +0200258 size_t i;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000259 for (i = 0; i < (sizeof(filetypes) / sizeof(struct filetypes_t)); i++)
260 if (filetypes[i].type == number)
261 return filetypes[i].name;
262 return "unknown";
263}
264
265uint64_t intfiletype(const char *name)
266{
Mathias Krause41c229c2012-07-17 21:17:15 +0200267 size_t i;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000268 for (i = 0; i < (sizeof(filetypes) / sizeof(struct filetypes_t)); i++)
269 if (strcmp(filetypes[i].name, name) == 0)
270 return filetypes[i].type;
271 return -1;
272}
273
274void print_cbfs_directory(const char *filename)
275{
Hung-Te Lin5a9f45c2013-01-28 23:42:25 +0800276 char *name = strdup(filename);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000277 printf
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800278 ("%s: %d kB, bootblocksize %d, romsize %d, offset 0x%x\n"
279 "alignment: %d bytes, architecture: %s\n\n",
Hung-Te Lin5a9f45c2013-01-28 23:42:25 +0800280 basename(name), romsize / 1024, ntohl(master_header->bootblocksize),
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800281 romsize, ntohl(master_header->offset), align, arch_to_string(arch));
Hung-Te Lin5a9f45c2013-01-28 23:42:25 +0800282 free(name);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000283 printf("%-30s %-10s %-12s Size\n", "Name", "Offset", "Type");
284 uint32_t current = phys_start;
285 while (current < phys_end) {
286 if (!cbfs_file_header(current)) {
287 current += align;
288 continue;
289 }
290 struct cbfs_file *thisfile =
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800291 (struct cbfs_file *)phys_to_virt(current);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000292 uint32_t length = ntohl(thisfile->len);
Maciej Pijankaf44eb782010-01-07 21:37:18 +0000293 char *fname = (char *)(phys_to_virt(current) + sizeof(struct cbfs_file));
Stefan Reinauer14e22772010-04-27 06:56:47 +0000294 if (strlen(fname) == 0)
Maciej Pijankaf44eb782010-01-07 21:37:18 +0000295 fname = "(empty)";
296
Stefan Reinauer14e22772010-04-27 06:56:47 +0000297 printf("%-30s 0x%-8x %-12s %d\n", fname,
David Hendricks90ca3b62012-11-16 14:48:22 -0800298 current - phys_start + ntohl(master_header->offset),
299 strfiletype(ntohl(thisfile->type)), length);
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800300
301 /* note the components of the subheader are in host order ... */
302 switch (ntohl(thisfile->type)) {
303 case CBFS_COMPONENT_STAGE:
304 {
305 struct cbfs_stage *stage = CBFS_SUBHEADER(thisfile);
306 dprintf(" %s compression, entry: 0x%llx, load: 0x%llx, length: %d/%d\n",
307 stage->compression == CBFS_COMPRESS_LZMA ? "LZMA" : "no",
308 (unsigned long long)stage->entry,
309 (unsigned long long)stage->load,
310 stage->len,
311 stage->memlen);
312 break;
313 }
314 case CBFS_COMPONENT_PAYLOAD:
315 {
316 struct cbfs_payload_segment *payload = CBFS_SUBHEADER(thisfile);
317 while(payload) {
318 switch(payload->type) {
319 case PAYLOAD_SEGMENT_CODE:
320 case PAYLOAD_SEGMENT_DATA:
321 dprintf(" %s (%s compression, offset: 0x%x, load: 0x%llx, length: %d/%d)\n",
322 payload->type == PAYLOAD_SEGMENT_CODE ? "code " : "data" ,
323 payload->compression == CBFS_COMPRESS_LZMA ? "LZMA" : "no",
324 ntohl(payload->offset),
325 (unsigned long long)ntohll(payload->load_addr),
326 ntohl(payload->len), ntohl(payload->mem_len));
327 break;
328 case PAYLOAD_SEGMENT_ENTRY:
329 dprintf(" entry (0x%llx)\n", (unsigned long long)ntohll(payload->load_addr));
330 break;
331 case PAYLOAD_SEGMENT_BSS:
332 dprintf(" BSS (address 0x%016llx, length 0x%x)\n", (unsigned long long)ntohll(payload->load_addr), ntohl(payload->len));
333 break;
334 case PAYLOAD_SEGMENT_PARAMS:
335 dprintf(" parameters\n");
336 break;
337 default:
338 dprintf(" %x (%s compression, offset: 0x%x, load: 0x%llx, length: %d/%d\n",
339 payload->type,
340 payload->compression == CBFS_COMPRESS_LZMA ? "LZMA" : "no",
341 ntohl(payload->offset),
342 (unsigned long long)ntohll(payload->load_addr),
343 ntohl(payload->len),
344 ntohl(payload->mem_len));
345 break;
346 }
347
348 if(payload->type == PAYLOAD_SEGMENT_ENTRY)
349 payload=NULL;
350 else
351 payload++;
352 }
353 break;
354 }
355 default:
356 break;
357 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000358 current =
359 ALIGN(current + ntohl(thisfile->len) +
360 ntohl(thisfile->offset), align);
361 }
362}
363
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000364int extract_file_from_cbfs(const char *filename, const char *payloadname, const char *outpath)
365{
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000366 FILE *outfile = NULL;
367 uint32_t current = phys_start;
368 while (current < phys_end) {
369 if (!cbfs_file_header(current)) {
370 current += align;
371 continue;
372 }
373
374 // Locate the file start struct
375 struct cbfs_file *thisfile =
376 (struct cbfs_file *)phys_to_virt(current);
377 // And its length
378 uint32_t length = ntohl(thisfile->len);
379 // Locate the file name
380 char *fname = (char *)(phys_to_virt(current) + sizeof(struct cbfs_file));
Stefan Reinauera1e48242011-10-21 14:24:57 -0700381
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000382 // It's not the file we are looking for..
383 if (strcmp(fname, payloadname) != 0)
384 {
385 current =
386 ALIGN(current + ntohl(thisfile->len) +
387 ntohl(thisfile->offset), align);
388 continue;
389 }
390
391 // Else, it's our file.
Peter Stuge441426b2011-01-17 05:08:32 +0000392 printf("Found file %.30s at 0x%x, type %.12s, size %d\n", fname,
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000393 current - phys_start, strfiletype(ntohl(thisfile->type)),
394 length);
395
396 // If we are not dumping to stdout, open the out file.
397 outfile = fopen(outpath, "wb");
398 if (!outfile)
399 {
Stefan Reinauer0a977592012-11-30 11:21:05 -0800400 fprintf(stderr, "E: Could not open the file %s for writing.\n", outpath);
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000401 return 1;
402 }
403
404 if (ntohl(thisfile->type) != CBFS_COMPONENT_RAW)
405 {
Stefan Reinauer0a977592012-11-30 11:21:05 -0800406 fprintf(stderr, "W: Only 'raw' files are safe to extract.\n");
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000407 }
408
409 fwrite(((char *)thisfile)
410 + ntohl(thisfile->offset), length, 1, outfile);
411
412 fclose(outfile);
Peter Stuge441426b2011-01-17 05:08:32 +0000413 printf("Successfully dumped the file.\n");
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000414
415 // We'll only dump one file.
416 return 0;
417 }
Stefan Reinauer0a977592012-11-30 11:21:05 -0800418 fprintf(stderr, "E: File %s not found.\n", payloadname);
Stefan Reinauera1e48242011-10-21 14:24:57 -0700419 return 1;
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000420}
421
422
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000423int add_file_to_cbfs(void *content, uint32_t contentsize, uint32_t location)
424{
425 uint32_t current = phys_start;
426 while (current < phys_end) {
427 if (!cbfs_file_header(current)) {
428 current += align;
429 continue;
430 }
431 struct cbfs_file *thisfile =
432 (struct cbfs_file *)phys_to_virt(current);
433 uint32_t length = ntohl(thisfile->len);
434
435 dprintf("at %x, %x bytes\n", current, length);
436 /* Is this a free chunk? */
437 if ((thisfile->type == CBFS_COMPONENT_DELETED)
438 || (thisfile->type == CBFS_COMPONENT_NULL)) {
439 dprintf("null||deleted at %x, %x bytes\n", current,
440 length);
441 /* if this is the right size, and if specified, the right location, use it */
442 if ((contentsize <= length)
443 && ((location == 0) || (current == location))) {
444 if (contentsize < length) {
445 dprintf
446 ("this chunk is %x bytes, we need %x. create a new chunk at %x with %x bytes\n",
447 length, contentsize,
448 ALIGN(current + contentsize,
449 align),
450 length - contentsize);
451 uint32_t start =
452 ALIGN(current + contentsize, align);
453 uint32_t size =
454 current + ntohl(thisfile->offset)
455 + length - start - 16 -
456 sizeof(struct cbfs_file);
457 cbfs_create_empty_file(start, size);
458 }
459 dprintf("copying data\n");
460 memcpy(phys_to_virt(current), content,
461 contentsize);
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000462 return 0;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000463 }
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000464 if (location != 0) {
465 /* CBFS has the constraint that the chain always moves up in memory. so once
466 we're past the place we seek, we don't need to look any further */
467 if (current > location) {
Stefan Reinauer0a977592012-11-30 11:21:05 -0800468 fprintf
469 (stderr, "E: The requested space is not available\n");
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000470 return 1;
471 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000472
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000473 /* Is the requested location inside the current chunk? */
474 if ((current < location)
475 && ((location + contentsize) <=
476 (current + length))) {
477 /* Split it up. In the next iteration the code will be at the right place. */
478 dprintf("split up. new length: %x\n",
479 location - current -
480 ntohl(thisfile->offset));
481 thisfile->len =
482 htonl(location - current -
483 ntohl(thisfile->offset));
Stefan Reinauera1e48242011-10-21 14:24:57 -0700484 cbfs_create_empty_file(location,
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000485 length -
486 (location -
487 current));
488 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000489 }
490 }
491 current =
492 ALIGN(current + ntohl(thisfile->len) +
493 ntohl(thisfile->offset), align);
494 }
Stefan Reinauer0a977592012-11-30 11:21:05 -0800495 fprintf(stderr, "E: Could not add the file to CBFS, it's probably too big.\n");
496 fprintf(stderr, "E: File size: %d bytes (%d KB).\n", contentsize, contentsize/1024);
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000497 return 1;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000498}
499
Gabe Blacke1bb49e2012-01-27 00:33:47 -0800500
501static struct cbfs_file *merge_adjacent_files(struct cbfs_file *first,
502 struct cbfs_file *second)
503{
504 uint32_t new_length =
505 ntohl(first->len) + ntohl(second->len) + ntohl(second->offset);
506 first->len = htonl(new_length);
507 first->checksum = 0; // FIXME?
508 return first;
509}
510
511static struct cbfs_file *next_file(struct cbfs_file *prev)
512{
513 uint32_t pos = (prev == NULL) ? phys_start :
514 ALIGN(virt_to_phys(prev) + ntohl(prev->len) + ntohl(prev->offset),
515 align);
516
517 for (; pos < phys_end; pos += align) {
518 if (cbfs_file_header(pos))
519 return (struct cbfs_file *)phys_to_virt(pos);
520 }
521 return NULL;
522}
523
524
525int remove_file_from_cbfs(const char *filename)
526{
527 struct cbfs_file *prev = NULL;
528 struct cbfs_file *cur = next_file(prev);
529 struct cbfs_file *next = next_file(cur);
530 for (; cur; prev = cur, cur = next, next = next_file(next)) {
531
532 /* Check if this is the file to remove. */
533 char *name = (char *)cur + sizeof(*cur);
534 if (strcmp(name, filename))
535 continue;
536
537 /* Mark the file as free space and erase its name. */
538 cur->type = CBFS_COMPONENT_NULL;
539 name[0] = '\0';
540
541 /* Merge it with the previous file if possible. */
542 if (prev && prev->type == CBFS_COMPONENT_NULL)
543 cur = merge_adjacent_files(prev, cur);
544
545 /* Merge it with the next file if possible. */
546 if (next && next->type == CBFS_COMPONENT_NULL)
547 merge_adjacent_files(cur, next);
548
549 return 0;
550 }
Stefan Reinauer0a977592012-11-30 11:21:05 -0800551 fprintf(stderr, "E: CBFS file %s not found.\n", filename);
Gabe Blacke1bb49e2012-01-27 00:33:47 -0800552 return 1;
553}
554
555
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000556/* returns new data block with cbfs_file header, suitable to dump into the ROM. location returns
557 the new location that points to the cbfs_file header */
558void *create_cbfs_file(const char *filename, void *data, uint32_t * datasize,
559 uint32_t type, uint32_t * location)
560{
561 uint32_t filename_len = ALIGN(strlen(filename) + 1, 16);
562 uint32_t headersize = sizeof(struct cbfs_file) + filename_len;
563 if ((location != 0) && (*location != 0)) {
564 uint32_t offset = *location % align;
565 /* If offset >= (headersize % align), we can stuff the header into the offset.
566 Otherwise the header has to be aligned itself, and put before the offset data */
567 if (offset >= (headersize % align)) {
568 offset -= (headersize % align);
569 } else {
570 offset += align - (headersize % align);
571 }
572 headersize += offset;
573 *location -= headersize;
574 }
575 void *newdata = malloc(*datasize + headersize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000576 if (!newdata) {
Stefan Reinauer0a977592012-11-30 11:21:05 -0800577 fprintf(stderr, "E: Could not get %d bytes for CBFS file.\n", *datasize +
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000578 headersize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000579 exit(1);
580 }
Peter Stugef4aca1d2009-12-06 12:14:39 +0000581 memset(newdata, 0xff, *datasize + headersize);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000582 struct cbfs_file *nextfile = (struct cbfs_file *)newdata;
Stefan Reinauera1e48242011-10-21 14:24:57 -0700583 strncpy((char *)(nextfile->magic), "LARCHIVE", 8);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000584 nextfile->len = htonl(*datasize);
585 nextfile->type = htonl(type);
586 nextfile->checksum = 0; // FIXME?
587 nextfile->offset = htonl(headersize);
588 strcpy(newdata + sizeof(struct cbfs_file), filename);
589 memcpy(newdata + headersize, data, *datasize);
590 *datasize += headersize;
591 return newdata;
592}
593
594int create_cbfs_image(const char *romfile, uint32_t _romsize,
David Hendricks90ca3b62012-11-16 14:48:22 -0800595 const char *bootblock, uint32_t align, uint32_t offs)
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000596{
Stefan Reinauer63217582012-10-29 16:52:36 -0700597 uint32_t bootblocksize = 0;
598 struct cbfs_header *master_header;
599 unsigned char *romarea, *bootblk;
600
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000601 romsize = _romsize;
Stefan Reinauer63217582012-10-29 16:52:36 -0700602 romarea = malloc(romsize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000603 if (!romarea) {
Stefan Reinauer63217582012-10-29 16:52:36 -0700604 fprintf(stderr, "E: Could not get %d bytes of memory"
605 " for CBFS image.\n", romsize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000606 exit(1);
607 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000608 memset(romarea, 0xff, romsize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000609
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000610 if (align == 0)
611 align = 64;
612
Stefan Reinauer63217582012-10-29 16:52:36 -0700613 bootblk = loadfile(bootblock, &bootblocksize,
614 romarea + romsize, SEEK_END);
615 if (!bootblk) {
616 fprintf(stderr, "E: Could not load bootblock %s.\n",
617 bootblock);
618 free(romarea);
619 return 1;
620 }
621
David Hendricks90ca3b62012-11-16 14:48:22 -0800622 // TODO(hungte) Replace magic numbers by named constants.
623 switch (arch) {
624 case CBFS_ARCHITECTURE_ARMV7:
625 /* Set up physical/virtual mapping */
626 offset = romarea;
Stefan Reinauer853270a2009-09-22 15:55:01 +0000627
David Hendricks90ca3b62012-11-16 14:48:22 -0800628 /*
David Hendricks0b23d472013-01-14 20:58:50 -0800629 * The initial jump instruction and bootblock will be placed
630 * before and after the master header, respectively. The
631 * bootblock image must contain a blank, aligned region large
632 * enough for the master header to fit.
David Hendricks90ca3b62012-11-16 14:48:22 -0800633 *
David Hendricks0b23d472013-01-14 20:58:50 -0800634 * An anchor string must be left such that when cbfstool is run
635 * we can find it and insert the master header at the next
636 * aligned boundary.
David Hendricks90ca3b62012-11-16 14:48:22 -0800637 */
David Hendricks0b23d472013-01-14 20:58:50 -0800638 loadfile(bootblock, &bootblocksize, romarea + offs, SEEK_SET);
639
640 unsigned char *p = romarea + offs;
641 while (1) {
642 /* FIXME: assumes little endian... */
643 if (*(uint32_t *)p == 0xdeadbeef)
644 break;
645 if (p >= (romarea + _romsize)) {
646 fprintf(stderr, "E: Could not determine CBFS "
647 "header location.\n", bootblock);
648 return 1;
649 }
650 p += (sizeof(unsigned int));
651 }
652 unsigned int u = ALIGN((unsigned int)(p - romarea), align);
653 master_header = (struct cbfs_header *)(romarea + u);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000654
David Hendricks90ca3b62012-11-16 14:48:22 -0800655 master_header->magic = ntohl(CBFS_HEADER_MAGIC);
Hung-Te Lin086842a2013-01-04 12:33:03 +0800656 master_header->version = ntohl(CBFS_HEADER_VERSION);
David Hendricks90ca3b62012-11-16 14:48:22 -0800657 master_header->romsize = htonl(romsize);
658 master_header->bootblocksize = htonl(bootblocksize);
659 master_header->align = htonl(align);
660 master_header->offset = htonl(
661 ALIGN((0x40 + bootblocksize), align));
662 master_header->architecture = htonl(CBFS_ARCHITECTURE_ARMV7);
663
David Hendricks454856b2013-01-02 17:29:00 -0800664 ((uint32_t *) phys_to_virt(0x4 + offs))[0] =
David Hendricks90ca3b62012-11-16 14:48:22 -0800665 virt_to_phys(master_header);
666
667 recalculate_rom_geometry(romarea);
668
669 cbfs_create_empty_file(
David Hendricks454856b2013-01-02 17:29:00 -0800670 offs + ALIGN((0x40 + bootblocksize), align),
671 romsize - offs - sizeof(struct cbfs_file) -
672 ALIGN((bootblocksize + 0x40), align));
David Hendricks90ca3b62012-11-16 14:48:22 -0800673 break;
674
675 case CBFS_ARCHITECTURE_X86:
676 // Set up physical/virtual mapping
677 offset = romarea + romsize - 0x100000000ULL;
678
679 loadfile(bootblock, &bootblocksize, romarea + romsize,
680 SEEK_END);
681 master_header = (struct cbfs_header *)(romarea + romsize -
682 bootblocksize - sizeof(struct cbfs_header));
683
684 master_header->magic = ntohl(CBFS_HEADER_MAGIC);
Hung-Te Lin086842a2013-01-04 12:33:03 +0800685 master_header->version = ntohl(CBFS_HEADER_VERSION);
David Hendricks90ca3b62012-11-16 14:48:22 -0800686 master_header->romsize = htonl(romsize);
687 master_header->bootblocksize = htonl(bootblocksize);
688 master_header->align = htonl(align);
689 master_header->offset = htonl(offs);
690 master_header->architecture = htonl(CBFS_ARCHITECTURE_X86);
691
692 ((uint32_t *) phys_to_virt(CBFS_HEADPTR_ADDR_X86))[0] =
693 virt_to_phys(master_header);
694
695 recalculate_rom_geometry(romarea);
696
697 cbfs_create_empty_file((0 - romsize + offs) & 0xffffffff,
698 romsize - offs - bootblocksize -
699 sizeof(struct cbfs_header) -
700 sizeof(struct cbfs_file) - 16);
701 break;
702
703 default:
704 // Should not happen.
705 fprintf(stderr, "E: You found a bug in cbfstool.\n");
706 exit(1);
707 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000708
709 writerom(romfile, romarea, romsize);
Stefan Reinauer63217582012-10-29 16:52:36 -0700710 free(romarea);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000711 return 0;
712}
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000713
714static int in_segment(int addr, int size, int gran)
715{
716 return ((addr & ~(gran - 1)) == ((addr + size) & ~(gran - 1)));
717}
718
719uint32_t cbfs_find_location(const char *romfile, uint32_t filesize,
720 const char *filename, uint32_t alignment)
721{
Stefan Reinauer63217582012-10-29 16:52:36 -0700722 void *rom;
723 size_t filename_size, headersize, totalsize;
724 int ret = 0;
725 uint32_t current;
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000726
Stefan Reinauer63217582012-10-29 16:52:36 -0700727 rom = loadrom(romfile);
728 if (rom == NULL) {
729 fprintf(stderr, "E: Could not load ROM image '%s'.\n",
730 romfile);
731 return 0;
732 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000733
Stefan Reinauer63217582012-10-29 16:52:36 -0700734 filename_size = strlen(filename);
735 headersize = sizeof(struct cbfs_file) + ALIGN(filename_size + 1, 16) +
736 sizeof(struct cbfs_stage);
737 totalsize = headersize + filesize;
738
739 current = phys_start;
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000740 while (current < phys_end) {
Stefan Reinauer63217582012-10-29 16:52:36 -0700741 uint32_t top;
742 struct cbfs_file *thisfile;
743
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000744 if (!cbfs_file_header(current)) {
745 current += align;
746 continue;
747 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000748
Stefan Reinauer63217582012-10-29 16:52:36 -0700749 thisfile = (struct cbfs_file *)phys_to_virt(current);
750
751 top = current + ntohl(thisfile->len) + ntohl(thisfile->offset);
752
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000753 if (((ntohl(thisfile->type) == 0x0)
754 || (ntohl(thisfile->type) == 0xffffffff))
755 && (ntohl(thisfile->len) + ntohl(thisfile->offset) >=
756 totalsize)) {
757 if (in_segment
Stefan Reinauer63217582012-10-29 16:52:36 -0700758 (current + headersize, filesize, alignment)) {
759 ret = current + headersize;
760 break;
761 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000762 if ((ALIGN(current, alignment) + filesize < top)
763 && (ALIGN(current, alignment) - headersize >
764 current)
765 && in_segment(ALIGN(current, alignment), filesize,
Stefan Reinauer63217582012-10-29 16:52:36 -0700766 alignment)) {
767 ret = ALIGN(current, alignment);
768 break;
769 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000770 if ((ALIGN(current, alignment) + alignment + filesize <
771 top)
772 && (ALIGN(current, alignment) + alignment -
773 headersize > current)
774 && in_segment(ALIGN(current, alignment) + alignment,
Stefan Reinauer63217582012-10-29 16:52:36 -0700775 filesize, alignment)) {
776 ret = ALIGN(current, alignment) + alignment;
777 break;
778 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000779 }
780 current =
781 ALIGN(current + ntohl(thisfile->len) +
782 ntohl(thisfile->offset), align);
783 }
Stefan Reinauer63217582012-10-29 16:52:36 -0700784
785 free(rom);
786 return ret;
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000787}