blob: befdc3f2ffab6411ff00acd209ab0ec33daefbc2 [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 */
146 if (master_header->version == VERSION1 &&
147 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
David Hendricks90ca3b62012-11-16 14:48:22 -0800277 ("%s: %d kB, bootblocksize %d, romsize %d, offset 0x%x\n"
Stefan Reinauer0a977592012-11-30 11:21:05 -0800278 "alignment: %d bytes, architecture: %s\n\n",
Uwe Hermann942a40d2010-02-10 19:52:35 +0000279 basename((char *)filename), romsize / 1024, ntohl(master_header->bootblocksize),
David Hendricks90ca3b62012-11-16 14:48:22 -0800280 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 =
289 (struct cbfs_file *)phys_to_virt(current);
290 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);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000298 current =
299 ALIGN(current + ntohl(thisfile->len) +
300 ntohl(thisfile->offset), align);
301 }
302}
303
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000304int extract_file_from_cbfs(const char *filename, const char *payloadname, const char *outpath)
305{
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000306 FILE *outfile = NULL;
307 uint32_t current = phys_start;
308 while (current < phys_end) {
309 if (!cbfs_file_header(current)) {
310 current += align;
311 continue;
312 }
313
314 // Locate the file start struct
315 struct cbfs_file *thisfile =
316 (struct cbfs_file *)phys_to_virt(current);
317 // And its length
318 uint32_t length = ntohl(thisfile->len);
319 // Locate the file name
320 char *fname = (char *)(phys_to_virt(current) + sizeof(struct cbfs_file));
Stefan Reinauera1e48242011-10-21 14:24:57 -0700321
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000322 // It's not the file we are looking for..
323 if (strcmp(fname, payloadname) != 0)
324 {
325 current =
326 ALIGN(current + ntohl(thisfile->len) +
327 ntohl(thisfile->offset), align);
328 continue;
329 }
330
331 // Else, it's our file.
Peter Stuge441426b2011-01-17 05:08:32 +0000332 printf("Found file %.30s at 0x%x, type %.12s, size %d\n", fname,
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000333 current - phys_start, strfiletype(ntohl(thisfile->type)),
334 length);
335
336 // If we are not dumping to stdout, open the out file.
337 outfile = fopen(outpath, "wb");
338 if (!outfile)
339 {
Stefan Reinauer0a977592012-11-30 11:21:05 -0800340 fprintf(stderr, "E: Could not open the file %s for writing.\n", outpath);
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000341 return 1;
342 }
343
344 if (ntohl(thisfile->type) != CBFS_COMPONENT_RAW)
345 {
Stefan Reinauer0a977592012-11-30 11:21:05 -0800346 fprintf(stderr, "W: Only 'raw' files are safe to extract.\n");
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000347 }
348
349 fwrite(((char *)thisfile)
350 + ntohl(thisfile->offset), length, 1, outfile);
351
352 fclose(outfile);
Peter Stuge441426b2011-01-17 05:08:32 +0000353 printf("Successfully dumped the file.\n");
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000354
355 // We'll only dump one file.
356 return 0;
357 }
Stefan Reinauer0a977592012-11-30 11:21:05 -0800358 fprintf(stderr, "E: File %s not found.\n", payloadname);
Stefan Reinauera1e48242011-10-21 14:24:57 -0700359 return 1;
Aurelien Guillaumefe7d6b92011-01-13 09:09:21 +0000360}
361
362
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000363int add_file_to_cbfs(void *content, uint32_t contentsize, uint32_t location)
364{
365 uint32_t current = phys_start;
366 while (current < phys_end) {
367 if (!cbfs_file_header(current)) {
368 current += align;
369 continue;
370 }
371 struct cbfs_file *thisfile =
372 (struct cbfs_file *)phys_to_virt(current);
373 uint32_t length = ntohl(thisfile->len);
374
375 dprintf("at %x, %x bytes\n", current, length);
376 /* Is this a free chunk? */
377 if ((thisfile->type == CBFS_COMPONENT_DELETED)
378 || (thisfile->type == CBFS_COMPONENT_NULL)) {
379 dprintf("null||deleted at %x, %x bytes\n", current,
380 length);
381 /* if this is the right size, and if specified, the right location, use it */
382 if ((contentsize <= length)
383 && ((location == 0) || (current == location))) {
384 if (contentsize < length) {
385 dprintf
386 ("this chunk is %x bytes, we need %x. create a new chunk at %x with %x bytes\n",
387 length, contentsize,
388 ALIGN(current + contentsize,
389 align),
390 length - contentsize);
391 uint32_t start =
392 ALIGN(current + contentsize, align);
393 uint32_t size =
394 current + ntohl(thisfile->offset)
395 + length - start - 16 -
396 sizeof(struct cbfs_file);
397 cbfs_create_empty_file(start, size);
398 }
399 dprintf("copying data\n");
400 memcpy(phys_to_virt(current), content,
401 contentsize);
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000402 return 0;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000403 }
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000404 if (location != 0) {
405 /* CBFS has the constraint that the chain always moves up in memory. so once
406 we're past the place we seek, we don't need to look any further */
407 if (current > location) {
Stefan Reinauer0a977592012-11-30 11:21:05 -0800408 fprintf
409 (stderr, "E: The requested space is not available\n");
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000410 return 1;
411 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000412
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000413 /* Is the requested location inside the current chunk? */
414 if ((current < location)
415 && ((location + contentsize) <=
416 (current + length))) {
417 /* Split it up. In the next iteration the code will be at the right place. */
418 dprintf("split up. new length: %x\n",
419 location - current -
420 ntohl(thisfile->offset));
421 thisfile->len =
422 htonl(location - current -
423 ntohl(thisfile->offset));
Stefan Reinauera1e48242011-10-21 14:24:57 -0700424 cbfs_create_empty_file(location,
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000425 length -
426 (location -
427 current));
428 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000429 }
430 }
431 current =
432 ALIGN(current + ntohl(thisfile->len) +
433 ntohl(thisfile->offset), align);
434 }
Stefan Reinauer0a977592012-11-30 11:21:05 -0800435 fprintf(stderr, "E: Could not add the file to CBFS, it's probably too big.\n");
436 fprintf(stderr, "E: File size: %d bytes (%d KB).\n", contentsize, contentsize/1024);
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000437 return 1;
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000438}
439
Gabe Blacke1bb49e2012-01-27 00:33:47 -0800440
441static struct cbfs_file *merge_adjacent_files(struct cbfs_file *first,
442 struct cbfs_file *second)
443{
444 uint32_t new_length =
445 ntohl(first->len) + ntohl(second->len) + ntohl(second->offset);
446 first->len = htonl(new_length);
447 first->checksum = 0; // FIXME?
448 return first;
449}
450
451static struct cbfs_file *next_file(struct cbfs_file *prev)
452{
453 uint32_t pos = (prev == NULL) ? phys_start :
454 ALIGN(virt_to_phys(prev) + ntohl(prev->len) + ntohl(prev->offset),
455 align);
456
457 for (; pos < phys_end; pos += align) {
458 if (cbfs_file_header(pos))
459 return (struct cbfs_file *)phys_to_virt(pos);
460 }
461 return NULL;
462}
463
464
465int remove_file_from_cbfs(const char *filename)
466{
467 struct cbfs_file *prev = NULL;
468 struct cbfs_file *cur = next_file(prev);
469 struct cbfs_file *next = next_file(cur);
470 for (; cur; prev = cur, cur = next, next = next_file(next)) {
471
472 /* Check if this is the file to remove. */
473 char *name = (char *)cur + sizeof(*cur);
474 if (strcmp(name, filename))
475 continue;
476
477 /* Mark the file as free space and erase its name. */
478 cur->type = CBFS_COMPONENT_NULL;
479 name[0] = '\0';
480
481 /* Merge it with the previous file if possible. */
482 if (prev && prev->type == CBFS_COMPONENT_NULL)
483 cur = merge_adjacent_files(prev, cur);
484
485 /* Merge it with the next file if possible. */
486 if (next && next->type == CBFS_COMPONENT_NULL)
487 merge_adjacent_files(cur, next);
488
489 return 0;
490 }
Stefan Reinauer0a977592012-11-30 11:21:05 -0800491 fprintf(stderr, "E: CBFS file %s not found.\n", filename);
Gabe Blacke1bb49e2012-01-27 00:33:47 -0800492 return 1;
493}
494
495
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000496/* returns new data block with cbfs_file header, suitable to dump into the ROM. location returns
497 the new location that points to the cbfs_file header */
498void *create_cbfs_file(const char *filename, void *data, uint32_t * datasize,
499 uint32_t type, uint32_t * location)
500{
501 uint32_t filename_len = ALIGN(strlen(filename) + 1, 16);
502 uint32_t headersize = sizeof(struct cbfs_file) + filename_len;
503 if ((location != 0) && (*location != 0)) {
504 uint32_t offset = *location % align;
505 /* If offset >= (headersize % align), we can stuff the header into the offset.
506 Otherwise the header has to be aligned itself, and put before the offset data */
507 if (offset >= (headersize % align)) {
508 offset -= (headersize % align);
509 } else {
510 offset += align - (headersize % align);
511 }
512 headersize += offset;
513 *location -= headersize;
514 }
515 void *newdata = malloc(*datasize + headersize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000516 if (!newdata) {
Stefan Reinauer0a977592012-11-30 11:21:05 -0800517 fprintf(stderr, "E: Could not get %d bytes for CBFS file.\n", *datasize +
Patrick Georgi56f5fb72009-09-30 11:21:18 +0000518 headersize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000519 exit(1);
520 }
Peter Stugef4aca1d2009-12-06 12:14:39 +0000521 memset(newdata, 0xff, *datasize + headersize);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000522 struct cbfs_file *nextfile = (struct cbfs_file *)newdata;
Stefan Reinauera1e48242011-10-21 14:24:57 -0700523 strncpy((char *)(nextfile->magic), "LARCHIVE", 8);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000524 nextfile->len = htonl(*datasize);
525 nextfile->type = htonl(type);
526 nextfile->checksum = 0; // FIXME?
527 nextfile->offset = htonl(headersize);
528 strcpy(newdata + sizeof(struct cbfs_file), filename);
529 memcpy(newdata + headersize, data, *datasize);
530 *datasize += headersize;
531 return newdata;
532}
533
534int create_cbfs_image(const char *romfile, uint32_t _romsize,
David Hendricks90ca3b62012-11-16 14:48:22 -0800535 const char *bootblock, uint32_t align, uint32_t offs)
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000536{
Stefan Reinauer63217582012-10-29 16:52:36 -0700537 uint32_t bootblocksize = 0;
538 struct cbfs_header *master_header;
539 unsigned char *romarea, *bootblk;
540
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000541 romsize = _romsize;
Stefan Reinauer63217582012-10-29 16:52:36 -0700542 romarea = malloc(romsize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000543 if (!romarea) {
Stefan Reinauer63217582012-10-29 16:52:36 -0700544 fprintf(stderr, "E: Could not get %d bytes of memory"
545 " for CBFS image.\n", romsize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000546 exit(1);
547 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000548 memset(romarea, 0xff, romsize);
Stefan Reinauer853270a2009-09-22 15:55:01 +0000549
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000550 if (align == 0)
551 align = 64;
552
Stefan Reinauer63217582012-10-29 16:52:36 -0700553 bootblk = loadfile(bootblock, &bootblocksize,
554 romarea + romsize, SEEK_END);
555 if (!bootblk) {
556 fprintf(stderr, "E: Could not load bootblock %s.\n",
557 bootblock);
558 free(romarea);
559 return 1;
560 }
561
David Hendricks90ca3b62012-11-16 14:48:22 -0800562 // TODO(hungte) Replace magic numbers by named constants.
563 switch (arch) {
564 case CBFS_ARCHITECTURE_ARMV7:
565 /* Set up physical/virtual mapping */
566 offset = romarea;
Stefan Reinauer853270a2009-09-22 15:55:01 +0000567
David Hendricks90ca3b62012-11-16 14:48:22 -0800568 // should be aligned to align but then we need to dynamically
569 // create the jump to the bootblock
David Hendricks454856b2013-01-02 17:29:00 -0800570 loadfile(bootblock, &bootblocksize, romarea + offs + 0x20 +
David Hendricks90ca3b62012-11-16 14:48:22 -0800571 sizeof(struct cbfs_header), SEEK_SET);
David Hendricks454856b2013-01-02 17:29:00 -0800572 master_header = (struct cbfs_header *)(romarea + offs + 0x20);
573 uint32_t *arm_vec = (uint32_t *)(romarea + offs);
David Hendricks90ca3b62012-11-16 14:48:22 -0800574 /*
575 * Encoding for this branch instruction is:
576 * 31:28 - condition (0xe for always/unconditional)
577 * 27:24 - 0xa
578 * 23: 0 - sign-extended offset (in multiples of 4)
579 *
580 * When executing the branch, the PC will read as the address
581 * of current instruction + 8.
582 */
Stefan Reinauer12246262012-12-08 23:10:08 +0100583 arm_vec[0] = htonl(0x0e0000ea); // branch to . + 64 bytes
Stefan Reinauer853270a2009-09-22 15:55:01 +0000584
David Hendricks90ca3b62012-11-16 14:48:22 -0800585 master_header->magic = ntohl(CBFS_HEADER_MAGIC);
586 master_header->version = ntohl(VERSION);
587 master_header->romsize = htonl(romsize);
588 master_header->bootblocksize = htonl(bootblocksize);
589 master_header->align = htonl(align);
590 master_header->offset = htonl(
591 ALIGN((0x40 + bootblocksize), align));
592 master_header->architecture = htonl(CBFS_ARCHITECTURE_ARMV7);
593
David Hendricks454856b2013-01-02 17:29:00 -0800594 ((uint32_t *) phys_to_virt(0x4 + offs))[0] =
David Hendricks90ca3b62012-11-16 14:48:22 -0800595 virt_to_phys(master_header);
596
597 recalculate_rom_geometry(romarea);
598
599 cbfs_create_empty_file(
David Hendricks454856b2013-01-02 17:29:00 -0800600 offs + ALIGN((0x40 + bootblocksize), align),
601 romsize - offs - sizeof(struct cbfs_file) -
602 ALIGN((bootblocksize + 0x40), align));
David Hendricks90ca3b62012-11-16 14:48:22 -0800603 break;
604
605 case CBFS_ARCHITECTURE_X86:
606 // Set up physical/virtual mapping
607 offset = romarea + romsize - 0x100000000ULL;
608
609 loadfile(bootblock, &bootblocksize, romarea + romsize,
610 SEEK_END);
611 master_header = (struct cbfs_header *)(romarea + romsize -
612 bootblocksize - sizeof(struct cbfs_header));
613
614 master_header->magic = ntohl(CBFS_HEADER_MAGIC);
615 master_header->version = ntohl(VERSION);
616 master_header->romsize = htonl(romsize);
617 master_header->bootblocksize = htonl(bootblocksize);
618 master_header->align = htonl(align);
619 master_header->offset = htonl(offs);
620 master_header->architecture = htonl(CBFS_ARCHITECTURE_X86);
621
622 ((uint32_t *) phys_to_virt(CBFS_HEADPTR_ADDR_X86))[0] =
623 virt_to_phys(master_header);
624
625 recalculate_rom_geometry(romarea);
626
627 cbfs_create_empty_file((0 - romsize + offs) & 0xffffffff,
628 romsize - offs - bootblocksize -
629 sizeof(struct cbfs_header) -
630 sizeof(struct cbfs_file) - 16);
631 break;
632
633 default:
634 // Should not happen.
635 fprintf(stderr, "E: You found a bug in cbfstool.\n");
636 exit(1);
637 }
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000638
639 writerom(romfile, romarea, romsize);
Stefan Reinauer63217582012-10-29 16:52:36 -0700640 free(romarea);
Patrick Georgib7b56dd82009-09-14 13:29:27 +0000641 return 0;
642}
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000643
644static int in_segment(int addr, int size, int gran)
645{
646 return ((addr & ~(gran - 1)) == ((addr + size) & ~(gran - 1)));
647}
648
649uint32_t cbfs_find_location(const char *romfile, uint32_t filesize,
650 const char *filename, uint32_t alignment)
651{
Stefan Reinauer63217582012-10-29 16:52:36 -0700652 void *rom;
653 size_t filename_size, headersize, totalsize;
654 int ret = 0;
655 uint32_t current;
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000656
Stefan Reinauer63217582012-10-29 16:52:36 -0700657 rom = loadrom(romfile);
658 if (rom == NULL) {
659 fprintf(stderr, "E: Could not load ROM image '%s'.\n",
660 romfile);
661 return 0;
662 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000663
Stefan Reinauer63217582012-10-29 16:52:36 -0700664 filename_size = strlen(filename);
665 headersize = sizeof(struct cbfs_file) + ALIGN(filename_size + 1, 16) +
666 sizeof(struct cbfs_stage);
667 totalsize = headersize + filesize;
668
669 current = phys_start;
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000670 while (current < phys_end) {
Stefan Reinauer63217582012-10-29 16:52:36 -0700671 uint32_t top;
672 struct cbfs_file *thisfile;
673
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000674 if (!cbfs_file_header(current)) {
675 current += align;
676 continue;
677 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000678
Stefan Reinauer63217582012-10-29 16:52:36 -0700679 thisfile = (struct cbfs_file *)phys_to_virt(current);
680
681 top = current + ntohl(thisfile->len) + ntohl(thisfile->offset);
682
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000683 if (((ntohl(thisfile->type) == 0x0)
684 || (ntohl(thisfile->type) == 0xffffffff))
685 && (ntohl(thisfile->len) + ntohl(thisfile->offset) >=
686 totalsize)) {
687 if (in_segment
Stefan Reinauer63217582012-10-29 16:52:36 -0700688 (current + headersize, filesize, alignment)) {
689 ret = current + headersize;
690 break;
691 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000692 if ((ALIGN(current, alignment) + filesize < top)
693 && (ALIGN(current, alignment) - headersize >
694 current)
695 && in_segment(ALIGN(current, alignment), filesize,
Stefan Reinauer63217582012-10-29 16:52:36 -0700696 alignment)) {
697 ret = ALIGN(current, alignment);
698 break;
699 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000700 if ((ALIGN(current, alignment) + alignment + filesize <
701 top)
702 && (ALIGN(current, alignment) + alignment -
703 headersize > current)
704 && in_segment(ALIGN(current, alignment) + alignment,
Stefan Reinauer63217582012-10-29 16:52:36 -0700705 filesize, alignment)) {
706 ret = ALIGN(current, alignment) + alignment;
707 break;
708 }
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000709 }
710 current =
711 ALIGN(current + ntohl(thisfile->len) +
712 ntohl(thisfile->offset), align);
713 }
Stefan Reinauer63217582012-10-29 16:52:36 -0700714
715 free(rom);
716 return ret;
Patrick Georgi0da38dd2009-11-09 17:18:02 +0000717}