blob: d4560f691f799daf7788dc13d0717061c3b19bf9 [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
Stefan Reinauer63217582012-10-29 16:52:36 -070043size_t getfilesize(const char *filename)
Patrick Georgi0da38dd2009-11-09 17:18:02 +000044{
Stefan Reinauer63217582012-10-29 16:52:36 -070045 size_t size;
Patrick Georgi0da38dd2009-11-09 17:18:02 +000046 FILE *file = fopen(filename, "rb");
Stefan Reinauer63217582012-10-29 16:52:36 -070047 if (file == NULL)
48 return -1;
49
Patrick Georgi0da38dd2009-11-09 17:18:02 +000050 fseek(file, 0, SEEK_END);
51 size = ftell(file);
52 fclose(file);
53 return size;
54}
55
Patrick Georgib7b56dd82009-09-14 13:29:27 +000056void *loadfile(const char *filename, uint32_t * romsize_p, void *content,
57 int place)
58{
59 FILE *file = fopen(filename, "rb");
Patrick Georgi45d8a832009-09-15 08:21:46 +000060 if (file == NULL)
61 return NULL;
Stefan Reinauer63217582012-10-29 16:52:36 -070062
Patrick Georgib7b56dd82009-09-14 13:29:27 +000063 fseek(file, 0, SEEK_END);
64 *romsize_p = ftell(file);
65 fseek(file, 0, SEEK_SET);
Stefan Reinauer853270a2009-09-22 15:55:01 +000066 if (!content) {
Patrick Georgib7b56dd82009-09-14 13:29:27 +000067 content = malloc(*romsize_p);
Stefan Reinauer853270a2009-09-22 15:55:01 +000068 if (!content) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +080069 ERROR("Could not get %d bytes for file %s\n",
70 *romsize_p, filename);
Stefan Reinauer853270a2009-09-22 15:55:01 +000071 exit(1);
72 }
73 } else if (place == SEEK_END)
Patrick Georgib7b56dd82009-09-14 13:29:27 +000074 content -= *romsize_p;
Stefan Reinauer853270a2009-09-22 15:55:01 +000075
Patrick Georgib7b56dd82009-09-14 13:29:27 +000076 if (!fread(content, *romsize_p, 1, file)) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +080077 ERROR("Failed to read %s\n", filename);
Patrick Georgib7b56dd82009-09-14 13:29:27 +000078 return NULL;
79 }
80 fclose(file);
81 return content;
82}
83
Stefan Reinauer63217582012-10-29 16:52:36 -070084static struct cbfs_header *master_header;
85static uint32_t phys_start, phys_end, align;
86uint32_t romsize;
Patrick Georgib7b56dd82009-09-14 13:29:27 +000087void *offset;
David Hendricks90ca3b62012-11-16 14:48:22 -080088uint32_t arch = CBFS_ARCHITECTURE_UNKNOWN;
89
90static struct {
91 uint32_t arch;
92 const char *name;
93} arch_names[] = {
94 { CBFS_ARCHITECTURE_ARMV7, "armv7" },
95 { CBFS_ARCHITECTURE_X86, "x86" },
96 { CBFS_ARCHITECTURE_UNKNOWN, "unknown" }
97};
98
99uint32_t string_to_arch(const char *arch_string)
100{
101 int i;
102 uint32_t ret = CBFS_ARCHITECTURE_UNKNOWN;
103
104 for (i = 0; i < ARRAY_SIZE(arch_names); i++) {
105 if (!strcasecmp(arch_string, arch_names[i].name)) {
106 ret = arch_names[i].arch;
107 break;
108 }
109 }
110
111 return ret;
112}
113
114const char *arch_to_string(uint32_t a)
115{
116 int i;
117 const char *ret = NULL;
118
119 for (i = 0; i < ARRAY_SIZE(arch_names); i++) {
120 if (a == arch_names[i].arch) {
121 ret = arch_names[i].name;
122 break;
123 }
124 }
125
126 return ret;
127
128}
129
130int find_master_header(void *romarea, size_t size)
131{
132 size_t offset;
133
134 if (master_header)
135 return 0;
136
137 for (offset = 0; offset < size - sizeof(struct cbfs_header); offset++) {
138 struct cbfs_header *tmp = romarea + offset;
139
140 if (tmp->magic == ntohl(CBFS_HEADER_MAGIC)) {
141 master_header = tmp;
142 break;
143 }
144 }
145
146 return master_header ? 0 : 1;
147}
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000148
149void recalculate_rom_geometry(void *romarea)
150{
David Hendricks90ca3b62012-11-16 14:48:22 -0800151 if (find_master_header(romarea, romsize)) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800152 ERROR("Cannot find master header\n");
David Hendricks90ca3b62012-11-16 14:48:22 -0800153 exit(1);
154 }
155
156 /* Update old headers */
Hung-Te Lin086842a2013-01-04 12:33:03 +0800157 if (master_header->version == CBFS_HEADER_VERSION1 &&
David Hendricks90ca3b62012-11-16 14:48:22 -0800158 ntohl(master_header->architecture) == CBFS_ARCHITECTURE_UNKNOWN) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800159 DEBUG("Updating CBFS master header to version 2\n");
David Hendricks90ca3b62012-11-16 14:48:22 -0800160 master_header->architecture = htonl(CBFS_ARCHITECTURE_X86);
161 }
162
163 arch = ntohl(master_header->architecture);
164
165 switch (arch) {
166 case CBFS_ARCHITECTURE_ARMV7:
167 offset = romarea;
168 phys_start = (0 + ntohl(master_header->offset)) & 0xffffffff;
169 phys_end = romsize & 0xffffffff;
170 break;
171 case CBFS_ARCHITECTURE_X86:
172 offset = romarea + romsize - 0x100000000ULL;
173 phys_start = (0 - romsize + ntohl(master_header->offset)) &
174 0xffffffff;
175 phys_end = (0 - ntohl(master_header->bootblocksize) -
176 sizeof(struct cbfs_header)) & 0xffffffff;
177 break;
178 default:
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800179 ERROR("Unknown architecture\n");
David Hendricks90ca3b62012-11-16 14:48:22 -0800180 exit(1);
181 }
182
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000183 align = ntohl(master_header->align);
184}
185
186void *loadrom(const char *filename)
187{
188 void *romarea = loadfile(filename, &romsize, 0, SEEK_SET);
Patrick Georgi45d8a832009-09-15 08:21:46 +0000189 if (romarea == NULL)
190 return NULL;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000191 recalculate_rom_geometry(romarea);
192 return romarea;
193}
194
Stefan Reinauer9bb043852010-06-24 13:37:59 +0000195int writerom(const char *filename, void *start, uint32_t size)
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000196{
197 FILE *file = fopen(filename, "wb");
Stefan Reinauer9bb043852010-06-24 13:37:59 +0000198 if (!file) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800199 ERROR("Could not open '%s' for writing: ", filename);
Stefan Reinauer9bb043852010-06-24 13:37:59 +0000200 perror("");
201 return 1;
202 }
203
204 if (fwrite(start, size, 1, file) != 1) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800205 ERROR("Could not write to '%s': ", filename);
Stefan Reinauer9bb043852010-06-24 13:37:59 +0000206 perror("");
207 return 1;
208 }
209
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000210 fclose(file);
Stefan Reinauer9bb043852010-06-24 13:37:59 +0000211 return 0;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000212}
213
David Hendricks90ca3b62012-11-16 14:48:22 -0800214int cbfs_file_header(unsigned long physaddr)
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000215{
216 /* maybe improve this test */
217 return (strncmp(phys_to_virt(physaddr), "LARCHIVE", 8) == 0);
218}
219
220struct cbfs_file *cbfs_create_empty_file(uint32_t physaddr, uint32_t size)
221{
222 struct cbfs_file *nextfile = (struct cbfs_file *)phys_to_virt(physaddr);
Stefan Reinauera1e48242011-10-21 14:24:57 -0700223 strncpy((char *)(nextfile->magic), "LARCHIVE", 8);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000224 nextfile->len = htonl(size);
225 nextfile->type = htonl(0xffffffff);
226 nextfile->checksum = 0; // FIXME?
227 nextfile->offset = htonl(sizeof(struct cbfs_file) + 16);
228 memset(((void *)nextfile) + sizeof(struct cbfs_file), 0, 16);
229 return nextfile;
230}
231
232int iself(unsigned char *input)
233{
234 Elf32_Ehdr *ehdr = (Elf32_Ehdr *) input;
235 return !memcmp(ehdr->e_ident, ELFMAG, 4);
236}
237
Mathias Krause941158f2012-04-12 21:36:23 +0200238static struct filetypes_t {
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000239 uint32_t type;
240 const char *name;
241} filetypes[] = {
242 {CBFS_COMPONENT_STAGE, "stage"},
243 {CBFS_COMPONENT_PAYLOAD, "payload"},
244 {CBFS_COMPONENT_OPTIONROM, "optionrom"},
Stefan Reinauer800379f2010-03-01 08:34:19 +0000245 {CBFS_COMPONENT_BOOTSPLASH, "bootsplash"},
246 {CBFS_COMPONENT_RAW, "raw"},
247 {CBFS_COMPONENT_VSA, "vsa"},
248 {CBFS_COMPONENT_MBI, "mbi"},
249 {CBFS_COMPONENT_MICROCODE, "microcode"},
Patrick Georgia865b172011-01-14 07:40:24 +0000250 {CBFS_COMPONENT_CMOS_DEFAULT, "cmos default"},
Mathias Krause941158f2012-04-12 21:36:23 +0200251 {CBFS_COMPONENT_CMOS_LAYOUT, "cmos layout"},
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000252 {CBFS_COMPONENT_DELETED, "deleted"},
253 {CBFS_COMPONENT_NULL, "null"}
254};
255
Stefan Reinauer07040582010-04-24 21:24:06 +0000256void print_supported_filetypes(void)
257{
258 int i, number = ARRAY_SIZE(filetypes);
259
260 for (i=0; i<number; i++) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800261 LOG(" %s%c", filetypes[i].name, (i==(number-1))?'\n':',');
Stefan Reinauer07040582010-04-24 21:24:06 +0000262 if ((i%8) == 7)
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800263 LOG("\n");
Stefan Reinauer07040582010-04-24 21:24:06 +0000264 }
265}
266
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000267const char *strfiletype(uint32_t number)
268{
Mathias Krause41c229c2012-07-17 21:17:15 +0200269 size_t i;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000270 for (i = 0; i < (sizeof(filetypes) / sizeof(struct filetypes_t)); i++)
271 if (filetypes[i].type == number)
272 return filetypes[i].name;
273 return "unknown";
274}
275
276uint64_t intfiletype(const char *name)
277{
Mathias Krause41c229c2012-07-17 21:17:15 +0200278 size_t i;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000279 for (i = 0; i < (sizeof(filetypes) / sizeof(struct filetypes_t)); i++)
280 if (strcmp(filetypes[i].name, name) == 0)
281 return filetypes[i].type;
282 return -1;
283}
284
285void print_cbfs_directory(const char *filename)
286{
Hung-Te Lin5a9f45c2013-01-28 23:42:25 +0800287 char *name = strdup(filename);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000288 printf
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800289 ("%s: %d kB, bootblocksize %d, romsize %d, offset 0x%x\n"
290 "alignment: %d bytes, architecture: %s\n\n",
Hung-Te Lin5a9f45c2013-01-28 23:42:25 +0800291 basename(name), romsize / 1024, ntohl(master_header->bootblocksize),
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800292 romsize, ntohl(master_header->offset), align, arch_to_string(arch));
Hung-Te Lin5a9f45c2013-01-28 23:42:25 +0800293 free(name);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000294 printf("%-30s %-10s %-12s Size\n", "Name", "Offset", "Type");
295 uint32_t current = phys_start;
296 while (current < phys_end) {
297 if (!cbfs_file_header(current)) {
298 current += align;
299 continue;
300 }
301 struct cbfs_file *thisfile =
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800302 (struct cbfs_file *)phys_to_virt(current);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000303 uint32_t length = ntohl(thisfile->len);
Maciej Pijankaf44eb782010-01-07 21:37:18 +0000304 char *fname = (char *)(phys_to_virt(current) + sizeof(struct cbfs_file));
Stefan Reinauer14e22772010-04-27 06:56:47 +0000305 if (strlen(fname) == 0)
Maciej Pijankaf44eb782010-01-07 21:37:18 +0000306 fname = "(empty)";
307
Stefan Reinauer14e22772010-04-27 06:56:47 +0000308 printf("%-30s 0x%-8x %-12s %d\n", fname,
David Hendricks90ca3b62012-11-16 14:48:22 -0800309 current - phys_start + ntohl(master_header->offset),
310 strfiletype(ntohl(thisfile->type)), length);
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800311
312 /* note the components of the subheader are in host order ... */
313 switch (ntohl(thisfile->type)) {
314 case CBFS_COMPONENT_STAGE:
315 {
316 struct cbfs_stage *stage = CBFS_SUBHEADER(thisfile);
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800317 INFO(" %s compression, entry: 0x%llx, load: 0x%llx, length: %d/%d\n",
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800318 stage->compression == CBFS_COMPRESS_LZMA ? "LZMA" : "no",
319 (unsigned long long)stage->entry,
320 (unsigned long long)stage->load,
321 stage->len,
322 stage->memlen);
323 break;
324 }
325 case CBFS_COMPONENT_PAYLOAD:
326 {
327 struct cbfs_payload_segment *payload = CBFS_SUBHEADER(thisfile);
328 while(payload) {
329 switch(payload->type) {
330 case PAYLOAD_SEGMENT_CODE:
331 case PAYLOAD_SEGMENT_DATA:
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800332 INFO(" %s (%s compression, offset: 0x%x, load: 0x%llx, length: %d/%d)\n",
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800333 payload->type == PAYLOAD_SEGMENT_CODE ? "code " : "data" ,
334 payload->compression == CBFS_COMPRESS_LZMA ? "LZMA" : "no",
335 ntohl(payload->offset),
336 (unsigned long long)ntohll(payload->load_addr),
337 ntohl(payload->len), ntohl(payload->mem_len));
338 break;
339 case PAYLOAD_SEGMENT_ENTRY:
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800340 INFO(" entry (0x%llx)\n", (unsigned long long)ntohll(payload->load_addr));
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800341 break;
342 case PAYLOAD_SEGMENT_BSS:
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800343 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 -0800344 break;
345 case PAYLOAD_SEGMENT_PARAMS:
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800346 INFO(" parameters\n");
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800347 break;
348 default:
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800349 INFO(" %x (%s compression, offset: 0x%x, load: 0x%llx, length: %d/%d\n",
Stefan Reinauerdb5b8932013-01-18 15:53:22 -0800350 payload->type,
351 payload->compression == CBFS_COMPRESS_LZMA ? "LZMA" : "no",
352 ntohl(payload->offset),
353 (unsigned long long)ntohll(payload->load_addr),
354 ntohl(payload->len),
355 ntohl(payload->mem_len));
356 break;
357 }
358
359 if(payload->type == PAYLOAD_SEGMENT_ENTRY)
360 payload=NULL;
361 else
362 payload++;
363 }
364 break;
365 }
366 default:
367 break;
368 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000369 current =
370 ALIGN(current + ntohl(thisfile->len) +
371 ntohl(thisfile->offset), align);
372 }
373}
374
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000375int extract_file_from_cbfs(const char *filename, const char *payloadname, const char *outpath)
376{
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000377 FILE *outfile = NULL;
378 uint32_t current = phys_start;
379 while (current < phys_end) {
380 if (!cbfs_file_header(current)) {
381 current += align;
382 continue;
383 }
384
385 // Locate the file start struct
386 struct cbfs_file *thisfile =
387 (struct cbfs_file *)phys_to_virt(current);
388 // And its length
389 uint32_t length = ntohl(thisfile->len);
390 // Locate the file name
391 char *fname = (char *)(phys_to_virt(current) + sizeof(struct cbfs_file));
Stefan Reinauera1e48242011-10-21 14:24:57 -0700392
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000393 // It's not the file we are looking for..
394 if (strcmp(fname, payloadname) != 0)
395 {
396 current =
397 ALIGN(current + ntohl(thisfile->len) +
398 ntohl(thisfile->offset), align);
399 continue;
400 }
401
402 // Else, it's our file.
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800403 LOG("Found file %.30s at 0x%x, type %.12s, size %d\n", fname,
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000404 current - phys_start, strfiletype(ntohl(thisfile->type)),
405 length);
406
407 // If we are not dumping to stdout, open the out file.
408 outfile = fopen(outpath, "wb");
409 if (!outfile)
410 {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800411 ERROR("Could not open the file %s for writing.\n", outpath);
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000412 return 1;
413 }
414
415 if (ntohl(thisfile->type) != CBFS_COMPONENT_RAW)
416 {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800417 WARN("Only 'raw' files are safe to extract.\n");
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000418 }
419
420 fwrite(((char *)thisfile)
421 + ntohl(thisfile->offset), length, 1, outfile);
422
423 fclose(outfile);
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800424 LOG("Successfully dumped the file.\n");
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000425
426 // We'll only dump one file.
427 return 0;
428 }
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800429 ERROR("File %s not found.\n", payloadname);
Stefan Reinauera1e48242011-10-21 14:24:57 -0700430 return 1;
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000431}
432
433
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000434int add_file_to_cbfs(void *content, uint32_t contentsize, uint32_t location)
435{
436 uint32_t current = phys_start;
437 while (current < phys_end) {
438 if (!cbfs_file_header(current)) {
439 current += align;
440 continue;
441 }
442 struct cbfs_file *thisfile =
443 (struct cbfs_file *)phys_to_virt(current);
444 uint32_t length = ntohl(thisfile->len);
445
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800446 DEBUG("at %x, %x bytes\n", current, length);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000447 /* Is this a free chunk? */
448 if ((thisfile->type == CBFS_COMPONENT_DELETED)
449 || (thisfile->type == CBFS_COMPONENT_NULL)) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800450 DEBUG("null||deleted at %x, %x bytes\n", current,
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000451 length);
452 /* if this is the right size, and if specified, the right location, use it */
453 if ((contentsize <= length)
454 && ((location == 0) || (current == location))) {
455 if (contentsize < length) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800456 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 +0000457 length, contentsize,
458 ALIGN(current + contentsize,
459 align),
460 length - contentsize);
461 uint32_t start =
462 ALIGN(current + contentsize, align);
463 uint32_t size =
464 current + ntohl(thisfile->offset)
465 + length - start - 16 -
466 sizeof(struct cbfs_file);
467 cbfs_create_empty_file(start, size);
468 }
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800469 DEBUG("copying data\n");
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000470 memcpy(phys_to_virt(current), content,
471 contentsize);
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000472 return 0;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000473 }
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000474 if (location != 0) {
475 /* CBFS has the constraint that the chain always moves up in memory. so once
476 we're past the place we seek, we don't need to look any further */
477 if (current > location) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800478 ERROR("The requested space is not available\n");
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000479 return 1;
480 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000481
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000482 /* Is the requested location inside the current chunk? */
483 if ((current < location)
484 && ((location + contentsize) <=
485 (current + length))) {
486 /* Split it up. In the next iteration the code will be at the right place. */
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800487 DEBUG("split up. new length: %x\n",
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000488 location - current -
489 ntohl(thisfile->offset));
490 thisfile->len =
491 htonl(location - current -
492 ntohl(thisfile->offset));
Stefan Reinauera1e48242011-10-21 14:24:57 -0700493 cbfs_create_empty_file(location,
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000494 length -
495 (location -
496 current));
497 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000498 }
499 }
500 current =
501 ALIGN(current + ntohl(thisfile->len) +
502 ntohl(thisfile->offset), align);
503 }
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800504 ERROR("Could not add the file to CBFS, it's probably too big.\n");
505 ERROR("File size: %d bytes (%d KB).\n", contentsize, contentsize/1024);
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000506 return 1;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000507}
508
Gabe Blacke1bb49e2012-01-27 00:33:47 -0800509
510static struct cbfs_file *merge_adjacent_files(struct cbfs_file *first,
511 struct cbfs_file *second)
512{
513 uint32_t new_length =
514 ntohl(first->len) + ntohl(second->len) + ntohl(second->offset);
515 first->len = htonl(new_length);
516 first->checksum = 0; // FIXME?
517 return first;
518}
519
520static struct cbfs_file *next_file(struct cbfs_file *prev)
521{
522 uint32_t pos = (prev == NULL) ? phys_start :
523 ALIGN(virt_to_phys(prev) + ntohl(prev->len) + ntohl(prev->offset),
524 align);
525
526 for (; pos < phys_end; pos += align) {
527 if (cbfs_file_header(pos))
528 return (struct cbfs_file *)phys_to_virt(pos);
529 }
530 return NULL;
531}
532
533
534int remove_file_from_cbfs(const char *filename)
535{
536 struct cbfs_file *prev = NULL;
537 struct cbfs_file *cur = next_file(prev);
538 struct cbfs_file *next = next_file(cur);
539 for (; cur; prev = cur, cur = next, next = next_file(next)) {
540
541 /* Check if this is the file to remove. */
542 char *name = (char *)cur + sizeof(*cur);
543 if (strcmp(name, filename))
544 continue;
545
546 /* Mark the file as free space and erase its name. */
547 cur->type = CBFS_COMPONENT_NULL;
548 name[0] = '\0';
549
550 /* Merge it with the previous file if possible. */
551 if (prev && prev->type == CBFS_COMPONENT_NULL)
552 cur = merge_adjacent_files(prev, cur);
553
554 /* Merge it with the next file if possible. */
555 if (next && next->type == CBFS_COMPONENT_NULL)
556 merge_adjacent_files(cur, next);
557
558 return 0;
559 }
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800560 ERROR("CBFS file %s not found.\n", filename);
Gabe Blacke1bb49e2012-01-27 00:33:47 -0800561 return 1;
562}
563
564
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000565/* returns new data block with cbfs_file header, suitable to dump into the ROM. location returns
566 the new location that points to the cbfs_file header */
567void *create_cbfs_file(const char *filename, void *data, uint32_t * datasize,
568 uint32_t type, uint32_t * location)
569{
570 uint32_t filename_len = ALIGN(strlen(filename) + 1, 16);
571 uint32_t headersize = sizeof(struct cbfs_file) + filename_len;
572 if ((location != 0) && (*location != 0)) {
573 uint32_t offset = *location % align;
574 /* If offset >= (headersize % align), we can stuff the header into the offset.
575 Otherwise the header has to be aligned itself, and put before the offset data */
576 if (offset >= (headersize % align)) {
577 offset -= (headersize % align);
578 } else {
579 offset += align - (headersize % align);
580 }
581 headersize += offset;
582 *location -= headersize;
583 }
584 void *newdata = malloc(*datasize + headersize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000585 if (!newdata) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800586 ERROR("Could not get %d bytes for CBFS file.\n", *datasize +
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000587 headersize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000588 exit(1);
589 }
Peter Stugef4aca1d2009-12-06 12:14:39 +0000590 memset(newdata, 0xff, *datasize + headersize);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000591 struct cbfs_file *nextfile = (struct cbfs_file *)newdata;
Stefan Reinauera1e48242011-10-21 14:24:57 -0700592 strncpy((char *)(nextfile->magic), "LARCHIVE", 8);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000593 nextfile->len = htonl(*datasize);
594 nextfile->type = htonl(type);
595 nextfile->checksum = 0; // FIXME?
596 nextfile->offset = htonl(headersize);
597 strcpy(newdata + sizeof(struct cbfs_file), filename);
598 memcpy(newdata + headersize, data, *datasize);
599 *datasize += headersize;
600 return newdata;
601}
602
603int create_cbfs_image(const char *romfile, uint32_t _romsize,
David Hendricks90ca3b62012-11-16 14:48:22 -0800604 const char *bootblock, uint32_t align, uint32_t offs)
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000605{
Stefan Reinauer63217582012-10-29 16:52:36 -0700606 uint32_t bootblocksize = 0;
607 struct cbfs_header *master_header;
608 unsigned char *romarea, *bootblk;
609
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000610 romsize = _romsize;
Stefan Reinauer63217582012-10-29 16:52:36 -0700611 romarea = malloc(romsize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000612 if (!romarea) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800613 ERROR("Could not get %d bytes of memory"
Stefan Reinauer63217582012-10-29 16:52:36 -0700614 " for CBFS image.\n", romsize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000615 exit(1);
616 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000617 memset(romarea, 0xff, romsize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000618
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000619 if (align == 0)
620 align = 64;
621
Stefan Reinauer63217582012-10-29 16:52:36 -0700622 bootblk = loadfile(bootblock, &bootblocksize,
623 romarea + romsize, SEEK_END);
624 if (!bootblk) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800625 ERROR("Could not load bootblock %s.\n",
Stefan Reinauer63217582012-10-29 16:52:36 -0700626 bootblock);
627 free(romarea);
628 return 1;
629 }
630
David Hendricks90ca3b62012-11-16 14:48:22 -0800631 // TODO(hungte) Replace magic numbers by named constants.
632 switch (arch) {
633 case CBFS_ARCHITECTURE_ARMV7:
634 /* Set up physical/virtual mapping */
635 offset = romarea;
Stefan Reinauer853270a2009-09-22 15:55:01 +0000636
David Hendricks90ca3b62012-11-16 14:48:22 -0800637 /*
David Hendricks0b23d472013-01-14 20:58:50 -0800638 * The initial jump instruction and bootblock will be placed
639 * before and after the master header, respectively. The
640 * bootblock image must contain a blank, aligned region large
641 * enough for the master header to fit.
David Hendricks90ca3b62012-11-16 14:48:22 -0800642 *
David Hendricks0b23d472013-01-14 20:58:50 -0800643 * An anchor string must be left such that when cbfstool is run
644 * we can find it and insert the master header at the next
645 * aligned boundary.
David Hendricks90ca3b62012-11-16 14:48:22 -0800646 */
David Hendricks0b23d472013-01-14 20:58:50 -0800647 loadfile(bootblock, &bootblocksize, romarea + offs, SEEK_SET);
648
649 unsigned char *p = romarea + offs;
650 while (1) {
651 /* FIXME: assumes little endian... */
652 if (*(uint32_t *)p == 0xdeadbeef)
653 break;
654 if (p >= (romarea + _romsize)) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800655 ERROR("Could not determine CBFS "
656 "header location.\n");
David Hendricks0b23d472013-01-14 20:58:50 -0800657 return 1;
658 }
659 p += (sizeof(unsigned int));
660 }
661 unsigned int u = ALIGN((unsigned int)(p - romarea), align);
662 master_header = (struct cbfs_header *)(romarea + u);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000663
David Hendricks90ca3b62012-11-16 14:48:22 -0800664 master_header->magic = ntohl(CBFS_HEADER_MAGIC);
Hung-Te Lin086842a2013-01-04 12:33:03 +0800665 master_header->version = ntohl(CBFS_HEADER_VERSION);
David Hendricks90ca3b62012-11-16 14:48:22 -0800666 master_header->romsize = htonl(romsize);
667 master_header->bootblocksize = htonl(bootblocksize);
668 master_header->align = htonl(align);
669 master_header->offset = htonl(
670 ALIGN((0x40 + bootblocksize), align));
671 master_header->architecture = htonl(CBFS_ARCHITECTURE_ARMV7);
672
David Hendricks454856b2013-01-02 17:29:00 -0800673 ((uint32_t *) phys_to_virt(0x4 + offs))[0] =
David Hendricks90ca3b62012-11-16 14:48:22 -0800674 virt_to_phys(master_header);
675
676 recalculate_rom_geometry(romarea);
677
678 cbfs_create_empty_file(
David Hendricks454856b2013-01-02 17:29:00 -0800679 offs + ALIGN((0x40 + bootblocksize), align),
680 romsize - offs - sizeof(struct cbfs_file) -
681 ALIGN((bootblocksize + 0x40), align));
David Hendricks90ca3b62012-11-16 14:48:22 -0800682 break;
683
684 case CBFS_ARCHITECTURE_X86:
685 // Set up physical/virtual mapping
686 offset = romarea + romsize - 0x100000000ULL;
687
688 loadfile(bootblock, &bootblocksize, romarea + romsize,
689 SEEK_END);
690 master_header = (struct cbfs_header *)(romarea + romsize -
691 bootblocksize - sizeof(struct cbfs_header));
692
693 master_header->magic = ntohl(CBFS_HEADER_MAGIC);
Hung-Te Lin086842a2013-01-04 12:33:03 +0800694 master_header->version = ntohl(CBFS_HEADER_VERSION);
David Hendricks90ca3b62012-11-16 14:48:22 -0800695 master_header->romsize = htonl(romsize);
696 master_header->bootblocksize = htonl(bootblocksize);
697 master_header->align = htonl(align);
698 master_header->offset = htonl(offs);
699 master_header->architecture = htonl(CBFS_ARCHITECTURE_X86);
700
701 ((uint32_t *) phys_to_virt(CBFS_HEADPTR_ADDR_X86))[0] =
702 virt_to_phys(master_header);
703
704 recalculate_rom_geometry(romarea);
705
706 cbfs_create_empty_file((0 - romsize + offs) & 0xffffffff,
707 romsize - offs - bootblocksize -
708 sizeof(struct cbfs_header) -
709 sizeof(struct cbfs_file) - 16);
710 break;
711
712 default:
713 // Should not happen.
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800714 ERROR("You found a bug in cbfstool.\n");
David Hendricks90ca3b62012-11-16 14:48:22 -0800715 exit(1);
716 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000717
718 writerom(romfile, romarea, romsize);
Stefan Reinauer63217582012-10-29 16:52:36 -0700719 free(romarea);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000720 return 0;
721}
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000722
723static int in_segment(int addr, int size, int gran)
724{
725 return ((addr & ~(gran - 1)) == ((addr + size) & ~(gran - 1)));
726}
727
728uint32_t cbfs_find_location(const char *romfile, uint32_t filesize,
729 const char *filename, uint32_t alignment)
730{
Stefan Reinauer63217582012-10-29 16:52:36 -0700731 void *rom;
732 size_t filename_size, headersize, totalsize;
733 int ret = 0;
734 uint32_t current;
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000735
Stefan Reinauer63217582012-10-29 16:52:36 -0700736 rom = loadrom(romfile);
737 if (rom == NULL) {
Hung-Te Lin4d87d4e2013-01-28 14:39:43 +0800738 ERROR("Could not load ROM image '%s'.\n", romfile);
Stefan Reinauer63217582012-10-29 16:52:36 -0700739 return 0;
740 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000741
Stefan Reinauer63217582012-10-29 16:52:36 -0700742 filename_size = strlen(filename);
743 headersize = sizeof(struct cbfs_file) + ALIGN(filename_size + 1, 16) +
744 sizeof(struct cbfs_stage);
745 totalsize = headersize + filesize;
746
747 current = phys_start;
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000748 while (current < phys_end) {
Stefan Reinauer63217582012-10-29 16:52:36 -0700749 uint32_t top;
750 struct cbfs_file *thisfile;
751
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000752 if (!cbfs_file_header(current)) {
753 current += align;
754 continue;
755 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000756
Stefan Reinauer63217582012-10-29 16:52:36 -0700757 thisfile = (struct cbfs_file *)phys_to_virt(current);
758
759 top = current + ntohl(thisfile->len) + ntohl(thisfile->offset);
760
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000761 if (((ntohl(thisfile->type) == 0x0)
762 || (ntohl(thisfile->type) == 0xffffffff))
763 && (ntohl(thisfile->len) + ntohl(thisfile->offset) >=
764 totalsize)) {
765 if (in_segment
Stefan Reinauer63217582012-10-29 16:52:36 -0700766 (current + headersize, filesize, alignment)) {
767 ret = current + headersize;
768 break;
769 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000770 if ((ALIGN(current, alignment) + filesize < top)
771 && (ALIGN(current, alignment) - headersize >
772 current)
773 && in_segment(ALIGN(current, alignment), filesize,
Stefan Reinauer63217582012-10-29 16:52:36 -0700774 alignment)) {
775 ret = ALIGN(current, alignment);
776 break;
777 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000778 if ((ALIGN(current, alignment) + alignment + filesize <
779 top)
780 && (ALIGN(current, alignment) + alignment -
781 headersize > current)
782 && in_segment(ALIGN(current, alignment) + alignment,
Stefan Reinauer63217582012-10-29 16:52:36 -0700783 filesize, alignment)) {
784 ret = ALIGN(current, alignment) + alignment;
785 break;
786 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000787 }
788 current =
789 ALIGN(current + ntohl(thisfile->len) +
790 ntohl(thisfile->offset), align);
791 }
Stefan Reinauer63217582012-10-29 16:52:36 -0700792
793 free(rom);
794 return ret;
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000795}