Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 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 | |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 7 | #include <stdint.h> |
| 8 | #include <stdio.h> |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 9 | |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 10 | #include "file_type.h" |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 11 | #include "fmap.h" |
| 12 | #include "futility.h" |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 13 | #include "traversal.h" |
| 14 | |
| 15 | /* What functions do we invoke for a particular operation and component? */ |
| 16 | |
| 17 | /* FUTIL_OP_SHOW */ |
Bill Richardson | 6f72ffa | 2014-09-23 14:40:20 -0700 | [diff] [blame] | 18 | static int (* const cb_show_funcs[])(struct futil_traverse_state_s *state) = { |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 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 */ |
Bill Richardson | 6f72ffa | 2014-09-23 14:40:20 -0700 | [diff] [blame] | 24 | futil_cb_show_fw_main, /* CB_FMAP_FW_MAIN_A */ |
| 25 | futil_cb_show_fw_main, /* CB_FMAP_FW_MAIN_B */ |
Bill Richardson | 4805f18 | 2015-01-30 22:21:10 -0800 | [diff] [blame] | 26 | futil_cb_show_pubkey, /* CB_PUBKEY */ |
Bill Richardson | 6f72ffa | 2014-09-23 14:40:20 -0700 | [diff] [blame] | 27 | futil_cb_show_keyblock, /* CB_KEYBLOCK */ |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 28 | futil_cb_show_gbb, /* CB_GBB */ |
| 29 | futil_cb_show_fw_preamble, /* CB_FW_PREAMBLE */ |
Bill Richardson | 5f2696d | 2014-09-23 22:03:56 -0700 | [diff] [blame] | 30 | futil_cb_show_kernel_preamble, /* CB_KERN_PREAMBLE */ |
Bill Richardson | 6f72ffa | 2014-09-23 14:40:20 -0700 | [diff] [blame] | 31 | NULL, /* CB_RAW_FIRMWARE */ |
| 32 | NULL, /* CB_RAW_KERNEL */ |
Bill Richardson | 4805f18 | 2015-01-30 22:21:10 -0800 | [diff] [blame] | 33 | futil_cb_show_privkey, /* CB_PRIVKEY */ |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 34 | }; |
| 35 | BUILD_ASSERT(ARRAY_SIZE(cb_show_funcs) == NUM_CB_COMPONENTS); |
| 36 | |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 37 | /* FUTIL_OP_SIGN */ |
Bill Richardson | 6f72ffa | 2014-09-23 14:40:20 -0700 | [diff] [blame] | 38 | static int (* const cb_sign_funcs[])(struct futil_traverse_state_s *state) = { |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 39 | futil_cb_sign_begin, /* CB_BEGIN_TRAVERSAL */ |
| 40 | futil_cb_sign_end, /* CB_END_TRAVERSAL */ |
| 41 | NULL, /* CB_FMAP_GBB */ |
Bill Richardson | 5f2696d | 2014-09-23 22:03:56 -0700 | [diff] [blame] | 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 */ |
Bill Richardson | 4805f18 | 2015-01-30 22:21:10 -0800 | [diff] [blame] | 53 | NULL, /* CB_PRIVKEY */ |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 54 | }; |
| 55 | BUILD_ASSERT(ARRAY_SIZE(cb_sign_funcs) == NUM_CB_COMPONENTS); |
| 56 | |
Bill Richardson | 6f72ffa | 2014-09-23 14:40:20 -0700 | [diff] [blame] | 57 | static int (* const * const cb_func[])(struct futil_traverse_state_s *state) = { |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 58 | cb_show_funcs, |
Bill Richardson | 15dc6fc | 2014-09-02 14:45:44 -0700 | [diff] [blame] | 59 | cb_sign_funcs, |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 60 | }; |
| 61 | BUILD_ASSERT(ARRAY_SIZE(cb_func) == NUM_FUTIL_OPS); |
| 62 | |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 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 */ |
Bill Richardson | 6f72ffa | 2014-09-23 14:40:20 -0700 | [diff] [blame] | 74 | {CB_FW_PREAMBLE, "FW Preamble"}, /* FILE_TYPE_FW_PREAMBLE */ |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 75 | {CB_GBB, "GBB"}, /* FILE_TYPE_GBB */ |
| 76 | {0, NULL}, /* FILE_TYPE_BIOS_IMAGE */ |
| 77 | {0, NULL}, /* FILE_TYPE_OLD_BIOS_IMAGE */ |
Bill Richardson | 6f72ffa | 2014-09-23 14:40:20 -0700 | [diff] [blame] | 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 */ |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 81 | {0, "chromiumos disk"}, /* FILE_TYPE_CHROMIUMOS_DISK */ |
Bill Richardson | 4805f18 | 2015-01-30 22:21:10 -0800 | [diff] [blame] | 82 | {CB_PRIVKEY, "VbPrivateKey"}, /* FILE_TYPE_PRIVKEY */ |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 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 | { |
Bill Richardson | 6f72ffa | 2014-09-23 14:40:20 -0700 | [diff] [blame] | 118 | /* We must have all the expected areas */ |
| 119 | for (; area->name; area++) |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 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 | |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 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 | } |
Bill Richardson | 6f72ffa | 2014-09-23 14:40:20 -0700 | [diff] [blame] | 138 | |
Bill Richardson | 7ccd9ce | 2015-01-30 23:45:49 -0800 | [diff] [blame] | 139 | static const char * const futil_cb_component_str[] = { |
Bill Richardson | 6f72ffa | 2014-09-23 14:40:20 -0700 | [diff] [blame] | 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", |
Bill Richardson | 4805f18 | 2015-01-30 22:21:10 -0800 | [diff] [blame] | 154 | "CB_PRIVKEY", |
Bill Richardson | 6f72ffa | 2014-09-23 14:40:20 -0700 | [diff] [blame] | 155 | }; |
| 156 | BUILD_ASSERT(ARRAY_SIZE(futil_cb_component_str) == NUM_CB_COMPONENTS); |
| 157 | |
Bill Richardson | 6f72ffa | 2014-09-23 14:40:20 -0700 | [diff] [blame] | 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 | |
David Riley | 05987b1 | 2015-02-05 19:22:49 -0800 | [diff] [blame] | 167 | if ((int) c < 0 || c >= NUM_CB_COMPONENTS) { |
Bill Richardson | 6f72ffa | 2014-09-23 14:40:20 -0700 | [diff] [blame] | 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); |
Bill Richardson | 6f72ffa | 2014-09-23 14:40:20 -0700 | [diff] [blame] | 181 | |
| 182 | return 0; |
| 183 | } |
| 184 | |
Bill Richardson | 7ccd9ce | 2015-01-30 23:45:49 -0800 | [diff] [blame] | 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 | |
Bill Richardson | b0f1cc5 | 2014-09-24 00:23:56 -0700 | [diff] [blame] | 197 | int futil_traverse(uint8_t *buf, uint32_t len, |
| 198 | struct futil_traverse_state_s *state, |
| 199 | enum futil_file_type type) |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 200 | { |
| 201 | FmapHeader *fmap; |
| 202 | FmapAreaHeader *ah = 0; |
| 203 | const struct bios_area_s *area; |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 204 | int retval = 0; |
| 205 | |
David Riley | 05987b1 | 2015-02-05 19:22:49 -0800 | [diff] [blame] | 206 | if ((int) state->op < 0 || state->op >= NUM_FUTIL_OPS) { |
Bill Richardson | b0f1cc5 | 2014-09-24 00:23:56 -0700 | [diff] [blame] | 207 | fprintf(stderr, "Invalid op %d\n", state->op); |
| 208 | return 1; |
| 209 | } |
| 210 | |
| 211 | if (type == FILE_TYPE_UNKNOWN) |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 212 | type = futil_file_type_buf(buf, len); |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 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) { |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 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); |
Bill Richardson | 7ccd9ce | 2015-01-30 23:45:49 -0800 | [diff] [blame] | 227 | /* But the file might be truncated */ |
| 228 | fmap_limit_area(ah, len); |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 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); |
Bill Richardson | 7ccd9ce | 2015-01-30 23:45:49 -0800 | [diff] [blame] | 245 | /* But the file might be truncated */ |
| 246 | fmap_limit_area(ah, len); |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 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 | |
Bill Richardson | 7ccd9ce | 2015-01-30 23:45:49 -0800 | [diff] [blame] | 257 | case FILE_TYPE_UNKNOWN: |
| 258 | case FILE_TYPE_CHROMIUMOS_DISK: |
| 259 | /* Nothing to do for these file types (yet) */ |
| 260 | break; |
| 261 | |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 262 | default: |
Bill Richardson | 7ccd9ce | 2015-01-30 23:45:49 -0800 | [diff] [blame] | 263 | /* All other file types have their own callbacks */ |
Bill Richardson | 2559338 | 2015-01-30 12:22:28 -0800 | [diff] [blame] | 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; |
Bill Richardson | cf6e78d | 2014-08-27 15:50:25 -0700 | [diff] [blame] | 270 | } |
| 271 | |
| 272 | retval |= invoke_callback(state, CB_END_TRAVERSAL, "<end>", |
| 273 | 0, buf, len); |
| 274 | return retval; |
| 275 | } |