Home | History | Annotate | Download | only in futility
      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 */
     18 static 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 };
     35 BUILD_ASSERT(ARRAY_SIZE(cb_show_funcs) == NUM_CB_COMPONENTS);
     36 
     37 /* FUTIL_OP_SIGN */
     38 static 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 };
     55 BUILD_ASSERT(ARRAY_SIZE(cb_sign_funcs) == NUM_CB_COMPONENTS);
     56 
     57 static int (* const * const cb_func[])(struct futil_traverse_state_s *state) = {
     58 	cb_show_funcs,
     59 	cb_sign_funcs,
     60 };
     61 BUILD_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  */
     67 static 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 };
     84 BUILD_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  */
     90 struct 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. */
     96 static 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. */
    106 static 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 
    115 static 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 
    127 enum 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 
    139 static 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 };
    156 BUILD_ASSERT(ARRAY_SIZE(futil_cb_component_str) == NUM_CB_COMPONENTS);
    157 
    158 static 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 
    185 static 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 
    197 int 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