blob: aa986963c1a83a92027415d92aff1fcf11277d2a [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
Hung-Te Lin332795c2013-01-28 15:53:34 +080030/* Utilities */
31
32/* Small, OS/libc independent runtime check for endianess */
33int is_big_endian(void)
34{
35 static const uint32_t inttest = 0x12345678;
36 uint8_t inttest_lsb = *(uint8_t *)&inttest;
37 if (inttest_lsb == 0x12) {
38 return 1;
39 }
40 return 0;
41}
42
Hung-Te Lin3cfacbf2013-01-30 00:43:46 +080043/* Buffer and file I/O */
44
45int buffer_create(struct buffer *buffer, size_t size, const char *name) {
46 buffer->name = strdup(name);
47 buffer->size = size;
48 buffer->data = (char *)malloc(buffer->size);
49 if (!buffer->data) {
50 fprintf(stderr, "buffer_create: Insufficient memory (0x%zx).\n",
51 size);
52 }
53 return (buffer->data == NULL);
54}
55
56int buffer_from_file(struct buffer *buffer, const char *filename) {
57 FILE *fp = fopen(filename, "rb");
58 if (!fp) {
59 perror(filename);
60 return -1;
61 }
62 fseek(fp, 0, SEEK_END);
63 buffer->size = ftell(fp);
64 buffer->name = strdup(filename);
65 rewind(fp);
66 buffer->data = (char *)malloc(buffer->size);
67 assert(buffer->data);
68 if (fread(buffer->data, 1, buffer->size, fp) != buffer->size) {
69 fprintf(stderr, "incomplete read: %s\n", filename);
70 fclose(fp);
71 return -1;
72 }
73 fclose(fp);
74 return 0;
75}
76
77int buffer_write_file(struct buffer *buffer, const char *filename) {
78 FILE *fp = fopen(filename, "wb");
79 if (!fp) {
80 perror(filename);
81 return -1;
82 }
83 assert(buffer && buffer->data);
84 if (fwrite(buffer->data, 1, buffer->size, fp) != buffer->size) {
85 fprintf(stderr, "incomplete write: %s\n", filename);
86 fclose(fp);
87 return -1;
88 }
89 fclose(fp);
90 return 0;
91}
92
93void buffer_delete(struct buffer *buffer) {
94 assert(buffer);
95 if (buffer->name) {
96 free(buffer->name);
97 buffer->name = NULL;
98 }
99 if (buffer->data) {
100 free(buffer->data);
101 buffer->data = NULL;
102 }
103 buffer->size = 0;
104}
105
Stefan Reinauer63217582012-10-29 16:52:36 -0700106size_t getfilesize(const char *filename)
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000107{
Stefan Reinauer63217582012-10-29 16:52:36 -0700108 size_t size;
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000109 FILE *file = fopen(filename, "rb");
Stefan Reinauer63217582012-10-29 16:52:36 -0700110 if (file == NULL)
111 return -1;
112
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000113 fseek(file, 0, SEEK_END);
114 size = ftell(file);
115 fclose(file);
116 return size;
117}
118
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000119void *loadfile(const char *filename, uint32_t * romsize_p, void *content,
120 int place)
121{
122 FILE *file = fopen(filename, "rb");
Patrick Georgi45d8a832009-09-15 08:21:46 +0000123 if (file == NULL)
124 return NULL;
Stefan Reinauer63217582012-10-29 16:52:36 -0700125
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000126 fseek(file, 0, SEEK_END);
127 *romsize_p = ftell(file);
128 fseek(file, 0, SEEK_SET);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000129 if (!content) {
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000130 content = malloc(*romsize_p);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000131 if (!content) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800132 ERROR("Could not get %d bytes for file %s\n",
133 *romsize_p, filename);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000134 exit(1);
135 }
136 } else if (place == SEEK_END)
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000137 content -= *romsize_p;
Stefan Reinauer853270a2009-09-22 15:55:01 +0000138
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000139 if (!fread(content, *romsize_p, 1, file)) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800140 ERROR("Failed to read %s\n", filename);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000141 return NULL;
142 }
143 fclose(file);
144 return content;
145}
146
Stefan Reinauer63217582012-10-29 16:52:36 -0700147static struct cbfs_header *master_header;
148static uint32_t phys_start, phys_end, align;
149uint32_t romsize;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000150void *offset;
David Hendricks90ca3b62012-11-16 14:48:22 -0800151uint32_t arch = CBFS_ARCHITECTURE_UNKNOWN;
152
153static struct {
154 uint32_t arch;
155 const char *name;
156} arch_names[] = {
157 { CBFS_ARCHITECTURE_ARMV7, "armv7" },
158 { CBFS_ARCHITECTURE_X86, "x86" },
159 { CBFS_ARCHITECTURE_UNKNOWN, "unknown" }
160};
161
162uint32_t string_to_arch(const char *arch_string)
163{
164 int i;
165 uint32_t ret = CBFS_ARCHITECTURE_UNKNOWN;
166
167 for (i = 0; i < ARRAY_SIZE(arch_names); i++) {
168 if (!strcasecmp(arch_string, arch_names[i].name)) {
169 ret = arch_names[i].arch;
170 break;
171 }
172 }
173
174 return ret;
175}
176
177const char *arch_to_string(uint32_t a)
178{
179 int i;
180 const char *ret = NULL;
181
182 for (i = 0; i < ARRAY_SIZE(arch_names); i++) {
183 if (a == arch_names[i].arch) {
184 ret = arch_names[i].name;
185 break;
186 }
187 }
188
189 return ret;
190
191}
192
193int find_master_header(void *romarea, size_t size)
194{
195 size_t offset;
196
197 if (master_header)
198 return 0;
199
200 for (offset = 0; offset < size - sizeof(struct cbfs_header); offset++) {
201 struct cbfs_header *tmp = romarea + offset;
202
203 if (tmp->magic == ntohl(CBFS_HEADER_MAGIC)) {
204 master_header = tmp;
205 break;
206 }
207 }
208
209 return master_header ? 0 : 1;
210}
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000211
212void recalculate_rom_geometry(void *romarea)
213{
David Hendricks90ca3b62012-11-16 14:48:22 -0800214 if (find_master_header(romarea, romsize)) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800215 ERROR("Cannot find master header\n");
David Hendricks90ca3b62012-11-16 14:48:22 -0800216 exit(1);
217 }
218
219 /* Update old headers */
Hung-Te Lin086842a2013-01-04 12:33:03 +0800220 if (master_header->version == CBFS_HEADER_VERSION1 &&
David Hendricks90ca3b62012-11-16 14:48:22 -0800221 ntohl(master_header->architecture) == CBFS_ARCHITECTURE_UNKNOWN) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800222 DEBUG("Updating CBFS master header to version 2\n");
David Hendricks90ca3b62012-11-16 14:48:22 -0800223 master_header->architecture = htonl(CBFS_ARCHITECTURE_X86);
224 }
225
226 arch = ntohl(master_header->architecture);
227
228 switch (arch) {
229 case CBFS_ARCHITECTURE_ARMV7:
230 offset = romarea;
231 phys_start = (0 + ntohl(master_header->offset)) & 0xffffffff;
232 phys_end = romsize & 0xffffffff;
233 break;
234 case CBFS_ARCHITECTURE_X86:
235 offset = romarea + romsize - 0x100000000ULL;
236 phys_start = (0 - romsize + ntohl(master_header->offset)) &
237 0xffffffff;
238 phys_end = (0 - ntohl(master_header->bootblocksize) -
239 sizeof(struct cbfs_header)) & 0xffffffff;
240 break;
241 default:
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800242 ERROR("Unknown architecture\n");
David Hendricks90ca3b62012-11-16 14:48:22 -0800243 exit(1);
244 }
245
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000246 align = ntohl(master_header->align);
247}
248
249void *loadrom(const char *filename)
250{
251 void *romarea = loadfile(filename, &romsize, 0, SEEK_SET);
Patrick Georgi45d8a832009-09-15 08:21:46 +0000252 if (romarea == NULL)
253 return NULL;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000254 recalculate_rom_geometry(romarea);
255 return romarea;
256}
257
Stefan Reinauer9bb043852010-06-24 13:37:59 +0000258int writerom(const char *filename, void *start, uint32_t size)
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000259{
260 FILE *file = fopen(filename, "wb");
Stefan Reinauer9bb043852010-06-24 13:37:59 +0000261 if (!file) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800262 ERROR("Could not open '%s' for writing: ", filename);
Stefan Reinauer9bb043852010-06-24 13:37:59 +0000263 perror("");
264 return 1;
265 }
266
267 if (fwrite(start, size, 1, file) != 1) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800268 ERROR("Could not write to '%s': ", filename);
Stefan Reinauer9bb043852010-06-24 13:37:59 +0000269 perror("");
270 return 1;
271 }
272
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000273 fclose(file);
Stefan Reinauer9bb043852010-06-24 13:37:59 +0000274 return 0;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000275}
276
David Hendricks90ca3b62012-11-16 14:48:22 -0800277int cbfs_file_header(unsigned long physaddr)
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000278{
279 /* maybe improve this test */
280 return (strncmp(phys_to_virt(physaddr), "LARCHIVE", 8) == 0);
281}
282
283struct cbfs_file *cbfs_create_empty_file(uint32_t physaddr, uint32_t size)
284{
285 struct cbfs_file *nextfile = (struct cbfs_file *)phys_to_virt(physaddr);
Stefan Reinauera1e48242011-10-21 14:24:57 -0700286 strncpy((char *)(nextfile->magic), "LARCHIVE", 8);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000287 nextfile->len = htonl(size);
288 nextfile->type = htonl(0xffffffff);
289 nextfile->checksum = 0; // FIXME?
290 nextfile->offset = htonl(sizeof(struct cbfs_file) + 16);
291 memset(((void *)nextfile) + sizeof(struct cbfs_file), 0, 16);
292 return nextfile;
293}
294
295int iself(unsigned char *input)
296{
297 Elf32_Ehdr *ehdr = (Elf32_Ehdr *) input;
298 return !memcmp(ehdr->e_ident, ELFMAG, 4);
299}
300
Mathias Krause941158f2012-04-12 21:36:23 +0200301static struct filetypes_t {
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000302 uint32_t type;
303 const char *name;
304} filetypes[] = {
305 {CBFS_COMPONENT_STAGE, "stage"},
306 {CBFS_COMPONENT_PAYLOAD, "payload"},
307 {CBFS_COMPONENT_OPTIONROM, "optionrom"},
Stefan Reinauer800379f2010-03-01 08:34:19 +0000308 {CBFS_COMPONENT_BOOTSPLASH, "bootsplash"},
309 {CBFS_COMPONENT_RAW, "raw"},
310 {CBFS_COMPONENT_VSA, "vsa"},
311 {CBFS_COMPONENT_MBI, "mbi"},
312 {CBFS_COMPONENT_MICROCODE, "microcode"},
Patrick Georgia865b172011-01-14 07:40:24 +0000313 {CBFS_COMPONENT_CMOS_DEFAULT, "cmos default"},
Mathias Krause941158f2012-04-12 21:36:23 +0200314 {CBFS_COMPONENT_CMOS_LAYOUT, "cmos layout"},
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000315 {CBFS_COMPONENT_DELETED, "deleted"},
316 {CBFS_COMPONENT_NULL, "null"}
317};
318
Stefan Reinauer07040582010-04-24 21:24:06 +0000319void print_supported_filetypes(void)
320{
321 int i, number = ARRAY_SIZE(filetypes);
322
323 for (i=0; i<number; i++) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800324 LOG(" %s%c", filetypes[i].name, (i==(number-1))?'\n':',');
Stefan Reinauer07040582010-04-24 21:24:06 +0000325 if ((i%8) == 7)
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800326 LOG("\n");
Stefan Reinauer07040582010-04-24 21:24:06 +0000327 }
328}
329
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000330const char *strfiletype(uint32_t number)
331{
Mathias Krause41c229c2012-07-17 21:17:15 +0200332 size_t i;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000333 for (i = 0; i < (sizeof(filetypes) / sizeof(struct filetypes_t)); i++)
334 if (filetypes[i].type == number)
335 return filetypes[i].name;
336 return "unknown";
337}
338
339uint64_t intfiletype(const char *name)
340{
Mathias Krause41c229c2012-07-17 21:17:15 +0200341 size_t i;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000342 for (i = 0; i < (sizeof(filetypes) / sizeof(struct filetypes_t)); i++)
343 if (strcmp(filetypes[i].name, name) == 0)
344 return filetypes[i].type;
345 return -1;
346}
347
348void print_cbfs_directory(const char *filename)
349{
Hung-Te Lin5a9f45c2013-01-28 23:42:25 +0800350 char *name = strdup(filename);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000351 printf
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800352 ("%s: %d kB, bootblocksize %d, romsize %d, offset 0x%x\n"
353 "alignment: %d bytes, architecture: %s\n\n",
Hung-Te Lin5a9f45c2013-01-28 23:42:25 +0800354 basename(name), romsize / 1024, ntohl(master_header->bootblocksize),
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800355 romsize, ntohl(master_header->offset), align, arch_to_string(arch));
Hung-Te Lin5a9f45c2013-01-28 23:42:25 +0800356 free(name);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000357 printf("%-30s %-10s %-12s Size\n", "Name", "Offset", "Type");
358 uint32_t current = phys_start;
359 while (current < phys_end) {
360 if (!cbfs_file_header(current)) {
361 current += align;
362 continue;
363 }
364 struct cbfs_file *thisfile =
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800365 (struct cbfs_file *)phys_to_virt(current);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000366 uint32_t length = ntohl(thisfile->len);
Maciej Pijankaf44eb782010-01-07 21:37:18 +0000367 char *fname = (char *)(phys_to_virt(current) + sizeof(struct cbfs_file));
Stefan Reinauer14e22772010-04-27 06:56:47 +0000368 if (strlen(fname) == 0)
Maciej Pijankaf44eb782010-01-07 21:37:18 +0000369 fname = "(empty)";
370
Stefan Reinauer14e22772010-04-27 06:56:47 +0000371 printf("%-30s 0x%-8x %-12s %d\n", fname,
David Hendricks90ca3b62012-11-16 14:48:22 -0800372 current - phys_start + ntohl(master_header->offset),
373 strfiletype(ntohl(thisfile->type)), length);
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800374
375 /* note the components of the subheader are in host order ... */
376 switch (ntohl(thisfile->type)) {
377 case CBFS_COMPONENT_STAGE:
378 {
379 struct cbfs_stage *stage = CBFS_SUBHEADER(thisfile);
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800380 INFO(" %s compression, entry: 0x%llx, load: 0x%llx, length: %d/%d\n",
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800381 stage->compression == CBFS_COMPRESS_LZMA ? "LZMA" : "no",
382 (unsigned long long)stage->entry,
383 (unsigned long long)stage->load,
384 stage->len,
385 stage->memlen);
386 break;
387 }
388 case CBFS_COMPONENT_PAYLOAD:
389 {
390 struct cbfs_payload_segment *payload = CBFS_SUBHEADER(thisfile);
391 while(payload) {
392 switch(payload->type) {
393 case PAYLOAD_SEGMENT_CODE:
394 case PAYLOAD_SEGMENT_DATA:
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800395 INFO(" %s (%s compression, offset: 0x%x, load: 0x%llx, length: %d/%d)\n",
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800396 payload->type == PAYLOAD_SEGMENT_CODE ? "code " : "data" ,
397 payload->compression == CBFS_COMPRESS_LZMA ? "LZMA" : "no",
398 ntohl(payload->offset),
399 (unsigned long long)ntohll(payload->load_addr),
400 ntohl(payload->len), ntohl(payload->mem_len));
401 break;
402 case PAYLOAD_SEGMENT_ENTRY:
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800403 INFO(" entry (0x%llx)\n", (unsigned long long)ntohll(payload->load_addr));
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800404 break;
405 case PAYLOAD_SEGMENT_BSS:
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800406 INFO(" BSS (address 0x%016llx, length 0x%x)\n", (unsigned long long)ntohll(payload->load_addr), ntohl(payload->len));
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800407 break;
408 case PAYLOAD_SEGMENT_PARAMS:
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800409 INFO(" parameters\n");
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800410 break;
411 default:
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800412 INFO(" %x (%s compression, offset: 0x%x, load: 0x%llx, length: %d/%d\n",
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800413 payload->type,
414 payload->compression == CBFS_COMPRESS_LZMA ? "LZMA" : "no",
415 ntohl(payload->offset),
416 (unsigned long long)ntohll(payload->load_addr),
417 ntohl(payload->len),
418 ntohl(payload->mem_len));
419 break;
420 }
421
422 if(payload->type == PAYLOAD_SEGMENT_ENTRY)
423 payload=NULL;
424 else
425 payload++;
426 }
427 break;
428 }
429 default:
430 break;
431 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000432 current =
433 ALIGN(current + ntohl(thisfile->len) +
434 ntohl(thisfile->offset), align);
435 }
436}
437
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000438int extract_file_from_cbfs(const char *filename, const char *payloadname, const char *outpath)
439{
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000440 FILE *outfile = NULL;
441 uint32_t current = phys_start;
442 while (current < phys_end) {
443 if (!cbfs_file_header(current)) {
444 current += align;
445 continue;
446 }
447
448 // Locate the file start struct
449 struct cbfs_file *thisfile =
450 (struct cbfs_file *)phys_to_virt(current);
451 // And its length
452 uint32_t length = ntohl(thisfile->len);
453 // Locate the file name
454 char *fname = (char *)(phys_to_virt(current) + sizeof(struct cbfs_file));
Stefan Reinauera1e48242011-10-21 14:24:57 -0700455
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000456 // It's not the file we are looking for..
457 if (strcmp(fname, payloadname) != 0)
458 {
459 current =
460 ALIGN(current + ntohl(thisfile->len) +
461 ntohl(thisfile->offset), align);
462 continue;
463 }
464
465 // Else, it's our file.
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800466 LOG("Found file %.30s at 0x%x, type %.12s, size %d\n", fname,
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000467 current - phys_start, strfiletype(ntohl(thisfile->type)),
468 length);
469
470 // If we are not dumping to stdout, open the out file.
471 outfile = fopen(outpath, "wb");
472 if (!outfile)
473 {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800474 ERROR("Could not open the file %s for writing.\n", outpath);
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000475 return 1;
476 }
477
478 if (ntohl(thisfile->type) != CBFS_COMPONENT_RAW)
479 {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800480 WARN("Only 'raw' files are safe to extract.\n");
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000481 }
482
483 fwrite(((char *)thisfile)
484 + ntohl(thisfile->offset), length, 1, outfile);
485
486 fclose(outfile);
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800487 LOG("Successfully dumped the file.\n");
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000488
489 // We'll only dump one file.
490 return 0;
491 }
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800492 ERROR("File %s not found.\n", payloadname);
Stefan Reinauera1e48242011-10-21 14:24:57 -0700493 return 1;
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000494}
495
496
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000497int add_file_to_cbfs(void *content, uint32_t contentsize, uint32_t location)
498{
499 uint32_t current = phys_start;
500 while (current < phys_end) {
501 if (!cbfs_file_header(current)) {
502 current += align;
503 continue;
504 }
505 struct cbfs_file *thisfile =
506 (struct cbfs_file *)phys_to_virt(current);
507 uint32_t length = ntohl(thisfile->len);
508
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800509 DEBUG("at %x, %x bytes\n", current, length);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000510 /* Is this a free chunk? */
511 if ((thisfile->type == CBFS_COMPONENT_DELETED)
512 || (thisfile->type == CBFS_COMPONENT_NULL)) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800513 DEBUG("null||deleted at %x, %x bytes\n", current,
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000514 length);
515 /* if this is the right size, and if specified, the right location, use it */
516 if ((contentsize <= length)
517 && ((location == 0) || (current == location))) {
518 if (contentsize < length) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800519 DEBUG("this chunk is %x bytes, we need %x. create a new chunk at %x with %x bytes\n",
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000520 length, contentsize,
521 ALIGN(current + contentsize,
522 align),
523 length - contentsize);
524 uint32_t start =
525 ALIGN(current + contentsize, align);
526 uint32_t size =
527 current + ntohl(thisfile->offset)
528 + length - start - 16 -
529 sizeof(struct cbfs_file);
530 cbfs_create_empty_file(start, size);
531 }
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800532 DEBUG("copying data\n");
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000533 memcpy(phys_to_virt(current), content,
534 contentsize);
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000535 return 0;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000536 }
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000537 if (location != 0) {
538 /* CBFS has the constraint that the chain always moves up in memory. so once
539 we're past the place we seek, we don't need to look any further */
540 if (current > location) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800541 ERROR("The requested space is not available\n");
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000542 return 1;
543 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000544
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000545 /* Is the requested location inside the current chunk? */
546 if ((current < location)
547 && ((location + contentsize) <=
548 (current + length))) {
549 /* Split it up. In the next iteration the code will be at the right place. */
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800550 DEBUG("split up. new length: %x\n",
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000551 location - current -
552 ntohl(thisfile->offset));
553 thisfile->len =
554 htonl(location - current -
555 ntohl(thisfile->offset));
Stefan Reinauera1e48242011-10-21 14:24:57 -0700556 cbfs_create_empty_file(location,
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000557 length -
558 (location -
559 current));
560 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000561 }
562 }
563 current =
564 ALIGN(current + ntohl(thisfile->len) +
565 ntohl(thisfile->offset), align);
566 }
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800567 ERROR("Could not add the file to CBFS, it's probably too big.\n");
568 ERROR("File size: %d bytes (%d KB).\n", contentsize, contentsize/1024);
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000569 return 1;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000570}
571
Gabe Blacke1bb49e2012-01-27 00:33:47 -0800572
573static struct cbfs_file *merge_adjacent_files(struct cbfs_file *first,
574 struct cbfs_file *second)
575{
576 uint32_t new_length =
577 ntohl(first->len) + ntohl(second->len) + ntohl(second->offset);
578 first->len = htonl(new_length);
579 first->checksum = 0; // FIXME?
580 return first;
581}
582
583static struct cbfs_file *next_file(struct cbfs_file *prev)
584{
585 uint32_t pos = (prev == NULL) ? phys_start :
586 ALIGN(virt_to_phys(prev) + ntohl(prev->len) + ntohl(prev->offset),
587 align);
588
589 for (; pos < phys_end; pos += align) {
590 if (cbfs_file_header(pos))
591 return (struct cbfs_file *)phys_to_virt(pos);
592 }
593 return NULL;
594}
595
596
597int remove_file_from_cbfs(const char *filename)
598{
599 struct cbfs_file *prev = NULL;
600 struct cbfs_file *cur = next_file(prev);
601 struct cbfs_file *next = next_file(cur);
602 for (; cur; prev = cur, cur = next, next = next_file(next)) {
603
604 /* Check if this is the file to remove. */
605 char *name = (char *)cur + sizeof(*cur);
606 if (strcmp(name, filename))
607 continue;
608
609 /* Mark the file as free space and erase its name. */
610 cur->type = CBFS_COMPONENT_NULL;
611 name[0] = '\0';
612
613 /* Merge it with the previous file if possible. */
614 if (prev && prev->type == CBFS_COMPONENT_NULL)
615 cur = merge_adjacent_files(prev, cur);
616
617 /* Merge it with the next file if possible. */
618 if (next && next->type == CBFS_COMPONENT_NULL)
619 merge_adjacent_files(cur, next);
620
621 return 0;
622 }
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800623 ERROR("CBFS file %s not found.\n", filename);
Gabe Blacke1bb49e2012-01-27 00:33:47 -0800624 return 1;
625}
626
627
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000628/* returns new data block with cbfs_file header, suitable to dump into the ROM. location returns
629 the new location that points to the cbfs_file header */
630void *create_cbfs_file(const char *filename, void *data, uint32_t * datasize,
631 uint32_t type, uint32_t * location)
632{
633 uint32_t filename_len = ALIGN(strlen(filename) + 1, 16);
634 uint32_t headersize = sizeof(struct cbfs_file) + filename_len;
635 if ((location != 0) && (*location != 0)) {
636 uint32_t offset = *location % align;
637 /* If offset >= (headersize % align), we can stuff the header into the offset.
638 Otherwise the header has to be aligned itself, and put before the offset data */
639 if (offset >= (headersize % align)) {
640 offset -= (headersize % align);
641 } else {
642 offset += align - (headersize % align);
643 }
644 headersize += offset;
645 *location -= headersize;
646 }
647 void *newdata = malloc(*datasize + headersize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000648 if (!newdata) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800649 ERROR("Could not get %d bytes for CBFS file.\n", *datasize +
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000650 headersize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000651 exit(1);
652 }
Peter Stugef4aca1d2009-12-06 12:14:39 +0000653 memset(newdata, 0xff, *datasize + headersize);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000654 struct cbfs_file *nextfile = (struct cbfs_file *)newdata;
Stefan Reinauera1e48242011-10-21 14:24:57 -0700655 strncpy((char *)(nextfile->magic), "LARCHIVE", 8);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000656 nextfile->len = htonl(*datasize);
657 nextfile->type = htonl(type);
658 nextfile->checksum = 0; // FIXME?
659 nextfile->offset = htonl(headersize);
660 strcpy(newdata + sizeof(struct cbfs_file), filename);
661 memcpy(newdata + headersize, data, *datasize);
662 *datasize += headersize;
663 return newdata;
664}
665
666int create_cbfs_image(const char *romfile, uint32_t _romsize,
David Hendricks90ca3b62012-11-16 14:48:22 -0800667 const char *bootblock, uint32_t align, uint32_t offs)
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000668{
Stefan Reinauer63217582012-10-29 16:52:36 -0700669 uint32_t bootblocksize = 0;
670 struct cbfs_header *master_header;
671 unsigned char *romarea, *bootblk;
672
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000673 romsize = _romsize;
Stefan Reinauer63217582012-10-29 16:52:36 -0700674 romarea = malloc(romsize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000675 if (!romarea) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800676 ERROR("Could not get %d bytes of memory"
Stefan Reinauer63217582012-10-29 16:52:36 -0700677 " for CBFS image.\n", romsize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000678 exit(1);
679 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000680 memset(romarea, 0xff, romsize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000681
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000682 if (align == 0)
683 align = 64;
684
Stefan Reinauer63217582012-10-29 16:52:36 -0700685 bootblk = loadfile(bootblock, &bootblocksize,
686 romarea + romsize, SEEK_END);
687 if (!bootblk) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800688 ERROR("Could not load bootblock %s.\n",
Stefan Reinauer63217582012-10-29 16:52:36 -0700689 bootblock);
690 free(romarea);
691 return 1;
692 }
693
David Hendricks90ca3b62012-11-16 14:48:22 -0800694 // TODO(hungte) Replace magic numbers by named constants.
695 switch (arch) {
696 case CBFS_ARCHITECTURE_ARMV7:
697 /* Set up physical/virtual mapping */
698 offset = romarea;
Stefan Reinauer853270a2009-09-22 15:55:01 +0000699
David Hendricks90ca3b62012-11-16 14:48:22 -0800700 /*
David Hendricks0b23d472013-01-14 20:58:50 -0800701 * The initial jump instruction and bootblock will be placed
702 * before and after the master header, respectively. The
703 * bootblock image must contain a blank, aligned region large
704 * enough for the master header to fit.
David Hendricks90ca3b62012-11-16 14:48:22 -0800705 *
David Hendricks0b23d472013-01-14 20:58:50 -0800706 * An anchor string must be left such that when cbfstool is run
707 * we can find it and insert the master header at the next
708 * aligned boundary.
David Hendricks90ca3b62012-11-16 14:48:22 -0800709 */
David Hendricks0b23d472013-01-14 20:58:50 -0800710 loadfile(bootblock, &bootblocksize, romarea + offs, SEEK_SET);
711
712 unsigned char *p = romarea + offs;
713 while (1) {
714 /* FIXME: assumes little endian... */
715 if (*(uint32_t *)p == 0xdeadbeef)
716 break;
717 if (p >= (romarea + _romsize)) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800718 ERROR("Could not determine CBFS "
719 "header location.\n");
David Hendricks0b23d472013-01-14 20:58:50 -0800720 return 1;
721 }
722 p += (sizeof(unsigned int));
723 }
724 unsigned int u = ALIGN((unsigned int)(p - romarea), align);
725 master_header = (struct cbfs_header *)(romarea + u);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000726
David Hendricks90ca3b62012-11-16 14:48:22 -0800727 master_header->magic = ntohl(CBFS_HEADER_MAGIC);
Hung-Te Lin086842a2013-01-04 12:33:03 +0800728 master_header->version = ntohl(CBFS_HEADER_VERSION);
David Hendricks90ca3b62012-11-16 14:48:22 -0800729 master_header->romsize = htonl(romsize);
730 master_header->bootblocksize = htonl(bootblocksize);
731 master_header->align = htonl(align);
732 master_header->offset = htonl(
733 ALIGN((0x40 + bootblocksize), align));
734 master_header->architecture = htonl(CBFS_ARCHITECTURE_ARMV7);
735
David Hendricks454856b2013-01-02 17:29:00 -0800736 ((uint32_t *) phys_to_virt(0x4 + offs))[0] =
David Hendricks90ca3b62012-11-16 14:48:22 -0800737 virt_to_phys(master_header);
738
739 recalculate_rom_geometry(romarea);
740
741 cbfs_create_empty_file(
David Hendricks454856b2013-01-02 17:29:00 -0800742 offs + ALIGN((0x40 + bootblocksize), align),
743 romsize - offs - sizeof(struct cbfs_file) -
744 ALIGN((bootblocksize + 0x40), align));
David Hendricks90ca3b62012-11-16 14:48:22 -0800745 break;
746
747 case CBFS_ARCHITECTURE_X86:
748 // Set up physical/virtual mapping
749 offset = romarea + romsize - 0x100000000ULL;
750
751 loadfile(bootblock, &bootblocksize, romarea + romsize,
752 SEEK_END);
753 master_header = (struct cbfs_header *)(romarea + romsize -
754 bootblocksize - sizeof(struct cbfs_header));
755
756 master_header->magic = ntohl(CBFS_HEADER_MAGIC);
Hung-Te Lin086842a2013-01-04 12:33:03 +0800757 master_header->version = ntohl(CBFS_HEADER_VERSION);
David Hendricks90ca3b62012-11-16 14:48:22 -0800758 master_header->romsize = htonl(romsize);
759 master_header->bootblocksize = htonl(bootblocksize);
760 master_header->align = htonl(align);
761 master_header->offset = htonl(offs);
762 master_header->architecture = htonl(CBFS_ARCHITECTURE_X86);
763
764 ((uint32_t *) phys_to_virt(CBFS_HEADPTR_ADDR_X86))[0] =
765 virt_to_phys(master_header);
766
767 recalculate_rom_geometry(romarea);
768
769 cbfs_create_empty_file((0 - romsize + offs) & 0xffffffff,
770 romsize - offs - bootblocksize -
771 sizeof(struct cbfs_header) -
772 sizeof(struct cbfs_file) - 16);
773 break;
774
775 default:
776 // Should not happen.
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800777 ERROR("You found a bug in cbfstool.\n");
David Hendricks90ca3b62012-11-16 14:48:22 -0800778 exit(1);
779 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000780
781 writerom(romfile, romarea, romsize);
Stefan Reinauer63217582012-10-29 16:52:36 -0700782 free(romarea);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000783 return 0;
784}
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000785
786static int in_segment(int addr, int size, int gran)
787{
788 return ((addr & ~(gran - 1)) == ((addr + size) & ~(gran - 1)));
789}
790
791uint32_t cbfs_find_location(const char *romfile, uint32_t filesize,
792 const char *filename, uint32_t alignment)
793{
Stefan Reinauer63217582012-10-29 16:52:36 -0700794 void *rom;
795 size_t filename_size, headersize, totalsize;
796 int ret = 0;
797 uint32_t current;
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000798
Stefan Reinauer63217582012-10-29 16:52:36 -0700799 rom = loadrom(romfile);
800 if (rom == NULL) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800801 ERROR("Could not load ROM image '%s'.\n", romfile);
Stefan Reinauer63217582012-10-29 16:52:36 -0700802 return 0;
803 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000804
Stefan Reinauer63217582012-10-29 16:52:36 -0700805 filename_size = strlen(filename);
806 headersize = sizeof(struct cbfs_file) + ALIGN(filename_size + 1, 16) +
807 sizeof(struct cbfs_stage);
808 totalsize = headersize + filesize;
809
810 current = phys_start;
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000811 while (current < phys_end) {
Stefan Reinauer63217582012-10-29 16:52:36 -0700812 uint32_t top;
813 struct cbfs_file *thisfile;
814
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000815 if (!cbfs_file_header(current)) {
816 current += align;
817 continue;
818 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000819
Stefan Reinauer63217582012-10-29 16:52:36 -0700820 thisfile = (struct cbfs_file *)phys_to_virt(current);
821
822 top = current + ntohl(thisfile->len) + ntohl(thisfile->offset);
823
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000824 if (((ntohl(thisfile->type) == 0x0)
825 || (ntohl(thisfile->type) == 0xffffffff))
826 && (ntohl(thisfile->len) + ntohl(thisfile->offset) >=
827 totalsize)) {
828 if (in_segment
Stefan Reinauer63217582012-10-29 16:52:36 -0700829 (current + headersize, filesize, alignment)) {
830 ret = current + headersize;
831 break;
832 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000833 if ((ALIGN(current, alignment) + filesize < top)
834 && (ALIGN(current, alignment) - headersize >
835 current)
836 && in_segment(ALIGN(current, alignment), filesize,
Stefan Reinauer63217582012-10-29 16:52:36 -0700837 alignment)) {
838 ret = ALIGN(current, alignment);
839 break;
840 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000841 if ((ALIGN(current, alignment) + alignment + filesize <
842 top)
843 && (ALIGN(current, alignment) + alignment -
844 headersize > current)
845 && in_segment(ALIGN(current, alignment) + alignment,
Stefan Reinauer63217582012-10-29 16:52:36 -0700846 filesize, alignment)) {
847 ret = ALIGN(current, alignment) + alignment;
848 break;
849 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000850 }
851 current =
852 ALIGN(current + ntohl(thisfile->len) +
853 ntohl(thisfile->offset), align);
854 }
Stefan Reinauer63217582012-10-29 16:52:36 -0700855
856 free(rom);
857 return ret;
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000858}