1/*
2 * Copyright 2014 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7#include <stdint.h>
8#include <stdio.h>
9
10#include "file_type.h"
11#include "fmap.h"
12#include "futility.h"
13#include "traversal.h"
14
15/* What functions do we invoke for a particular operation and component? */
16
17/* FUTIL_OP_SHOW */
18static int (* const cb_show_funcs[])(struct futil_traverse_state_s *state) = {
19	futil_cb_show_begin,		/* CB_BEGIN_TRAVERSAL */
20	NULL,				/* CB_END_TRAVERSAL */
21	futil_cb_show_gbb,		/* CB_FMAP_GBB */
22	futil_cb_show_fw_preamble,	/* CB_FMAP_VBLOCK_A */
23	futil_cb_show_fw_preamble,	/* CB_FMAP_VBLOCK_B */
24	futil_cb_show_fw_main,		/* CB_FMAP_FW_MAIN_A */
25	futil_cb_show_fw_main,		/* CB_FMAP_FW_MAIN_B */
26	futil_cb_show_pubkey,		/* CB_PUBKEY */
27	futil_cb_show_keyblock,		/* CB_KEYBLOCK */
28	futil_cb_show_gbb,		/* CB_GBB */
29	futil_cb_show_fw_preamble,	/* CB_FW_PREAMBLE */
30	futil_cb_show_kernel_preamble,	/* CB_KERN_PREAMBLE */
31	NULL,				/* CB_RAW_FIRMWARE */
32	NULL,				/* CB_RAW_KERNEL */
33	futil_cb_show_privkey,		/* CB_PRIVKEY */
34};
35BUILD_ASSERT(ARRAY_SIZE(cb_show_funcs) == NUM_CB_COMPONENTS);
36
37/* FUTIL_OP_SIGN */
38static int (* const cb_sign_funcs[])(struct futil_traverse_state_s *state) = {
39	futil_cb_sign_begin,		/* CB_BEGIN_TRAVERSAL */
40	futil_cb_sign_end,		/* CB_END_TRAVERSAL */
41	NULL,				/* CB_FMAP_GBB */
42	futil_cb_sign_fw_vblock,	/* CB_FMAP_VBLOCK_A */
43	futil_cb_sign_fw_vblock,	/* CB_FMAP_VBLOCK_B */
44	futil_cb_sign_fw_main,		/* CB_FMAP_FW_MAIN_A */
45	futil_cb_sign_fw_main,		/* CB_FMAP_FW_MAIN_B */
46	futil_cb_sign_pubkey,		/* CB_PUBKEY */
47	NULL,				/* CB_KEYBLOCK */
48	NULL,				/* CB_GBB */
49	NULL,				/* CB_FW_PREAMBLE */
50	futil_cb_resign_kernel_part,	/* CB_KERN_PREAMBLE */
51	futil_cb_sign_raw_firmware,	/* CB_RAW_FIRMWARE */
52	futil_cb_create_kernel_part,	/* CB_RAW_KERNEL */
53	NULL,				/* CB_PRIVKEY */
54};
55BUILD_ASSERT(ARRAY_SIZE(cb_sign_funcs) == NUM_CB_COMPONENTS);
56
57static int (* const * const cb_func[])(struct futil_traverse_state_s *state) = {
58	cb_show_funcs,
59	cb_sign_funcs,
60};
61BUILD_ASSERT(ARRAY_SIZE(cb_func) == NUM_FUTIL_OPS);
62
63/*
64 * File types that don't need iterating can use a lookup table to determine the
65 * callback component and name. The index is the file type.
66 */
67static const struct {
68	enum futil_cb_component component;
69	const char * const name;
70} direct_callback[] = {
71	{0,                NULL},		/* FILE_TYPE_UNKNOWN */
72	{CB_PUBKEY,        "VbPublicKey"},	/* FILE_TYPE_PUBKEY */
73	{CB_KEYBLOCK,      "VbKeyBlock"},	/* FILE_TYPE_KEYBLOCK */
74	{CB_FW_PREAMBLE,   "FW Preamble"},	/* FILE_TYPE_FW_PREAMBLE */
75	{CB_GBB,           "GBB"},		/* FILE_TYPE_GBB */
76	{0,                NULL},		/* FILE_TYPE_BIOS_IMAGE */
77	{0,                NULL},		/* FILE_TYPE_OLD_BIOS_IMAGE */
78	{CB_KERN_PREAMBLE, "Kernel Preamble"},	/* FILE_TYPE_KERN_PREAMBLE */
79	{CB_RAW_FIRMWARE,  "raw firmware"},	/* FILE_TYPE_RAW_FIRMWARE */
80	{CB_RAW_KERNEL,    "raw kernel"},	/* FILE_TYPE_RAW_KERNEL */
81	{0,                "chromiumos disk"},	/* FILE_TYPE_CHROMIUMOS_DISK */
82	{CB_PRIVKEY,       "VbPrivateKey"},	/* FILE_TYPE_PRIVKEY */
83};
84BUILD_ASSERT(ARRAY_SIZE(direct_callback) == NUM_FILE_TYPES);
85
86/*
87 * The Chrome OS BIOS must contain specific FMAP areas, and we generally want
88 * to look at each one in a certain order.
89 */
90struct bios_area_s {
91	const char * const name;
92	enum futil_cb_component component;
93};
94
95/* This are the expected areas, in order of traversal. */
96static const struct bios_area_s bios_area[] = {
97	{"GBB",       CB_FMAP_GBB},
98	{"FW_MAIN_A", CB_FMAP_FW_MAIN_A},
99	{"FW_MAIN_B", CB_FMAP_FW_MAIN_B},
100	{"VBLOCK_A",  CB_FMAP_VBLOCK_A},
101	{"VBLOCK_B",  CB_FMAP_VBLOCK_B},
102	{0, 0}
103};
104
105/* Really old BIOS images had different names, but worked the same. */
106static const struct bios_area_s old_bios_area[] = {
107	{"GBB Area",        CB_FMAP_GBB},
108	{"Firmware A Data", CB_FMAP_FW_MAIN_A},
109	{"Firmware B Data", CB_FMAP_FW_MAIN_B},
110	{"Firmware A Key",  CB_FMAP_VBLOCK_A},
111	{"Firmware B Key",  CB_FMAP_VBLOCK_B},
112	{0, 0}
113};
114
115static int has_all_areas(uint8_t *buf, uint32_t len, FmapHeader *fmap,
116			 const struct bios_area_s *area)
117{
118	/* We must have all the expected areas */
119	for (; area->name; area++)
120		if (!fmap_find_by_name(buf, len, fmap, area->name, 0))
121			return 0;
122
123	/* Found 'em all */
124	return 1;
125}
126
127enum futil_file_type recognize_bios_image(uint8_t *buf, uint32_t len)
128{
129	FmapHeader *fmap = fmap_find(buf, len);
130	if (fmap) {
131		if (has_all_areas(buf, len, fmap, bios_area))
132			return FILE_TYPE_BIOS_IMAGE;
133		if (has_all_areas(buf, len, fmap, old_bios_area))
134			return FILE_TYPE_OLD_BIOS_IMAGE;
135	}
136	return FILE_TYPE_UNKNOWN;
137}
138
139static const char * const futil_cb_component_str[] = {
140	"CB_BEGIN_TRAVERSAL",
141	"CB_END_TRAVERSAL",
142	"CB_FMAP_GBB",
143	"CB_FMAP_VBLOCK_A",
144	"CB_FMAP_VBLOCK_B",
145	"CB_FMAP_FW_MAIN_A",
146	"CB_FMAP_FW_MAIN_B",
147	"CB_PUBKEY",
148	"CB_KEYBLOCK",
149	"CB_GBB",
150	"CB_FW_PREAMBLE",
151	"CB_KERN_PREAMBLE",
152	"CB_RAW_FIRMWARE",
153	"CB_RAW_KERNEL",
154	"CB_PRIVKEY",
155};
156BUILD_ASSERT(ARRAY_SIZE(futil_cb_component_str) == NUM_CB_COMPONENTS);
157
158static int invoke_callback(struct futil_traverse_state_s *state,
159			   enum futil_cb_component c, const char *name,
160			   uint32_t offset, uint8_t *buf, uint32_t len)
161{
162	Debug("%s: name \"%s\" op %d component %s"
163	      " offset=0x%08x len=0x%08x, buf=%p\n",
164	      __func__, name, state->op, futil_cb_component_str[c],
165	      offset, len, buf);
166
167	if ((int) c < 0 || c >= NUM_CB_COMPONENTS) {
168		fprintf(stderr, "Invalid component %d\n", c);
169		return 1;
170	}
171
172	state->component = c;
173	state->name = name;
174	state->cb_area[c].offset = offset;
175	state->cb_area[c].buf = buf;
176	state->cb_area[c].len = len;
177	state->my_area = &state->cb_area[c];
178
179	if (cb_func[state->op][c])
180		return cb_func[state->op][c](state);
181
182	return 0;
183}
184
185static void fmap_limit_area(FmapAreaHeader *ah, uint32_t len)
186{
187	uint32_t sum = ah->area_offset + ah->area_size;
188	if (sum < ah->area_size || sum > len) {
189		Debug("%s(%s) 0x%x + 0x%x > 0x%x\n",
190		      __func__, ah->area_name,
191		      ah->area_offset, ah->area_size, len);
192		ah->area_offset = 0;
193		ah->area_size = 0;
194	}
195}
196
197int futil_traverse(uint8_t *buf, uint32_t len,
198		   struct futil_traverse_state_s *state,
199		   enum futil_file_type type)
200{
201	FmapHeader *fmap;
202	FmapAreaHeader *ah = 0;
203	const struct bios_area_s *area;
204	int retval = 0;
205
206	if ((int) state->op < 0 || state->op >= NUM_FUTIL_OPS) {
207		fprintf(stderr, "Invalid op %d\n", state->op);
208		return 1;
209	}
210
211	if (type == FILE_TYPE_UNKNOWN)
212		type = futil_file_type_buf(buf, len);
213	state->in_type = type;
214
215	state->errors = retval;
216	retval |= invoke_callback(state, CB_BEGIN_TRAVERSAL, "<begin>",
217				  0, buf, len);
218	state->errors = retval;
219
220	switch (type) {
221	case FILE_TYPE_BIOS_IMAGE:
222		/* We've already checked, so we know this will work. */
223		fmap = fmap_find(buf, len);
224		for (area = bios_area; area->name; area++) {
225			/* We know this will work, too */
226			fmap_find_by_name(buf, len, fmap, area->name, &ah);
227			/* But the file might be truncated */
228			fmap_limit_area(ah, len);
229			retval |= invoke_callback(state,
230						  area->component,
231						  area->name,
232						  ah->area_offset,
233						  buf + ah->area_offset,
234						  ah->area_size);
235			state->errors = retval;
236		}
237		break;
238
239	case FILE_TYPE_OLD_BIOS_IMAGE:
240		/* We've already checked, so we know this will work. */
241		fmap = fmap_find(buf, len);
242		for (area = old_bios_area; area->name; area++) {
243			/* We know this will work, too */
244			fmap_find_by_name(buf, len, fmap, area->name, &ah);
245			/* But the file might be truncated */
246			fmap_limit_area(ah, len);
247			retval |= invoke_callback(state,
248						  area->component,
249						  area->name,
250						  ah->area_offset,
251						  buf + ah->area_offset,
252						  ah->area_size);
253			state->errors = retval;
254		}
255		break;
256
257	case FILE_TYPE_UNKNOWN:
258	case FILE_TYPE_CHROMIUMOS_DISK:
259		/* Nothing to do for these file types (yet) */
260		break;
261
262	default:
263		/* All other file types have their own callbacks */
264		retval |= invoke_callback(state,
265					  direct_callback[type].component,
266					  direct_callback[type].name,
267					  0, buf, len);
268		state->errors = retval;
269		break;
270	}
271
272	retval |= invoke_callback(state, CB_END_TRAVERSAL, "<end>",
273				  0, buf, len);
274	return retval;
275}
276