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