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