Randall Spangler | 584e0d2 | 2012-07-26 10:29:48 -0700 | [diff] [blame^] | 1 | /* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 2 | * Use of this source code is governed by a BSD-style license that can be |
| 3 | * found in the LICENSE file. |
| 4 | * |
| 5 | * Display functions used in kernel selection. |
| 6 | */ |
| 7 | |
Bill Richardson | 0a9977e | 2011-08-22 16:03:59 -0700 | [diff] [blame] | 8 | #include "bmpblk_font.h" |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 9 | #include "gbb_header.h" |
| 10 | #include "utility.h" |
| 11 | #include "vboot_api.h" |
| 12 | #include "vboot_common.h" |
| 13 | #include "vboot_display.h" |
| 14 | #include "vboot_nvstorage.h" |
| 15 | |
| 16 | static uint32_t disp_current_screen = VB_SCREEN_BLANK; |
| 17 | static uint32_t disp_width = 0, disp_height = 0; |
| 18 | |
| 19 | |
| 20 | /* Get the number of localizations in the GBB bitmap data. */ |
| 21 | static VbError_t VbGetLocalizationCount(VbCommonParams* cparams, |
| 22 | uint32_t* count) { |
| 23 | GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)cparams->gbb_data; |
| 24 | BmpBlockHeader* hdr; |
| 25 | |
| 26 | /* Default to 0 on error */ |
| 27 | *count = 0; |
| 28 | |
| 29 | /* Make sure the bitmap data is inside the GBB and is non-zero in size */ |
| 30 | if (0 == gbb->bmpfv_size || |
| 31 | gbb->bmpfv_offset > cparams->gbb_size || |
| 32 | gbb->bmpfv_offset + gbb->bmpfv_size > cparams->gbb_size) { |
| 33 | return VBERROR_INVALID_GBB; |
| 34 | } |
| 35 | |
| 36 | /* Sanity-check the bitmap block header */ |
| 37 | hdr = (BmpBlockHeader *)(((uint8_t*)gbb) + gbb->bmpfv_offset); |
| 38 | if ((0 != Memcmp(hdr->signature, BMPBLOCK_SIGNATURE, |
| 39 | BMPBLOCK_SIGNATURE_SIZE)) || |
| 40 | (hdr->major_version > BMPBLOCK_MAJOR_VERSION) || |
| 41 | ((hdr->major_version == BMPBLOCK_MAJOR_VERSION) && |
| 42 | (hdr->minor_version > BMPBLOCK_MINOR_VERSION))) { |
| 43 | return VBERROR_INVALID_BMPFV; |
| 44 | } |
| 45 | |
| 46 | *count = hdr->number_of_localizations; |
| 47 | return VBERROR_SUCCESS; |
| 48 | } |
| 49 | |
| 50 | |
| 51 | |
Bill Richardson | 0a9977e | 2011-08-22 16:03:59 -0700 | [diff] [blame] | 52 | /* Return a fixed string representing the HWID */ |
| 53 | static char *VbHWID(VbCommonParams* cparams) { |
| 54 | GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)cparams->gbb_data; |
| 55 | if (0 == gbb->hwid_size || |
| 56 | gbb->hwid_offset > cparams->gbb_size || |
| 57 | gbb->hwid_offset + gbb->hwid_size > cparams->gbb_size) { |
| 58 | VBDEBUG(("VbHWID(): invalid hwid offset/size\n")); |
| 59 | return "{INVALID}"; |
| 60 | } |
| 61 | return (char*)((uint8_t*)gbb + gbb->hwid_offset); |
| 62 | } |
| 63 | |
| 64 | |
| 65 | /* TODO: We could cache the font info to speed things up, by making the |
| 66 | * in-memory font structure distinct from the in-flash version. We'll do that |
| 67 | * Real Soon Now. Until then, we just repeat the same linear search every time. |
| 68 | */ |
| 69 | typedef FontArrayHeader VbFont_t; |
| 70 | |
| 71 | static VbFont_t *VbInternalizeFontData(FontArrayHeader *fonthdr) { |
| 72 | /* Just return the raw data pointer for now. */ |
| 73 | return (VbFont_t *)fonthdr; |
| 74 | } |
| 75 | |
| 76 | static void VbDoneWithFontForNow(VbFont_t *ptr) { |
| 77 | /* Nothing. */ |
| 78 | } |
| 79 | |
| 80 | static ImageInfo *VbFindFontGlyph(VbFont_t *font, uint32_t ascii, |
| 81 | void **bufferptr, uint32_t *buffersize) { |
| 82 | uint8_t *ptr, *firstptr; |
| 83 | uint32_t max; |
| 84 | uint32_t i; |
| 85 | FontArrayEntryHeader *entry; |
| 86 | |
| 87 | ptr = (uint8_t *)font; |
| 88 | max = ((FontArrayHeader *)ptr)->num_entries; |
| 89 | ptr += sizeof(FontArrayHeader); |
| 90 | firstptr = ptr; |
| 91 | |
| 92 | /* Simple linear search. */ |
| 93 | for(i=0; i<max; i++) |
| 94 | { |
| 95 | entry = (FontArrayEntryHeader *)ptr; |
| 96 | if (entry->ascii == ascii) { |
| 97 | /* Note: We're assuming the glpyh is uncompressed. That's true |
| 98 | * because the bmpblk_font tool doesn't compress anything. The |
| 99 | * bmpblk_utility does, but it compresses the entire font blob at once, |
| 100 | * and we've already uncompressed that before we got here. |
| 101 | */ |
| 102 | *bufferptr = ptr + sizeof(FontArrayEntryHeader); |
| 103 | *buffersize = entry->info.original_size; |
| 104 | return &(entry->info); |
| 105 | } |
| 106 | ptr += sizeof(FontArrayEntryHeader)+entry->info.compressed_size; |
| 107 | } |
| 108 | |
| 109 | /* NOTE: We must return something valid. We'll just use the first glyph in the |
| 110 | * font structure (so it should be something distinct). |
| 111 | */ |
| 112 | entry = (FontArrayEntryHeader *)firstptr; |
| 113 | *bufferptr = firstptr + sizeof(FontArrayEntryHeader); |
| 114 | *buffersize = entry->info.original_size; |
| 115 | return &(entry->info); |
| 116 | } |
| 117 | |
| 118 | /* Try to display the specified text at a particular position. */ |
| 119 | static void VbRenderTextAtPos(char *text, int right_to_left, |
| 120 | uint32_t x, uint32_t y, VbFont_t *font) { |
| 121 | int i; |
| 122 | ImageInfo *image_info = 0; |
| 123 | void *buffer; |
| 124 | uint32_t buffersize; |
| 125 | uint32_t cur_x = x, cur_y = y; |
| 126 | |
| 127 | if (!text || !font) { |
| 128 | VBDEBUG((" VbRenderTextAtPos: invalid args\n")); |
| 129 | return; |
| 130 | } |
| 131 | |
| 132 | for (i=0; text[i]; i++) { |
| 133 | |
| 134 | if (text[i] == '\n') { |
| 135 | if (!image_info) |
| 136 | image_info = VbFindFontGlyph(font, text[i], &buffer, &buffersize); |
| 137 | cur_x = x; |
| 138 | cur_y += image_info->height; |
| 139 | continue; |
| 140 | } |
| 141 | |
| 142 | image_info = VbFindFontGlyph(font, text[i], &buffer, &buffersize); |
| 143 | |
| 144 | if (right_to_left) { |
| 145 | cur_x -= image_info->width; |
| 146 | } |
| 147 | |
| 148 | if (VBERROR_SUCCESS != VbExDisplayImage(cur_x, cur_y, buffer, buffersize)) { |
| 149 | VBDEBUG((" VbRenderTextAtPos: can't display ascii 0x%x\n", text[i])); |
| 150 | } |
| 151 | |
| 152 | if (!right_to_left) { |
| 153 | cur_x += image_info->width; |
| 154 | } |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 159 | /* Display a screen from the GBB. */ |
Bill Richardson | f2ad05f | 2011-09-28 14:42:36 -0700 | [diff] [blame] | 160 | #define OUTBUF_LEN 128 |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 161 | VbError_t VbDisplayScreenFromGBB(VbCommonParams* cparams, uint32_t screen, |
| 162 | VbNvContext *vncptr) { |
| 163 | GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)cparams->gbb_data; |
| 164 | uint8_t* bmpfv = NULL; |
Bill Richardson | 0a9977e | 2011-08-22 16:03:59 -0700 | [diff] [blame] | 165 | void* fullimage = NULL; |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 166 | BmpBlockHeader* hdr; |
| 167 | ScreenLayout* layout; |
| 168 | ImageInfo* image_info; |
| 169 | uint32_t screen_index; |
| 170 | uint32_t localization = 0; |
Bill Richardson | 0a9977e | 2011-08-22 16:03:59 -0700 | [diff] [blame] | 171 | VbError_t retval = VBERROR_UNKNOWN; /* Assume error until proven ok */ |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 172 | uint32_t inoutsize; |
| 173 | uint32_t offset; |
| 174 | uint32_t i; |
Bill Richardson | 0a9977e | 2011-08-22 16:03:59 -0700 | [diff] [blame] | 175 | VbFont_t *font; |
| 176 | char *text_to_show; |
| 177 | int rtol = 0; |
Bill Richardson | f2ad05f | 2011-09-28 14:42:36 -0700 | [diff] [blame] | 178 | char outbuf[OUTBUF_LEN] = ""; |
| 179 | uint32_t used = 0; |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 180 | |
| 181 | /* Make sure the bitmap data is inside the GBB and is non-zero in size */ |
| 182 | if (0 == gbb->bmpfv_size || |
| 183 | gbb->bmpfv_offset > cparams->gbb_size || |
| 184 | gbb->bmpfv_offset + gbb->bmpfv_size > cparams->gbb_size) { |
| 185 | VBDEBUG(("VbDisplayScreenFromGBB(): invalid bmpfv offset/size\n")); |
| 186 | return VBERROR_INVALID_GBB; |
| 187 | } |
| 188 | |
| 189 | /* Copy bitmap data from GBB into RAM for speed */ |
| 190 | bmpfv = (uint8_t*)VbExMalloc(gbb->bmpfv_size); |
| 191 | Memcpy(bmpfv, ((uint8_t*)gbb) + gbb->bmpfv_offset, gbb->bmpfv_size); |
| 192 | |
| 193 | /* Sanity-check the bitmap block header */ |
| 194 | hdr = (BmpBlockHeader *)bmpfv; |
| 195 | if ((0 != Memcmp(hdr->signature, BMPBLOCK_SIGNATURE, |
| 196 | BMPBLOCK_SIGNATURE_SIZE)) || |
| 197 | (hdr->major_version > BMPBLOCK_MAJOR_VERSION) || |
| 198 | ((hdr->major_version == BMPBLOCK_MAJOR_VERSION) && |
| 199 | (hdr->minor_version > BMPBLOCK_MINOR_VERSION))) { |
| 200 | VBDEBUG(("VbDisplayScreenFromGBB(): invalid/too new bitmap header\n")); |
| 201 | retval = VBERROR_INVALID_BMPFV; |
| 202 | goto VbDisplayScreenFromGBB_exit; |
| 203 | } |
| 204 | |
| 205 | /* Translate screen ID into index. Note that not all screens are in the |
| 206 | * GBB. */ |
| 207 | /* TODO: ensure screen IDs match indices? Having this translation |
| 208 | * here is awful. */ |
| 209 | switch (screen) { |
Bill Richardson | 0a9977e | 2011-08-22 16:03:59 -0700 | [diff] [blame] | 210 | case VB_SCREEN_DEVELOPER_WARNING: |
| 211 | screen_index = 0; |
| 212 | break; |
| 213 | case VB_SCREEN_RECOVERY_REMOVE: |
| 214 | screen_index = 1; |
| 215 | break; |
| 216 | case VB_SCREEN_RECOVERY_NO_GOOD: |
| 217 | screen_index = 2; |
| 218 | break; |
| 219 | case VB_SCREEN_RECOVERY_INSERT: |
| 220 | screen_index = 3; |
| 221 | break; |
Bill Richardson | ec8df16 | 2012-06-07 04:21:14 -0700 | [diff] [blame] | 222 | case VB_SCREEN_RECOVERY_TO_DEV: |
| 223 | screen_index = 4; |
| 224 | break; |
| 225 | case VB_SCREEN_RECOVERY_TO_NORM: |
| 226 | screen_index = 5; |
| 227 | break; |
Bill Richardson | 0a9977e | 2011-08-22 16:03:59 -0700 | [diff] [blame] | 228 | case VB_SCREEN_BLANK: |
| 229 | case VB_SCREEN_DEVELOPER_EGG: |
| 230 | default: |
| 231 | /* Screens which aren't in the GBB */ |
| 232 | VBDEBUG(("VbDisplayScreenFromGBB(): screen %d not in the GBB\n", |
| 233 | (int)screen)); |
| 234 | retval = VBERROR_INVALID_SCREEN_INDEX; |
| 235 | goto VbDisplayScreenFromGBB_exit; |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 236 | } |
| 237 | if (screen_index >= hdr->number_of_screenlayouts) { |
| 238 | VBDEBUG(("VbDisplayScreenFromGBB(): screen %d index %d not in the GBB\n", |
| 239 | (int)screen, (int)screen_index)); |
| 240 | retval = VBERROR_INVALID_SCREEN_INDEX; |
| 241 | goto VbDisplayScreenFromGBB_exit; |
| 242 | } |
| 243 | |
| 244 | /* Clip localization to the number of localizations present in the GBB */ |
| 245 | VbNvGet(vncptr, VBNV_LOCALIZATION_INDEX, &localization); |
| 246 | if (localization >= hdr->number_of_localizations) { |
| 247 | localization = 0; |
| 248 | VbNvSet(vncptr, VBNV_LOCALIZATION_INDEX, localization); |
| 249 | } |
| 250 | |
| 251 | /* Calculate offset of screen layout = start of screen stuff + |
| 252 | * correct locale + correct screen. */ |
| 253 | offset = sizeof(BmpBlockHeader) + |
Bill Richardson | 0a9977e | 2011-08-22 16:03:59 -0700 | [diff] [blame] | 254 | localization * hdr->number_of_screenlayouts * sizeof(ScreenLayout) + |
| 255 | screen_index * sizeof(ScreenLayout); |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 256 | layout = (ScreenLayout*)(bmpfv + offset); |
| 257 | |
| 258 | /* Display all bitmaps for the image */ |
| 259 | for (i = 0; i < MAX_IMAGE_IN_LAYOUT; i++) { |
| 260 | if (layout->images[i].image_info_offset) { |
| 261 | offset = layout->images[i].image_info_offset; |
| 262 | image_info = (ImageInfo*)(bmpfv + offset); |
Bill Richardson | 0a9977e | 2011-08-22 16:03:59 -0700 | [diff] [blame] | 263 | fullimage = bmpfv + offset + sizeof(ImageInfo); |
| 264 | inoutsize = image_info->original_size; |
| 265 | if (inoutsize && image_info->compression != COMPRESS_NONE) { |
| 266 | fullimage = VbExMalloc(inoutsize); |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 267 | retval = VbExDecompress(bmpfv + offset + sizeof(ImageInfo), |
| 268 | image_info->compressed_size, |
| 269 | image_info->compression, |
| 270 | fullimage, &inoutsize); |
| 271 | if (VBERROR_SUCCESS != retval) { |
| 272 | VbExFree(fullimage); |
| 273 | goto VbDisplayScreenFromGBB_exit; |
| 274 | } |
Bill Richardson | 0a9977e | 2011-08-22 16:03:59 -0700 | [diff] [blame] | 275 | } |
| 276 | |
| 277 | switch(image_info->format) { |
| 278 | case FORMAT_BMP: |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 279 | retval = VbExDisplayImage(layout->images[i].x, layout->images[i].y, |
| 280 | fullimage, inoutsize); |
Bill Richardson | 0a9977e | 2011-08-22 16:03:59 -0700 | [diff] [blame] | 281 | break; |
| 282 | |
| 283 | case FORMAT_FONT: |
| 284 | /* The uncompressed blob is our font structure. Cache it as needed. */ |
| 285 | font = VbInternalizeFontData(fullimage); |
| 286 | |
| 287 | /* TODO: handle text in general here */ |
| 288 | if (TAG_HWID == image_info->tag || TAG_HWID_RTOL == image_info->tag) { |
| 289 | text_to_show = VbHWID(cparams); |
| 290 | rtol = (TAG_HWID_RTOL == image_info->tag); |
| 291 | } else { |
| 292 | text_to_show = ""; |
| 293 | rtol = 0; |
| 294 | } |
| 295 | |
| 296 | VbRenderTextAtPos(text_to_show, rtol, |
| 297 | layout->images[i].x, layout->images[i].y, font); |
| 298 | |
| 299 | VbDoneWithFontForNow(font); |
| 300 | break; |
| 301 | |
| 302 | default: |
| 303 | VBDEBUG(("VbDisplayScreenFromGBB(): unsupported ImageFormat %d\n", |
| 304 | image_info->format)); |
| 305 | retval = VBERROR_INVALID_GBB; |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 306 | } |
Bill Richardson | 0a9977e | 2011-08-22 16:03:59 -0700 | [diff] [blame] | 307 | |
| 308 | if (COMPRESS_NONE != image_info->compression) |
| 309 | VbExFree(fullimage); |
| 310 | |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 311 | if (VBERROR_SUCCESS != retval) |
| 312 | goto VbDisplayScreenFromGBB_exit; |
Bill Richardson | 0a9977e | 2011-08-22 16:03:59 -0700 | [diff] [blame] | 313 | |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 314 | } |
| 315 | } |
| 316 | |
| 317 | /* Successful if all bitmaps displayed */ |
| 318 | retval = VBERROR_SUCCESS; |
| 319 | |
Bill Richardson | ec35beb | 2011-09-14 10:42:54 -0700 | [diff] [blame] | 320 | /* If GBB flags is nonzero, complain because that's something that the |
| 321 | * factory MUST fix before shipping. We only have to do this here, because |
| 322 | * it's obvious that something is wrong if we're not displaying screens from |
| 323 | * the GBB. |
| 324 | */ |
| 325 | if (gbb->major_version == GBB_MAJOR_VER && gbb->minor_version >= 1 && |
| 326 | (gbb->flags != 0)) { |
Bill Richardson | f2ad05f | 2011-09-28 14:42:36 -0700 | [diff] [blame] | 327 | used += Strncat(outbuf + used, "gbb.flags is nonzero: 0x", |
| 328 | OUTBUF_LEN - used); |
| 329 | used += Uint64ToString(outbuf + used, OUTBUF_LEN - used, gbb->flags, 16, 8); |
Stefan Reinauer | 5ee257d | 2012-02-28 13:32:19 -0800 | [diff] [blame] | 330 | used += Strncat(outbuf + used, "\n", OUTBUF_LEN - used); |
Bill Richardson | f2ad05f | 2011-09-28 14:42:36 -0700 | [diff] [blame] | 331 | (void)VbExDisplayDebugInfo(outbuf); |
Bill Richardson | ec35beb | 2011-09-14 10:42:54 -0700 | [diff] [blame] | 332 | } |
| 333 | |
| 334 | |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 335 | VbDisplayScreenFromGBB_exit: |
| 336 | |
Bill Richardson | ec8df16 | 2012-06-07 04:21:14 -0700 | [diff] [blame] | 337 | VBDEBUG(("leaving VbDisplayScreenFromGBB() with %d\n",retval)); |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 338 | /* Free the bitmap data copy */ |
| 339 | VbExFree(bmpfv); |
| 340 | return retval; |
| 341 | } |
| 342 | |
| 343 | |
| 344 | /* Display a screen, initializing the display if necessary. If force!=0, |
| 345 | * redisplays the screen even if it's the same as the current screen. */ |
| 346 | VbError_t VbDisplayScreen(VbCommonParams* cparams, uint32_t screen, int force, |
| 347 | VbNvContext *vncptr) { |
| 348 | VbError_t retval; |
| 349 | |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 350 | /* Initialize display if necessary */ |
| 351 | if (!disp_width) { |
| 352 | retval = VbExDisplayInit(&disp_width, &disp_height); |
| 353 | if (VBERROR_SUCCESS != retval) |
| 354 | return retval; |
| 355 | } |
| 356 | |
| 357 | /* If the requested screen is the same as the current one, we're done. */ |
| 358 | if (disp_current_screen == screen && 0 == force) |
| 359 | return VBERROR_SUCCESS; |
| 360 | |
| 361 | /* If the screen is blank, turn off the backlight; else turn it on. */ |
| 362 | VbExDisplayBacklight(VB_SCREEN_BLANK == screen ? 0 : 1); |
| 363 | |
| 364 | /* Request the screen */ |
| 365 | disp_current_screen = screen; |
| 366 | |
| 367 | /* Look in the GBB first */ |
| 368 | if (VBERROR_SUCCESS == VbDisplayScreenFromGBB(cparams, screen, vncptr)) |
| 369 | return VBERROR_SUCCESS; |
| 370 | |
| 371 | /* If the screen wasn't in the GBB bitmaps, fall back to a default screen. */ |
| 372 | return VbExDisplayScreen(screen); |
| 373 | } |
| 374 | |
| 375 | |
Bill Richardson | 5de6b40 | 2011-09-01 14:47:05 -0700 | [diff] [blame] | 376 | static void Uint8ToString(char *buf, uint8_t val) { |
| 377 | const char *trans = "0123456789abcdef"; |
| 378 | *buf++ = trans[val >> 4]; |
| 379 | *buf = trans[val & 0xF]; |
| 380 | } |
| 381 | |
| 382 | static void FillInSha1Sum(char *outbuf, VbPublicKey* key) { |
| 383 | uint8_t* buf = ((uint8_t *)key) + key->key_offset; |
| 384 | uint64_t buflen = key->key_size; |
| 385 | uint8_t* digest = DigestBuf(buf, buflen, SHA1_DIGEST_ALGORITHM); |
| 386 | int i; |
| 387 | for (i=0; i<SHA1_DIGEST_SIZE; i++) { |
| 388 | Uint8ToString(outbuf, digest[i]); |
| 389 | outbuf += 2; |
| 390 | } |
| 391 | *outbuf = '\0'; |
| 392 | VbExFree(digest); |
| 393 | } |
| 394 | |
Bill Richardson | f2ad05f | 2011-09-28 14:42:36 -0700 | [diff] [blame] | 395 | |
| 396 | static const char *RecoveryReasonString(uint8_t code) { |
| 397 | switch(code) { |
| 398 | case VBNV_RECOVERY_NOT_REQUESTED: |
| 399 | return "Recovery not requested"; |
| 400 | case VBNV_RECOVERY_LEGACY: |
| 401 | return "Recovery requested from legacy utility"; |
| 402 | case VBNV_RECOVERY_RO_MANUAL: |
| 403 | return "recovery button pressed"; |
| 404 | case VBNV_RECOVERY_RO_INVALID_RW: |
| 405 | return "RW firmware failed signature check"; |
| 406 | case VBNV_RECOVERY_RO_S3_RESUME: |
| 407 | return "S3 resume failed"; |
| 408 | case VBNV_RECOVERY_RO_TPM_ERROR: |
| 409 | return "TPM error in read-only firmware"; |
| 410 | case VBNV_RECOVERY_RO_SHARED_DATA: |
| 411 | return "Shared data error in read-only firmware"; |
| 412 | case VBNV_RECOVERY_RO_TEST_S3: |
| 413 | return "Test error from S3Resume()"; |
| 414 | case VBNV_RECOVERY_RO_TEST_LFS: |
| 415 | return "Test error from LoadFirmwareSetup()"; |
| 416 | case VBNV_RECOVERY_RO_TEST_LF: |
| 417 | return "Test error from LoadFirmware()"; |
| 418 | case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_NOT_DONE: |
| 419 | return "RW firmware check not done"; |
| 420 | case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_DEV_MISMATCH: |
| 421 | return "RW firmware developer flag mismatch"; |
| 422 | case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_REC_MISMATCH: |
| 423 | return "RW firmware recovery flag mismatch"; |
| 424 | case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_VERIFY_KEYBLOCK: |
| 425 | return "RW firmware unable to verify key block"; |
| 426 | case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_KEY_ROLLBACK: |
| 427 | return "RW firmware key version rollback detected"; |
| 428 | case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_DATA_KEY_PARSE: |
| 429 | return "RW firmware unable to parse data key"; |
| 430 | case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_VERIFY_PREAMBLE: |
| 431 | return "RW firmware unable to verify preamble"; |
| 432 | case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_FW_ROLLBACK: |
| 433 | return "RW firmware version rollback detected"; |
| 434 | case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_GET_FW_BODY: |
| 435 | return "RW firmware unable to get firmware body"; |
| 436 | case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_HASH_WRONG_SIZE: |
| 437 | return "RW firmware hash is wrong size"; |
| 438 | case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_VERIFY_BODY: |
| 439 | return "RW firmware unable to verify firmware body"; |
| 440 | case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_NO_RO_NORMAL: |
| 441 | return "RW firmware read-only normal path is not supported"; |
| 442 | case VBNV_RECOVERY_RO_FIRMWARE: |
| 443 | return "Firmware problem outside of verified boot"; |
| 444 | case VBNV_RECOVERY_RO_TPM_REBOOT: |
| 445 | return "TPM requires a system reboot (should be transient)"; |
Randall Spangler | d4faa06 | 2012-07-16 16:42:40 -0700 | [diff] [blame] | 446 | case VBNV_RECOVERY_EC_SOFTWARE_SYNC: |
| 447 | return "EC software sync error"; |
| 448 | case VBNV_RECOVERY_EC_UNKNOWN_IMAGE: |
Randall Spangler | 584e0d2 | 2012-07-26 10:29:48 -0700 | [diff] [blame^] | 449 | return "EC software sync unable to determine active EC image"; |
| 450 | case VBNV_RECOVERY_EC_HASH: |
| 451 | return "EC software sync error obtaining EC image hash"; |
| 452 | case VBNV_RECOVERY_EC_EXPECTED_IMAGE: |
| 453 | return "EC software sync error obtaining expected EC image from BIOS"; |
| 454 | case VBNV_RECOVERY_EC_UPDATE: |
| 455 | return "EC software sync error updating EC"; |
| 456 | case VBNV_RECOVERY_EC_JUMP_RW: |
| 457 | return "EC software sync unable to jump to EC-RW"; |
| 458 | case VBNV_RECOVERY_EC_PROTECT: |
| 459 | return "EC software sync protection error"; |
Bill Richardson | f2ad05f | 2011-09-28 14:42:36 -0700 | [diff] [blame] | 460 | case VBNV_RECOVERY_RO_UNSPECIFIED: |
| 461 | return "Unspecified/unknown error in RO firmware"; |
| 462 | case VBNV_RECOVERY_RW_DEV_SCREEN: |
| 463 | return "User requested recovery from dev-mode warning screen"; |
| 464 | case VBNV_RECOVERY_RW_NO_OS: |
| 465 | return "No OS kernel detected (or kernel rollback attempt?)"; |
| 466 | case VBNV_RECOVERY_RW_INVALID_OS: |
| 467 | return "OS kernel failed signature check"; |
| 468 | case VBNV_RECOVERY_RW_TPM_ERROR: |
| 469 | return "TPM error in rewritable firmware"; |
| 470 | case VBNV_RECOVERY_RW_DEV_MISMATCH: |
| 471 | return "RW firmware in dev mode, but dev switch is off"; |
| 472 | case VBNV_RECOVERY_RW_SHARED_DATA: |
| 473 | return "Shared data error in rewritable firmware"; |
| 474 | case VBNV_RECOVERY_RW_TEST_LK: |
| 475 | return "Test error from LoadKernel()"; |
| 476 | case VBNV_RECOVERY_RW_NO_DISK: |
| 477 | return "No bootable disk found"; |
| 478 | case VBNV_RECOVERY_RW_UNSPECIFIED: |
| 479 | return "Unspecified/unknown error in RW firmware"; |
| 480 | case VBNV_RECOVERY_KE_DM_VERITY: |
| 481 | return "DM-verity error"; |
| 482 | case VBNV_RECOVERY_KE_UNSPECIFIED: |
| 483 | return "Unspecified/unknown error in kernel"; |
| 484 | case VBNV_RECOVERY_US_TEST: |
| 485 | return "Recovery mode test from user-mode"; |
| 486 | case VBNV_RECOVERY_US_UNSPECIFIED: |
| 487 | return "Unspecified/unknown error in user-mode"; |
| 488 | } |
| 489 | return "We have no idea what this means"; |
| 490 | } |
| 491 | |
| 492 | |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 493 | #define DEBUG_INFO_SIZE 512 |
| 494 | |
| 495 | /* Display debug info to the screen */ |
| 496 | VbError_t VbDisplayDebugInfo(VbCommonParams* cparams, VbNvContext *vncptr) { |
| 497 | VbSharedDataHeader* shared = (VbSharedDataHeader*)cparams->shared_data_blob; |
| 498 | GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)cparams->gbb_data; |
| 499 | char buf[DEBUG_INFO_SIZE] = ""; |
Bill Richardson | 5de6b40 | 2011-09-01 14:47:05 -0700 | [diff] [blame] | 500 | char sha1sum[SHA1_DIGEST_SIZE * 2 + 1]; |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 501 | uint32_t used = 0; |
| 502 | uint32_t i; |
| 503 | |
| 504 | /* Redisplay the current screen, to overwrite any previous debug output */ |
| 505 | VbDisplayScreen(cparams, disp_current_screen, 1, vncptr); |
| 506 | |
| 507 | /* Add hardware ID */ |
| 508 | used += Strncat(buf + used, "HWID: ", DEBUG_INFO_SIZE - used); |
| 509 | if (0 == gbb->hwid_size || |
| 510 | gbb->hwid_offset > cparams->gbb_size || |
| 511 | gbb->hwid_offset + gbb->hwid_size > cparams->gbb_size) { |
Bill Richardson | 791d6e6 | 2011-11-14 17:00:40 -0800 | [diff] [blame] | 512 | VBDEBUG(("VbDisplayDebugInfo(): invalid hwid offset/size\n")); |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 513 | used += Strncat(buf + used, "(INVALID)", DEBUG_INFO_SIZE - used); |
| 514 | } else { |
| 515 | used += Strncat(buf + used, (char*)((uint8_t*)gbb + gbb->hwid_offset), |
| 516 | DEBUG_INFO_SIZE - used); |
| 517 | } |
| 518 | |
| 519 | /* Add recovery reason */ |
| 520 | used += Strncat(buf + used, "\nrecovery_reason: 0x", DEBUG_INFO_SIZE - used); |
| 521 | used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, |
| 522 | shared->recovery_reason, 16, 2); |
Bill Richardson | f2ad05f | 2011-09-28 14:42:36 -0700 | [diff] [blame] | 523 | used += Strncat(buf + used, " ", DEBUG_INFO_SIZE - used); |
| 524 | used += Strncat(buf + used, RecoveryReasonString(shared->recovery_reason), |
| 525 | DEBUG_INFO_SIZE - used); |
| 526 | |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 527 | |
| 528 | /* Add VbSharedData flags */ |
| 529 | used += Strncat(buf + used, "\nVbSD.flags: 0x", DEBUG_INFO_SIZE - used); |
| 530 | used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, |
| 531 | shared->flags, 16, 8); |
| 532 | |
| 533 | /* Add raw contents of VbNvStorage */ |
| 534 | used += Strncat(buf + used, "\nVbNv.raw:", DEBUG_INFO_SIZE - used); |
| 535 | for (i = 0; i < VBNV_BLOCK_SIZE; i++) { |
| 536 | used += Strncat(buf + used, " ", DEBUG_INFO_SIZE - used); |
| 537 | used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, |
| 538 | vncptr->raw[i], 16, 2); |
| 539 | } |
| 540 | |
| 541 | /* Add dev_boot_usb flag */ |
| 542 | VbNvGet(vncptr, VBNV_DEV_BOOT_USB, &i); |
| 543 | used += Strncat(buf + used, "\ndev_boot_usb: ", DEBUG_INFO_SIZE - used); |
| 544 | used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, i, 10, 0); |
| 545 | |
Bill Richardson | 7272a69 | 2011-11-17 10:48:59 -0800 | [diff] [blame] | 546 | /* Add dev_boot_signed_only flag */ |
| 547 | VbNvGet(vncptr, VBNV_DEV_BOOT_SIGNED_ONLY, &i); |
| 548 | used += Strncat(buf + used, "\ndev_boot_signed_only: ", |
| 549 | DEBUG_INFO_SIZE - used); |
Bill Richardson | fa9d778 | 2011-11-09 09:11:34 -0800 | [diff] [blame] | 550 | used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, i, 10, 0); |
| 551 | |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 552 | /* Add TPM versions */ |
| 553 | used += Strncat(buf + used, "\nTPM: fwver=0x", DEBUG_INFO_SIZE - used); |
| 554 | used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, |
| 555 | shared->fw_version_tpm, 16, 8); |
| 556 | used += Strncat(buf + used, " kernver=0x", DEBUG_INFO_SIZE - used); |
| 557 | used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, |
| 558 | shared->kernel_version_tpm, 16, 8); |
| 559 | |
| 560 | /* Add GBB flags */ |
| 561 | used += Strncat(buf + used, "\ngbb.flags: 0x", DEBUG_INFO_SIZE - used); |
| 562 | if (gbb->major_version == GBB_MAJOR_VER && gbb->minor_version >= 1) { |
| 563 | used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, |
| 564 | gbb->flags, 16, 8); |
| 565 | } else { |
| 566 | used += Strncat(buf + used, "0 (default)", DEBUG_INFO_SIZE - used); |
| 567 | } |
| 568 | |
Bill Richardson | 5de6b40 | 2011-09-01 14:47:05 -0700 | [diff] [blame] | 569 | /* Add sha1sum for Root & Recovery keys */ |
| 570 | FillInSha1Sum(sha1sum, |
| 571 | (VbPublicKey*)((uint8_t*)gbb + gbb->rootkey_offset)); |
| 572 | used += Strncat(buf + used, "\ngbb.rootkey: ", DEBUG_INFO_SIZE - used); |
| 573 | used += Strncat(buf + used, sha1sum, DEBUG_INFO_SIZE - used); |
| 574 | FillInSha1Sum(sha1sum, |
| 575 | (VbPublicKey*)((uint8_t*)gbb + gbb->recovery_key_offset)); |
| 576 | used += Strncat(buf + used, "\ngbb.recovery_key: ", DEBUG_INFO_SIZE - used); |
| 577 | used += Strncat(buf + used, sha1sum, DEBUG_INFO_SIZE - used); |
| 578 | |
| 579 | /* If we're in dev-mode, show the kernel subkey that we expect, too. */ |
| 580 | if (0 == shared->recovery_reason) { |
| 581 | FillInSha1Sum(sha1sum, &shared->kernel_subkey); |
| 582 | used += Strncat(buf + used, "\nkernel_subkey: ", DEBUG_INFO_SIZE - used); |
| 583 | used += Strncat(buf + used, sha1sum, DEBUG_INFO_SIZE - used); |
| 584 | } |
| 585 | |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 586 | /* Make sure we finish with a newline */ |
| 587 | used += Strncat(buf + used, "\n", DEBUG_INFO_SIZE - used); |
| 588 | |
| 589 | /* TODO: add more interesting data: |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 590 | * - Information on current disks */ |
| 591 | |
| 592 | buf[DEBUG_INFO_SIZE - 1] = '\0'; |
| 593 | return VbExDisplayDebugInfo(buf); |
| 594 | } |
| 595 | |
| 596 | |
Bill Richardson | 518d4f3 | 2011-09-13 15:28:55 -0700 | [diff] [blame] | 597 | #define MAGIC_WORD_LEN 5 |
| 598 | #define MAGIC_WORD "xyzzy" |
| 599 | static uint8_t MagicBuffer[MAGIC_WORD_LEN]; |
| 600 | |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 601 | VbError_t VbCheckDisplayKey(VbCommonParams* cparams, uint32_t key, |
| 602 | VbNvContext *vncptr) { |
Bill Richardson | 518d4f3 | 2011-09-13 15:28:55 -0700 | [diff] [blame] | 603 | int i; |
| 604 | |
| 605 | /* Update key buffer */ |
| 606 | for(i=1; i<MAGIC_WORD_LEN; i++) |
| 607 | MagicBuffer[i-1] = MagicBuffer[i]; |
| 608 | /* Save as lower-case ASCII */ |
| 609 | MagicBuffer[MAGIC_WORD_LEN-1] = (key | 0x20) & 0xFF; |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 610 | |
| 611 | if ('\t' == key) { |
| 612 | /* Tab = display debug info */ |
| 613 | return VbDisplayDebugInfo(cparams, vncptr); |
Bill Richardson | c55f0f6 | 2011-09-13 13:42:01 -0700 | [diff] [blame] | 614 | } else if (VB_KEY_LEFT == key || VB_KEY_RIGHT == key || |
| 615 | VB_KEY_DOWN == key || VB_KEY_UP == key) { |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 616 | /* Arrow keys = change localization */ |
| 617 | uint32_t loc = 0; |
| 618 | uint32_t count = 0; |
| 619 | |
| 620 | VbNvGet(vncptr, VBNV_LOCALIZATION_INDEX, &loc); |
| 621 | if (VBERROR_SUCCESS != VbGetLocalizationCount(cparams, &count)) |
| 622 | loc = 0; /* No localization count (bad GBB?), so set to 0 (default) */ |
Bill Richardson | c55f0f6 | 2011-09-13 13:42:01 -0700 | [diff] [blame] | 623 | else if (VB_KEY_RIGHT == key || VB_KEY_UP == key) |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 624 | loc = (loc < count - 1 ? loc + 1 : 0); |
| 625 | else |
| 626 | loc = (loc > 0 ? loc - 1 : count - 1); |
| 627 | VBDEBUG(("VbCheckDisplayKey() - change localization to %d\n", (int)loc)); |
| 628 | VbNvSet(vncptr, VBNV_LOCALIZATION_INDEX, loc); |
Bill Richardson | 5fd3597 | 2012-01-24 13:27:43 -0800 | [diff] [blame] | 629 | /* Workaround for coreboot on x86, which will power off asynchronously |
| 630 | * without giving us a chance to react. This is not an example of the Right |
| 631 | * Way to do things. See chrome-os-partner:7689, and the commit message |
| 632 | * that made this change. |
| 633 | */ |
| 634 | VbNvTeardown(vncptr); /* really only computes checksum */ |
| 635 | if (vncptr->raw_changed) |
| 636 | VbExNvStorageWrite(vncptr->raw); |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 637 | |
| 638 | /* Force redraw of current screen */ |
| 639 | return VbDisplayScreen(cparams, disp_current_screen, 1, vncptr); |
| 640 | } |
| 641 | |
Bill Richardson | 518d4f3 | 2011-09-13 15:28:55 -0700 | [diff] [blame] | 642 | if (0 == Memcmp(MagicBuffer, MAGIC_WORD, MAGIC_WORD_LEN)) { |
Bill Richardson | 0600e41 | 2011-10-11 15:00:17 -0700 | [diff] [blame] | 643 | if (VBEASTEREGG) |
Bill Richardson | 5b96f81 | 2011-10-11 11:53:29 -0700 | [diff] [blame] | 644 | (void)VbDisplayScreen(cparams, disp_current_screen, 1, vncptr); |
Bill Richardson | 518d4f3 | 2011-09-13 15:28:55 -0700 | [diff] [blame] | 645 | } |
| 646 | |
Bill Richardson | 822eca6 | 2011-08-22 14:06:38 -0700 | [diff] [blame] | 647 | return VBERROR_SUCCESS; |
| 648 | } |