blob: 9fed49424a5251f7334517f45f57608b89aa691c [file] [log] [blame]
Hung-Te Lineab2c812013-01-29 01:56:17 +08001/*
2 * CBFS Image Manipulation
3 *
4 * Copyright (C) 2013 The Chromium OS Authors. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
18 */
19
Hung-Te Lin3bb035b2013-01-29 02:15:49 +080020#include <inttypes.h>
21#include <libgen.h>
Hung-Te Lineab2c812013-01-29 01:56:17 +080022#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25
26#include "common.h"
27#include "cbfs_image.h"
28
29/* The file name align is not defined in CBFS spec -- only a preference by
30 * (old) cbfstool. */
31#define CBFS_FILENAME_ALIGN (16)
32
33/* To make CBFS more friendly to ROM, fill -1 (0xFF) instead of zero. */
34#define CBFS_CONTENT_DEFAULT_VALUE (-1)
35
36static uint32_t align_up(uint32_t value, uint32_t align) {
37 if (value % align)
38 value += align - (value % align);
39 return value;
40}
41
Hung-Te Lin3bb035b2013-01-29 02:15:49 +080042/* Type and format */
43
44struct typedesc_t {
45 uint32_t type;
46 const char *name;
47};
48
49static struct typedesc_t types_cbfs_entry[] = {
50 {CBFS_COMPONENT_STAGE, "stage"},
51 {CBFS_COMPONENT_PAYLOAD, "payload"},
52 {CBFS_COMPONENT_OPTIONROM, "optionrom"},
53 {CBFS_COMPONENT_BOOTSPLASH, "bootsplash"},
54 {CBFS_COMPONENT_RAW, "raw"},
55 {CBFS_COMPONENT_VSA, "vsa"},
56 {CBFS_COMPONENT_MBI, "mbi"},
57 {CBFS_COMPONENT_MICROCODE, "microcode"},
58 {CBFS_COMPONENT_CMOS_DEFAULT, "cmos_default"},
59 {CBFS_COMPONENT_CMOS_LAYOUT, "cmos_layout"},
60 {CBFS_COMPONENT_DELETED, "deleted"},
61 {CBFS_COMPONENT_NULL, "null"},
62 {0, NULL},
63};
64
65static struct typedesc_t types_cbfs_compression[] = {
66 {CBFS_COMPRESS_NONE, "none"},
67 {CBFS_COMPRESS_LZMA, "LZMA"},
68 {0, NULL},
69};
70
71uint32_t lookup_type_by_name(struct typedesc_t *desc, const char *name,
72 uint32_t default_value) {
73 int i;
74 for (i = 0; desc[i].name; i++)
75 if (strcmp(desc[i].name, name) == 0)
76 return desc[i].type;
77 return default_value;
78}
79
80const char *lookup_name_by_type(struct typedesc_t *desc, uint32_t type,
81 const char *default_value) {
82 int i;
83 for (i = 0; desc[i].name; i++)
84 if (desc[i].type == type)
85 return desc[i].name;
86 return default_value;
87}
88
89uint32_t get_cbfs_entry_type(const char *name, uint32_t default_value) {
90 return lookup_type_by_name(types_cbfs_entry, name, default_value);
91}
92
93const char *get_cbfs_entry_type_name(uint32_t type) {
94 return lookup_name_by_type(types_cbfs_entry, type, "(unknown)");
95}
96
97uint32_t get_cbfs_compression(const char *name, uint32_t unknown) {
98 return lookup_type_by_name(types_cbfs_compression, name, unknown);
99}
100
Hung-Te Lineab2c812013-01-29 01:56:17 +0800101int cbfs_image_from_file(struct cbfs_image *image, const char *filename) {
102 if (buffer_from_file(&image->buffer, filename) != 0)
103 return -1;
104 DEBUG("read_cbfs_image: %s (%zd bytes)\n", image->buffer.name,
105 image->buffer.size);
106 image->header = cbfs_find_header(image->buffer.data,
107 image->buffer.size);
108 if (!image->header) {
109 ERROR("%s does not have CBFS master header.\n", filename);
110 cbfs_image_delete(image);
111 return -1;
112 }
113
114 return 0;
115}
116
117int cbfs_image_write_file(struct cbfs_image *image, const char *filename) {
118 assert(image && image->buffer.data);
119 return buffer_write_file(&image->buffer, filename);
120}
121
122int cbfs_image_delete(struct cbfs_image *image) {
123 buffer_delete(&image->buffer);
124 image->header = NULL;
125 return 0;
126}
127
Hung-Te Lin3bb035b2013-01-29 02:15:49 +0800128int cbfs_print_header_info(struct cbfs_image *image) {
129 char *name = strdup(image->buffer.name);
130 assert(image && image->header);
131 printf("%s: %zd kB, bootblocksize %d, romsize %d, offset 0x%x\n"
132 "alignment: %d bytes\n\n",
133 basename(name),
134 image->buffer.size / 1024,
135 ntohl(image->header->bootblocksize),
136 ntohl(image->header->romsize),
137 ntohl(image->header->offset),
138 ntohl(image->header->align));
139 free(name);
140 return 0;
141}
142
143static int cbfs_print_stage_info(struct cbfs_stage *stage, FILE* fp) {
144 fprintf(fp,
145 " %s compression, entry: 0x%" PRIx64 ", load: 0x%" PRIx64 ", "
146 "length: %d/%d\n",
147 lookup_name_by_type(types_cbfs_compression,
148 stage->compression, "(unknown)"),
149 stage->entry,
150 stage->load,
151 stage->len,
152 stage->memlen);
153 return 0;
154}
155
156static int cbfs_print_payload_segment_info(struct cbfs_payload_segment *payload,
157 FILE *fp)
158{
159 switch(payload->type) {
160 case PAYLOAD_SEGMENT_CODE:
161 case PAYLOAD_SEGMENT_DATA:
162 fprintf(fp, " %s (%s compression, offset: 0x%x, "
163 "load: 0x%" PRIx64 ", length: %d/%d)\n",
164 (payload->type == PAYLOAD_SEGMENT_CODE ?
165 "code " : "data"),
166 lookup_name_by_type(types_cbfs_compression,
167 ntohl(payload->compression),
168 "(unknown)"),
169 ntohl(payload->offset),
170 ntohll(payload->load_addr),
171 ntohl(payload->len), ntohl(payload->mem_len));
172 break;
173
174 case PAYLOAD_SEGMENT_ENTRY:
175 fprintf(fp, " entry (0x%" PRIx64 ")\n",
176 ntohll(payload->load_addr));
177 break;
178
179 case PAYLOAD_SEGMENT_BSS:
180 fprintf(fp, " BSS (address 0x%016" PRIx64 ", "
181 "length 0x%x)\n",
182 ntohll(payload->load_addr),
183 ntohl(payload->len));
184 break;
185
186 case PAYLOAD_SEGMENT_PARAMS:
187 fprintf(fp, " parameters\n");
188 break;
189
190 default:
191 fprintf(fp, " 0x%x (%s compression, offset: 0x%x, "
192 "load: 0x%" PRIx64 ", length: %d/%d\n",
193 payload->type,
194 lookup_name_by_type(types_cbfs_compression,
195 payload->compression,
196 "(unknown)"),
197 ntohl(payload->offset),
198 ntohll(payload->load_addr),
199 ntohl(payload->len),
200 ntohl(payload->mem_len));
201 break;
202 }
203 return 0;
204}
205
206int cbfs_print_entry_info(struct cbfs_image *image, struct cbfs_file *entry,
207 void *arg) {
208 const char *name = CBFS_NAME(entry);
209 struct cbfs_payload_segment *payload;
210 FILE *fp = (FILE *)arg;
211
212 if (!cbfs_is_valid_entry(entry)) {
213 ERROR("cbfs_print_entry_info: Invalid entry at 0x%x\n",
214 cbfs_get_entry_addr(image, entry));
215 return -1;
216 }
217 if (!fp)
218 fp = stdout;
219
220 fprintf(fp, "%-30s 0x%-8x %-12s %d\n",
221 *name ? name : "(empty)",
222 cbfs_get_entry_addr(image, entry),
223 get_cbfs_entry_type_name(ntohl(entry->type)),
224 ntohl(entry->len));
225
226 if (!verbose)
227 return 0;
228
229 DEBUG(" cbfs_file=0x%x, offset=0x%x, content_address=0x%x+0x%x\n",
230 cbfs_get_entry_addr(image, entry), ntohl(entry->offset),
231 cbfs_get_entry_addr(image, entry) + ntohl(entry->offset),
232 ntohl(entry->len));
233
234 /* note the components of the subheader may be in host order ... */
235 switch (ntohl(entry->type)) {
236 case CBFS_COMPONENT_STAGE:
237 cbfs_print_stage_info((struct cbfs_stage *)
238 CBFS_SUBHEADER(entry), fp);
239 break;
240
241 case CBFS_COMPONENT_PAYLOAD:
242 payload = (struct cbfs_payload_segment *)
243 CBFS_SUBHEADER(entry);
244 while (payload) {
245 cbfs_print_payload_segment_info(payload, fp);
246 if (payload->type == PAYLOAD_SEGMENT_ENTRY)
247 break;
248 else
249 payload ++;
250 }
251 break;
252 default:
253 break;
254 }
255 return 0;
256}
257
258int cbfs_print_directory(struct cbfs_image *image) {
259 cbfs_print_header_info(image);
260 printf("%-30s %-10s %-12s Size\n", "Name", "Offset", "Type");
261 cbfs_walk(image, cbfs_print_entry_info, NULL);
262 return 0;
263}
264
265int cbfs_walk(struct cbfs_image *image, cbfs_entry_callback callback,
266 void *arg) {
267 int count = 0;
268 struct cbfs_file *entry;
269 for (entry = cbfs_find_first_entry(image);
270 entry && cbfs_is_valid_entry(entry);
271 entry = cbfs_find_next_entry(image, entry)) {
272 count ++;
273 if (callback(image, entry, arg) != 0)
274 break;
275 }
276 return count;
277}
278
Hung-Te Lineab2c812013-01-29 01:56:17 +0800279struct cbfs_header *cbfs_find_header(char *data, size_t size) {
280 size_t offset;
281 int found = 0;
282 uint32_t x86sig;
283 struct cbfs_header *header, *result = NULL;
284
285 // Try x86 style (check signature in bottom) header first.
286 x86sig = *(uint32_t *)(data + size - sizeof(uint32_t));
287 offset = (x86sig + (uint32_t)size);
288 DEBUG("x86sig: 0x%x, offset: 0x%zx\n", x86sig, offset);
289 if (offset >= size - sizeof(*header) ||
290 ntohl(((struct cbfs_header *)(data + offset))->magic) !=
291 CBFS_HEADER_MAGIC)
292 offset = 0;
293
294 for (; offset + sizeof(*header) < size; offset++) {
295 header = (struct cbfs_header *)(data + offset);
296 if (ntohl(header->magic) !=(CBFS_HEADER_MAGIC))
297 continue;
298 if (ntohl(header->version) != CBFS_HEADER_VERSION1 &&
299 ntohl(header->version) != CBFS_HEADER_VERSION2) {
300 // Probably not a real CBFS header?
301 continue;
302 }
303 found++;
304 result = header;
305 }
306 if (found > 1) {
307 ERROR("multiple (%d) CBFS headers found!\n",
308 found);
309 result = NULL;
310 }
311 return result;
312}
313
314
315struct cbfs_file *cbfs_find_first_entry(struct cbfs_image *image) {
316 assert(image && image->header);
317 return (struct cbfs_file *)(image->buffer.data +
318 ntohl(image->header->offset));
319}
320
321struct cbfs_file *cbfs_find_next_entry(struct cbfs_image *image,
322 struct cbfs_file *entry) {
323 uint32_t addr = cbfs_get_entry_addr(image, entry);
324 int align = ntohl(image->header->align);
325 assert(entry && cbfs_is_valid_entry(entry));
326 addr += ntohl(entry->offset) + ntohl(entry->len);
327 addr = align_up(addr, align);
328 return (struct cbfs_file *)(image->buffer.data + addr);
329}
330
331uint32_t cbfs_get_entry_addr(struct cbfs_image *image, struct cbfs_file *entry) {
332 assert(image && image->buffer.data && entry);
333 return (int32_t)((char *)entry - image->buffer.data);
334}
335
336int cbfs_is_valid_entry(struct cbfs_file *entry) {
337 return (entry &&memcmp(entry->magic, CBFS_FILE_MAGIC,
338 sizeof(entry->magic)) == 0);
339}
340