blob: 97bf168402eeecd8e583a9b8debcbe5270b15b39 [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{
276 printf
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800277 ("%s: %d kB, bootblocksize %d, romsize %d, offset 0x%x\n"
278 "alignment: %d bytes, architecture: %s\n\n",
279 basename((char *)filename), romsize / 1024, ntohl(master_header->bootblocksize),
280 romsize, ntohl(master_header->offset), align, arch_to_string(arch));
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000281 printf("%-30s %-10s %-12s Size\n", "Name", "Offset", "Type");
282 uint32_t current = phys_start;
283 while (current < phys_end) {
284 if (!cbfs_file_header(current)) {
285 current += align;
286 continue;
287 }
288 struct cbfs_file *thisfile =
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800289 (struct cbfs_file *)phys_to_virt(current);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000290 uint32_t length = ntohl(thisfile->len);
Maciej Pijankaf44eb782010-01-07 21:37:18 +0000291 char *fname = (char *)(phys_to_virt(current) + sizeof(struct cbfs_file));
Stefan Reinauer14e22772010-04-27 06:56:47 +0000292 if (strlen(fname) == 0)
Maciej Pijankaf44eb782010-01-07 21:37:18 +0000293 fname = "(empty)";
294
Stefan Reinauer14e22772010-04-27 06:56:47 +0000295 printf("%-30s 0x%-8x %-12s %d\n", fname,
David Hendricks90ca3b62012-11-16 14:48:22 -0800296 current - phys_start + ntohl(master_header->offset),
297 strfiletype(ntohl(thisfile->type)), length);
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800298
299 /* note the components of the subheader are in host order ... */
300 switch (ntohl(thisfile->type)) {
301 case CBFS_COMPONENT_STAGE:
302 {
303 struct cbfs_stage *stage = CBFS_SUBHEADER(thisfile);
304 dprintf(" %s compression, entry: 0x%llx, load: 0x%llx, length: %d/%d\n",
305 stage->compression == CBFS_COMPRESS_LZMA ? "LZMA" : "no",
306 (unsigned long long)stage->entry,
307 (unsigned long long)stage->load,
308 stage->len,
309 stage->memlen);
310 break;
311 }
312 case CBFS_COMPONENT_PAYLOAD:
313 {
314 struct cbfs_payload_segment *payload = CBFS_SUBHEADER(thisfile);
315 while(payload) {
316 switch(payload->type) {
317 case PAYLOAD_SEGMENT_CODE:
318 case PAYLOAD_SEGMENT_DATA:
319 dprintf(" %s (%s compression, offset: 0x%x, load: 0x%llx, length: %d/%d)\n",
320 payload->type == PAYLOAD_SEGMENT_CODE ? "code " : "data" ,
321 payload->compression == CBFS_COMPRESS_LZMA ? "LZMA" : "no",
322 ntohl(payload->offset),
323 (unsigned long long)ntohll(payload->load_addr),
324 ntohl(payload->len), ntohl(payload->mem_len));
325 break;
326 case PAYLOAD_SEGMENT_ENTRY:
327 dprintf(" entry (0x%llx)\n", (unsigned long long)ntohll(payload->load_addr));
328 break;
329 case PAYLOAD_SEGMENT_BSS:
330 dprintf(" BSS (address 0x%016llx, length 0x%x)\n", (unsigned long long)ntohll(payload->load_addr), ntohl(payload->len));
331 break;
332 case PAYLOAD_SEGMENT_PARAMS:
333 dprintf(" parameters\n");
334 break;
335 default:
336 dprintf(" %x (%s compression, offset: 0x%x, load: 0x%llx, length: %d/%d\n",
337 payload->type,
338 payload->compression == CBFS_COMPRESS_LZMA ? "LZMA" : "no",
339 ntohl(payload->offset),
340 (unsigned long long)ntohll(payload->load_addr),
341 ntohl(payload->len),
342 ntohl(payload->mem_len));
343 break;
344 }
345
346 if(payload->type == PAYLOAD_SEGMENT_ENTRY)
347 payload=NULL;
348 else
349 payload++;
350 }
351 break;
352 }
353 default:
354 break;
355 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000356 current =
357 ALIGN(current + ntohl(thisfile->len) +
358 ntohl(thisfile->offset), align);
359 }
360}
361
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000362int extract_file_from_cbfs(const char *filename, const char *payloadname, const char *outpath)
363{
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000364 FILE *outfile = NULL;
365 uint32_t current = phys_start;
366 while (current < phys_end) {
367 if (!cbfs_file_header(current)) {
368 current += align;
369 continue;
370 }
371
372 // Locate the file start struct
373 struct cbfs_file *thisfile =
374 (struct cbfs_file *)phys_to_virt(current);
375 // And its length
376 uint32_t length = ntohl(thisfile->len);
377 // Locate the file name
378 char *fname = (char *)(phys_to_virt(current) + sizeof(struct cbfs_file));
Stefan Reinauera1e48242011-10-21 14:24:57 -0700379
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000380 // It's not the file we are looking for..
381 if (strcmp(fname, payloadname) != 0)
382 {
383 current =
384 ALIGN(current + ntohl(thisfile->len) +
385 ntohl(thisfile->offset), align);
386 continue;
387 }
388
389 // Else, it's our file.
Peter Stuge441426b2011-01-17 05:08:32 +0000390 printf("Found file %.30s at 0x%x, type %.12s, size %d\n", fname,
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000391 current - phys_start, strfiletype(ntohl(thisfile->type)),
392 length);
393
394 // If we are not dumping to stdout, open the out file.
395 outfile = fopen(outpath, "wb");
396 if (!outfile)
397 {
Stefan Reinauer0a977592012-11-30 11:21:05 -0800398 fprintf(stderr, "E: Could not open the file %s for writing.\n", outpath);
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000399 return 1;
400 }
401
402 if (ntohl(thisfile->type) != CBFS_COMPONENT_RAW)
403 {
Stefan Reinauer0a977592012-11-30 11:21:05 -0800404 fprintf(stderr, "W: Only 'raw' files are safe to extract.\n");
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000405 }
406
407 fwrite(((char *)thisfile)
408 + ntohl(thisfile->offset), length, 1, outfile);
409
410 fclose(outfile);
Peter Stuge441426b2011-01-17 05:08:32 +0000411 printf("Successfully dumped the file.\n");
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000412
413 // We'll only dump one file.
414 return 0;
415 }
Stefan Reinauer0a977592012-11-30 11:21:05 -0800416 fprintf(stderr, "E: File %s not found.\n", payloadname);
Stefan Reinauera1e48242011-10-21 14:24:57 -0700417 return 1;
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000418}
419
420
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000421int add_file_to_cbfs(void *content, uint32_t contentsize, uint32_t location)
422{
423 uint32_t current = phys_start;
424 while (current < phys_end) {
425 if (!cbfs_file_header(current)) {
426 current += align;
427 continue;
428 }
429 struct cbfs_file *thisfile =
430 (struct cbfs_file *)phys_to_virt(current);
431 uint32_t length = ntohl(thisfile->len);
432
433 dprintf("at %x, %x bytes\n", current, length);
434 /* Is this a free chunk? */
435 if ((thisfile->type == CBFS_COMPONENT_DELETED)
436 || (thisfile->type == CBFS_COMPONENT_NULL)) {
437 dprintf("null||deleted at %x, %x bytes\n", current,
438 length);
439 /* if this is the right size, and if specified, the right location, use it */
440 if ((contentsize <= length)
441 && ((location == 0) || (current == location))) {
442 if (contentsize < length) {
443 dprintf
444 ("this chunk is %x bytes, we need %x. create a new chunk at %x with %x bytes\n",
445 length, contentsize,
446 ALIGN(current + contentsize,
447 align),
448 length - contentsize);
449 uint32_t start =
450 ALIGN(current + contentsize, align);
451 uint32_t size =
452 current + ntohl(thisfile->offset)
453 + length - start - 16 -
454 sizeof(struct cbfs_file);
455 cbfs_create_empty_file(start, size);
456 }
457 dprintf("copying data\n");
458 memcpy(phys_to_virt(current), content,
459 contentsize);
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000460 return 0;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000461 }
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000462 if (location != 0) {
463 /* CBFS has the constraint that the chain always moves up in memory. so once
464 we're past the place we seek, we don't need to look any further */
465 if (current > location) {
Stefan Reinauer0a977592012-11-30 11:21:05 -0800466 fprintf
467 (stderr, "E: The requested space is not available\n");
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000468 return 1;
469 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000470
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000471 /* Is the requested location inside the current chunk? */
472 if ((current < location)
473 && ((location + contentsize) <=
474 (current + length))) {
475 /* Split it up. In the next iteration the code will be at the right place. */
476 dprintf("split up. new length: %x\n",
477 location - current -
478 ntohl(thisfile->offset));
479 thisfile->len =
480 htonl(location - current -
481 ntohl(thisfile->offset));
Stefan Reinauera1e48242011-10-21 14:24:57 -0700482 cbfs_create_empty_file(location,
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000483 length -
484 (location -
485 current));
486 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000487 }
488 }
489 current =
490 ALIGN(current + ntohl(thisfile->len) +
491 ntohl(thisfile->offset), align);
492 }
Stefan Reinauer0a977592012-11-30 11:21:05 -0800493 fprintf(stderr, "E: Could not add the file to CBFS, it's probably too big.\n");
494 fprintf(stderr, "E: File size: %d bytes (%d KB).\n", contentsize, contentsize/1024);
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000495 return 1;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000496}
497
Gabe Blacke1bb49e2012-01-27 00:33:47 -0800498
499static struct cbfs_file *merge_adjacent_files(struct cbfs_file *first,
500 struct cbfs_file *second)
501{
502 uint32_t new_length =
503 ntohl(first->len) + ntohl(second->len) + ntohl(second->offset);
504 first->len = htonl(new_length);
505 first->checksum = 0; // FIXME?
506 return first;
507}
508
509static struct cbfs_file *next_file(struct cbfs_file *prev)
510{
511 uint32_t pos = (prev == NULL) ? phys_start :
512 ALIGN(virt_to_phys(prev) + ntohl(prev->len) + ntohl(prev->offset),
513 align);
514
515 for (; pos < phys_end; pos += align) {
516 if (cbfs_file_header(pos))
517 return (struct cbfs_file *)phys_to_virt(pos);
518 }
519 return NULL;
520}
521
522
523int remove_file_from_cbfs(const char *filename)
524{
525 struct cbfs_file *prev = NULL;
526 struct cbfs_file *cur = next_file(prev);
527 struct cbfs_file *next = next_file(cur);
528 for (; cur; prev = cur, cur = next, next = next_file(next)) {
529
530 /* Check if this is the file to remove. */
531 char *name = (char *)cur + sizeof(*cur);
532 if (strcmp(name, filename))
533 continue;
534
535 /* Mark the file as free space and erase its name. */
536 cur->type = CBFS_COMPONENT_NULL;
537 name[0] = '\0';
538
539 /* Merge it with the previous file if possible. */
540 if (prev && prev->type == CBFS_COMPONENT_NULL)
541 cur = merge_adjacent_files(prev, cur);
542
543 /* Merge it with the next file if possible. */
544 if (next && next->type == CBFS_COMPONENT_NULL)
545 merge_adjacent_files(cur, next);
546
547 return 0;
548 }
Stefan Reinauer0a977592012-11-30 11:21:05 -0800549 fprintf(stderr, "E: CBFS file %s not found.\n", filename);
Gabe Blacke1bb49e2012-01-27 00:33:47 -0800550 return 1;
551}
552
553
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000554/* returns new data block with cbfs_file header, suitable to dump into the ROM. location returns
555 the new location that points to the cbfs_file header */
556void *create_cbfs_file(const char *filename, void *data, uint32_t * datasize,
557 uint32_t type, uint32_t * location)
558{
559 uint32_t filename_len = ALIGN(strlen(filename) + 1, 16);
560 uint32_t headersize = sizeof(struct cbfs_file) + filename_len;
561 if ((location != 0) && (*location != 0)) {
562 uint32_t offset = *location % align;
563 /* If offset >= (headersize % align), we can stuff the header into the offset.
564 Otherwise the header has to be aligned itself, and put before the offset data */
565 if (offset >= (headersize % align)) {
566 offset -= (headersize % align);
567 } else {
568 offset += align - (headersize % align);
569 }
570 headersize += offset;
571 *location -= headersize;
572 }
573 void *newdata = malloc(*datasize + headersize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000574 if (!newdata) {
Stefan Reinauer0a977592012-11-30 11:21:05 -0800575 fprintf(stderr, "E: Could not get %d bytes for CBFS file.\n", *datasize +
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000576 headersize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000577 exit(1);
578 }
Peter Stugef4aca1d2009-12-06 12:14:39 +0000579 memset(newdata, 0xff, *datasize + headersize);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000580 struct cbfs_file *nextfile = (struct cbfs_file *)newdata;
Stefan Reinauera1e48242011-10-21 14:24:57 -0700581 strncpy((char *)(nextfile->magic), "LARCHIVE", 8);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000582 nextfile->len = htonl(*datasize);
583 nextfile->type = htonl(type);
584 nextfile->checksum = 0; // FIXME?
585 nextfile->offset = htonl(headersize);
586 strcpy(newdata + sizeof(struct cbfs_file), filename);
587 memcpy(newdata + headersize, data, *datasize);
588 *datasize += headersize;
589 return newdata;
590}
591
592int create_cbfs_image(const char *romfile, uint32_t _romsize,
David Hendricks90ca3b62012-11-16 14:48:22 -0800593 const char *bootblock, uint32_t align, uint32_t offs)
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000594{
Stefan Reinauer63217582012-10-29 16:52:36 -0700595 uint32_t bootblocksize = 0;
596 struct cbfs_header *master_header;
597 unsigned char *romarea, *bootblk;
598
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000599 romsize = _romsize;
Stefan Reinauer63217582012-10-29 16:52:36 -0700600 romarea = malloc(romsize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000601 if (!romarea) {
Stefan Reinauer63217582012-10-29 16:52:36 -0700602 fprintf(stderr, "E: Could not get %d bytes of memory"
603 " for CBFS image.\n", romsize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000604 exit(1);
605 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000606 memset(romarea, 0xff, romsize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000607
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000608 if (align == 0)
609 align = 64;
610
Stefan Reinauer63217582012-10-29 16:52:36 -0700611 bootblk = loadfile(bootblock, &bootblocksize,
612 romarea + romsize, SEEK_END);
613 if (!bootblk) {
614 fprintf(stderr, "E: Could not load bootblock %s.\n",
615 bootblock);
616 free(romarea);
617 return 1;
618 }
619
David Hendricks90ca3b62012-11-16 14:48:22 -0800620 // TODO(hungte) Replace magic numbers by named constants.
621 switch (arch) {
622 case CBFS_ARCHITECTURE_ARMV7:
623 /* Set up physical/virtual mapping */
624 offset = romarea;
Stefan Reinauer853270a2009-09-22 15:55:01 +0000625
David Hendricks90ca3b62012-11-16 14:48:22 -0800626 /*
David Hendricks0b23d472013-01-14 20:58:50 -0800627 * The initial jump instruction and bootblock will be placed
628 * before and after the master header, respectively. The
629 * bootblock image must contain a blank, aligned region large
630 * enough for the master header to fit.
David Hendricks90ca3b62012-11-16 14:48:22 -0800631 *
David Hendricks0b23d472013-01-14 20:58:50 -0800632 * An anchor string must be left such that when cbfstool is run
633 * we can find it and insert the master header at the next
634 * aligned boundary.
David Hendricks90ca3b62012-11-16 14:48:22 -0800635 */
David Hendricks0b23d472013-01-14 20:58:50 -0800636 loadfile(bootblock, &bootblocksize, romarea + offs, SEEK_SET);
637
638 unsigned char *p = romarea + offs;
639 while (1) {
640 /* FIXME: assumes little endian... */
641 if (*(uint32_t *)p == 0xdeadbeef)
642 break;
643 if (p >= (romarea + _romsize)) {
644 fprintf(stderr, "E: Could not determine CBFS "
645 "header location.\n", bootblock);
646 return 1;
647 }
648 p += (sizeof(unsigned int));
649 }
650 unsigned int u = ALIGN((unsigned int)(p - romarea), align);
651 master_header = (struct cbfs_header *)(romarea + u);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000652
David Hendricks90ca3b62012-11-16 14:48:22 -0800653 master_header->magic = ntohl(CBFS_HEADER_MAGIC);
Hung-Te Lin086842a2013-01-04 12:33:03 +0800654 master_header->version = ntohl(CBFS_HEADER_VERSION);
David Hendricks90ca3b62012-11-16 14:48:22 -0800655 master_header->romsize = htonl(romsize);
656 master_header->bootblocksize = htonl(bootblocksize);
657 master_header->align = htonl(align);
658 master_header->offset = htonl(
659 ALIGN((0x40 + bootblocksize), align));
660 master_header->architecture = htonl(CBFS_ARCHITECTURE_ARMV7);
661
David Hendricks454856b2013-01-02 17:29:00 -0800662 ((uint32_t *) phys_to_virt(0x4 + offs))[0] =
David Hendricks90ca3b62012-11-16 14:48:22 -0800663 virt_to_phys(master_header);
664
665 recalculate_rom_geometry(romarea);
666
667 cbfs_create_empty_file(
David Hendricks454856b2013-01-02 17:29:00 -0800668 offs + ALIGN((0x40 + bootblocksize), align),
669 romsize - offs - sizeof(struct cbfs_file) -
670 ALIGN((bootblocksize + 0x40), align));
David Hendricks90ca3b62012-11-16 14:48:22 -0800671 break;
672
673 case CBFS_ARCHITECTURE_X86:
674 // Set up physical/virtual mapping
675 offset = romarea + romsize - 0x100000000ULL;
676
677 loadfile(bootblock, &bootblocksize, romarea + romsize,
678 SEEK_END);
679 master_header = (struct cbfs_header *)(romarea + romsize -
680 bootblocksize - sizeof(struct cbfs_header));
681
682 master_header->magic = ntohl(CBFS_HEADER_MAGIC);
Hung-Te Lin086842a2013-01-04 12:33:03 +0800683 master_header->version = ntohl(CBFS_HEADER_VERSION);
David Hendricks90ca3b62012-11-16 14:48:22 -0800684 master_header->romsize = htonl(romsize);
685 master_header->bootblocksize = htonl(bootblocksize);
686 master_header->align = htonl(align);
687 master_header->offset = htonl(offs);
688 master_header->architecture = htonl(CBFS_ARCHITECTURE_X86);
689
690 ((uint32_t *) phys_to_virt(CBFS_HEADPTR_ADDR_X86))[0] =
691 virt_to_phys(master_header);
692
693 recalculate_rom_geometry(romarea);
694
695 cbfs_create_empty_file((0 - romsize + offs) & 0xffffffff,
696 romsize - offs - bootblocksize -
697 sizeof(struct cbfs_header) -
698 sizeof(struct cbfs_file) - 16);
699 break;
700
701 default:
702 // Should not happen.
703 fprintf(stderr, "E: You found a bug in cbfstool.\n");
704 exit(1);
705 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000706
707 writerom(romfile, romarea, romsize);
Stefan Reinauer63217582012-10-29 16:52:36 -0700708 free(romarea);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000709 return 0;
710}
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000711
712static int in_segment(int addr, int size, int gran)
713{
714 return ((addr & ~(gran - 1)) == ((addr + size) & ~(gran - 1)));
715}
716
717uint32_t cbfs_find_location(const char *romfile, uint32_t filesize,
718 const char *filename, uint32_t alignment)
719{
Stefan Reinauer63217582012-10-29 16:52:36 -0700720 void *rom;
721 size_t filename_size, headersize, totalsize;
722 int ret = 0;
723 uint32_t current;
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000724
Stefan Reinauer63217582012-10-29 16:52:36 -0700725 rom = loadrom(romfile);
726 if (rom == NULL) {
727 fprintf(stderr, "E: Could not load ROM image '%s'.\n",
728 romfile);
729 return 0;
730 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000731
Stefan Reinauer63217582012-10-29 16:52:36 -0700732 filename_size = strlen(filename);
733 headersize = sizeof(struct cbfs_file) + ALIGN(filename_size + 1, 16) +
734 sizeof(struct cbfs_stage);
735 totalsize = headersize + filesize;
736
737 current = phys_start;
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000738 while (current < phys_end) {
Stefan Reinauer63217582012-10-29 16:52:36 -0700739 uint32_t top;
740 struct cbfs_file *thisfile;
741
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000742 if (!cbfs_file_header(current)) {
743 current += align;
744 continue;
745 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000746
Stefan Reinauer63217582012-10-29 16:52:36 -0700747 thisfile = (struct cbfs_file *)phys_to_virt(current);
748
749 top = current + ntohl(thisfile->len) + ntohl(thisfile->offset);
750
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000751 if (((ntohl(thisfile->type) == 0x0)
752 || (ntohl(thisfile->type) == 0xffffffff))
753 && (ntohl(thisfile->len) + ntohl(thisfile->offset) >=
754 totalsize)) {
755 if (in_segment
Stefan Reinauer63217582012-10-29 16:52:36 -0700756 (current + headersize, filesize, alignment)) {
757 ret = current + headersize;
758 break;
759 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000760 if ((ALIGN(current, alignment) + filesize < top)
761 && (ALIGN(current, alignment) - headersize >
762 current)
763 && in_segment(ALIGN(current, alignment), filesize,
Stefan Reinauer63217582012-10-29 16:52:36 -0700764 alignment)) {
765 ret = ALIGN(current, alignment);
766 break;
767 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000768 if ((ALIGN(current, alignment) + alignment + filesize <
769 top)
770 && (ALIGN(current, alignment) + alignment -
771 headersize > current)
772 && in_segment(ALIGN(current, alignment) + alignment,
Stefan Reinauer63217582012-10-29 16:52:36 -0700773 filesize, alignment)) {
774 ret = ALIGN(current, alignment) + alignment;
775 break;
776 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000777 }
778 current =
779 ALIGN(current + ntohl(thisfile->len) +
780 ntohl(thisfile->offset), align);
781 }
Stefan Reinauer63217582012-10-29 16:52:36 -0700782
783 free(rom);
784 return ret;
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000785}