diff options
author | Joel Kitching <kitching@google.com> | 2018-03-28 17:15:38 +0800 |
---|---|---|
committer | ChromeOS Commit Bot <chromeos-commit-bot@chromium.org> | 2018-07-27 03:19:40 +0000 |
commit | 95ab37681a997054f00a3137c4211777bc946eff (patch) | |
tree | 48648c1ec96b2efe0030c36994ef1be6f2442df0 | |
parent | 82414e8186bc4c3f0cd902ec47e3f96fd98e6f02 (diff) | |
download | vboot-95ab37681a997054f00a3137c4211777bc946eff.tar.gz |
depthcharge: initial implementation of Alt OS boot flow
The goal of Alt OS is to allow for booting of alternate
operating systems without leaving verified normal mode.
See here for details: go/vboot-altos
BUG=b:70804764
TEST=make runtests
TEST=COV=1 make coverage
TEST=Step with GDB and either use kbatboot or hold down A
CQ-DEPEND=CL:1126806,CL:972763
BRANCH=firmware-eve-campfire
Change-Id: I71045f6a0f29768f15fcb4130f6063d4190611bb
Reviewed-on: https://chromium-review.googlesource.com/983312
Tested-by: Joel Kitching <kitching@chromium.org>
Trybot-Ready: Joel Kitching <kitching@chromium.org>
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Commit-Queue: Hung-Te Lin <hungte@chromium.org>
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | firmware/include/vboot_api.h | 7 | ||||
-rw-r--r-- | firmware/include/vboot_struct.h | 4 | ||||
-rw-r--r-- | firmware/lib/include/rollback_index.h | 2 | ||||
-rw-r--r-- | firmware/lib/include/vboot_kernel.h | 5 | ||||
-rw-r--r-- | firmware/lib/vboot_api_kernel.c | 116 | ||||
-rw-r--r-- | firmware/lib/vboot_ui.c | 97 | ||||
-rw-r--r-- | firmware/stub/vboot_api_stub_switches.c | 14 | ||||
-rw-r--r-- | tests/vboot_api_kernel2_tests.c | 42 | ||||
-rw-r--r-- | tests/vboot_api_kernel4_tests.c | 114 |
10 files changed, 402 insertions, 2 deletions
@@ -417,7 +417,8 @@ VBINIT_SRCS += \ VBSLK_SRCS += \ firmware/stub/vboot_api_stub.c \ firmware/stub/vboot_api_stub_disk.c \ - firmware/stub/vboot_api_stub_stream.c + firmware/stub/vboot_api_stub_stream.c \ + firmware/stub/vboot_api_stub_switches.c FWLIB2X_SRCS += \ firmware/2lib/2stub.c diff --git a/firmware/include/vboot_api.h b/firmware/include/vboot_api.h index b6fb0969..2f88a38a 100644 --- a/firmware/include/vboot_api.h +++ b/firmware/include/vboot_api.h @@ -126,6 +126,8 @@ enum VbErrorPredefined_t { VBERROR_RW_JUMP_FAILED = 0x10028, /* Error reading FWMP from TPM (note: not present is not an error) */ VBERROR_TPM_READ_FWMP = 0x10029, + /* Error reading or writing Alt OS flags to TPM */ + VBERROR_TPM_ALT_OS = 0x10030, /* VbExEcGetExpectedRWHash() may return the following codes */ /* Compute expected RW hash from the EC image; BIOS doesn't have it */ @@ -916,6 +918,11 @@ uint32_t VbExKeyboardReadWithFlags(uint32_t *flags_ptr); */ uint32_t VbExGetSwitches(uint32_t request_mask); +/** + * Return whether Alt OS hotkey was held down at boot. + */ +int vb2ex_get_alt_os_hotkey(void); + /*****************************************************************************/ /* Embedded controller (EC) */ diff --git a/firmware/include/vboot_struct.h b/firmware/include/vboot_struct.h index 08910d13..f005feb3 100644 --- a/firmware/include/vboot_struct.h +++ b/firmware/include/vboot_struct.h @@ -241,6 +241,10 @@ typedef struct VbKernelPreambleHeader { #define VBSD_OPROM_LOADED 0x00020000 /* Don't try for boot failures */ #define VBSD_NOFAIL_BOOT 0x00040000 +/* Confirm enabling Alt OS for this boot */ +#define VBSD_ALT_OS_CONFIRM_ENABLE 0x00080000 +/* Show Alt OS picker screen for this boot */ +#define VBSD_ALT_OS_SHOW_PICKER 0x00100000 /* * Supported flags by header version. It's ok to add new flags while keeping diff --git a/firmware/lib/include/rollback_index.h b/firmware/lib/include/rollback_index.h index 9152d549..b568ab09 100644 --- a/firmware/lib/include/rollback_index.h +++ b/firmware/lib/include/rollback_index.h @@ -164,7 +164,7 @@ uint32_t SetVirtualDevMode(int val); enum alt_os_flags { ALT_OS_ENABLE = (1 << 0), - ALT_OS_HOT_KEY = (1 << 1), + ALT_OS_HOTKEY = (1 << 1), }; /** diff --git a/firmware/lib/include/vboot_kernel.h b/firmware/lib/include/vboot_kernel.h index 2195e0cf..ba2f51b0 100644 --- a/firmware/lib/include/vboot_kernel.h +++ b/firmware/lib/include/vboot_kernel.h @@ -83,6 +83,11 @@ VbError_t VbBootDeveloperMenu(struct vb2_context *ctx, VbCommonParams *cparams); VbError_t VbBootRecoveryMenu(struct vb2_context *ctx, VbCommonParams *cparams); /** + * Handle an Alt OS-mode boot. + */ +VbError_t VbBootAltOS(struct vb2_context *ctx, VbCommonParams *cparams); + +/** * Return the current FWMP flags. Valid only inside VbSelectAndLoadKernel(). */ uint32_t vb2_get_fwmp_flags(void); diff --git a/firmware/lib/vboot_api_kernel.c b/firmware/lib/vboot_api_kernel.c index d1afc4f7..08254a47 100644 --- a/firmware/lib/vboot_api_kernel.c +++ b/firmware/lib/vboot_api_kernel.c @@ -406,6 +406,101 @@ static void vb2_kernel_cleanup(struct vb2_context *ctx, VbCommonParams *cparams) shared->timer_vb_select_and_load_kernel_exit = VbExGetTimer(); } +int VbAltOSForceChromeOS(void) { + return 0; +} + +VbError_t VbCheckAltOS(struct vb2_context *ctx, VbCommonParams *cparams, + int trusted_ec) { + VbSharedDataHeader *shared = + (VbSharedDataHeader *)cparams->shared_data_blob; + + int req_enable = vb2_nv_get(ctx, VB2_NV_ENABLE_ALT_OS_REQUEST); + int req_disable = vb2_nv_get(ctx, VB2_NV_DISABLE_ALT_OS_REQUEST); + + /* Reset enable/disable requests right away to prevent cycles. */ + vb2_nv_set(ctx, VB2_NV_ENABLE_ALT_OS_REQUEST, 0); + vb2_nv_set(ctx, VB2_NV_DISABLE_ALT_OS_REQUEST, 0); + + uint8_t kflags; + uint8_t kflags_set; + int rv; + rv = GetAltOSFlags(&kflags); + if (rv) { + VB2_DEBUG("Unable to read Alt OS flags from TPM\n"); + return rv; + } + kflags_set = kflags; + + int need_oprom = 0; + int oprom_loaded = !(shared->flags & VBSD_OPROM_MATTERS) || + shared->flags & VBSD_OPROM_LOADED; + int hotkey_after_sync = vb2ex_get_alt_os_hotkey(); + int hotkey_last_boot = !!(kflags & ALT_OS_HOTKEY); + int enabled = !!(kflags & ALT_OS_ENABLE); + int force_cros = VbAltOSForceChromeOS(); + + + /* Case 1: Disable Alt OS mode. Does not need UI. */ + if (enabled && req_disable) { + VB2_DEBUG("Disabling Alt OS mode...\n"); + /* Disable has priority over enable. */ + req_enable = 0; + enabled = 0; + kflags_set &= ~ALT_OS_ENABLE; + } + + /* Case 2: Enable Alt OS mode. Needs UI. */ + if (!enabled && ((req_enable && hotkey_after_sync && trusted_ec) || + hotkey_last_boot)) { + VB2_DEBUG("Setting flag to show confirm Alt OS mode\n"); + shared->flags |= VBSD_ALT_OS_CONFIRM_ENABLE; + need_oprom = 1; + /* If we need to reboot to load VGA Option ROM, save Alt OS + * hotkey state in TPM for next boot. */ + kflags_set |= ALT_OS_HOTKEY; + } + + /* Case 3: Show Alt OS picker. Needs UI. */ + if (enabled && !force_cros) { + VB2_DEBUG("Setting flag to show Alt OS picker\n"); + shared->flags |= VBSD_ALT_OS_SHOW_PICKER; + need_oprom = 1; + } + + /* If we don't need to store Alt OS hotkey state, then remove it. */ + if (!need_oprom || oprom_loaded) + kflags_set &= ~ALT_OS_HOTKEY; + + if (kflags_set != kflags) { + rv = SetAltOSFlags(kflags_set); + if (rv) { + VB2_DEBUG("Unable to write Alt OS flags to TPM\n"); + return rv; + } + } + + VB2_DEBUG("Alt OS: kflags=%d\n", kflags); + VB2_DEBUG("Alt OS: kflags_set=%d\n", kflags_set); + VB2_DEBUG("Alt OS: hotkey_after_sync=%d\n", hotkey_after_sync); + VB2_DEBUG("Alt OS: hotkey_last_boot=%d\n", hotkey_last_boot); + VB2_DEBUG("Alt OS: need_oprom=%d\n", need_oprom); + VB2_DEBUG("Alt OS: oprom_loaded=%d\n", oprom_loaded); + VB2_DEBUG("Alt OS: enabled=%d\n", enabled); + VB2_DEBUG("Alt OS: force_cros=%d\n", force_cros); + VB2_DEBUG("Alt OS: req_enable=%d\n", req_enable); + VB2_DEBUG("Alt OS: req_disable=%d\n", req_disable); + VB2_DEBUG("Alt OS: trusted_ec=%d\n", trusted_ec); + + if (need_oprom && !oprom_loaded) { + VB2_DEBUG("Reboot to load VGA Option ROM\n"); + vb2_nv_set(ctx, VB2_NV_OPROM_NEEDED, 1); + return VBERROR_VGA_OPROM_MISMATCH; + } + + return VBERROR_SUCCESS; +} + VbError_t VbSelectAndLoadKernel(VbCommonParams *cparams, VbSelectAndLoadKernelParams *kparams) { @@ -417,6 +512,12 @@ VbError_t VbSelectAndLoadKernel(VbCommonParams *cparams, goto VbSelectAndLoadKernel_exit; /* + * Determine whether the EC is in RO or RW. This information + * will be used later on in Alt OS boot flow. + */ + int trusted_ec = VbExTrustEC(0); + + /* * Do EC software sync if necessary. This has UI, but it's just a * single non-interactive WAIT screen. */ @@ -424,6 +525,15 @@ VbError_t VbSelectAndLoadKernel(VbCommonParams *cparams, if (retval) goto VbSelectAndLoadKernel_exit; + /* + * Check whether confirmation screen or picker screen need to be + * shown for Alt OS. Ignore return value, and in the case of failure, + * continue to a normal boot. + */ + retval = VbCheckAltOS(&ctx, cparams, trusted_ec); + if (retval == VBERROR_VGA_OPROM_MISMATCH) + goto VbSelectAndLoadKernel_exit; + /* Select boot path */ if (shared->recovery_reason) { /* Recovery boot. This has UI. */ @@ -439,6 +549,12 @@ VbError_t VbSelectAndLoadKernel(VbCommonParams *cparams, else retval = VbBootDeveloper(&ctx, cparams); VbExEcEnteringMode(0, VB_EC_DEVELOPER); + } else if (shared->flags & VBSD_ALT_OS_CONFIRM_ENABLE || + shared->flags & VBSD_ALT_OS_SHOW_PICKER) { + /* Alt OS boot. This has UI. */ + retval = VbBootAltOS(&ctx, cparams); + /* Report as normal mode to the EC. */ + VbExEcEnteringMode(0, VB_EC_NORMAL); } else { /* Normal boot */ retval = VbBootNormal(&ctx, cparams); diff --git a/firmware/lib/vboot_ui.c b/firmware/lib/vboot_ui.c index 4739cd64..d4691157 100644 --- a/firmware/lib/vboot_ui.c +++ b/firmware/lib/vboot_ui.c @@ -153,6 +153,103 @@ int VbUserConfirms(struct vb2_context *ctx, VbCommonParams *cparams, return -1; } +VbError_t vb2_alt_os_picker(struct vb2_context *ctx, VbCommonParams *cparams, + uint32_t timeout_msec, int *index) +{ + /* Calibrate delay */ + uint64_t a, b; + a = VbExGetTimer(); + VbExSleepMs(10); + b = VbExGetTimer(); + uint64_t ticks_per_msec = (b - a) / 10ULL ; + uint64_t show_until = VbExGetTimer() + timeout_msec * ticks_per_msec; + + VB2_DEBUG("Alt OS picker: timeout_msec=%d\n", timeout_msec); + VB2_DEBUG("Alt OS picker: ticks_per_msec=%" PRIu64 "\n", + ticks_per_msec); + + *index = 0; + while (1) { + VbDisplayMenu(ctx, cparams, VB_SCREEN_ALT_OS, 0, *index); + if (VbWantShutdown(cparams->gbb->flags)) + return VBERROR_SHUTDOWN_REQUESTED; + uint32_t key = VbExKeyboardRead(); + if (key == VB_KEY_LEFT) + *index = 0; + else if (key == VB_KEY_RIGHT) + *index = 1; + else if (key == '\r' || key == ' ') + break; + VbExSleepMs(20); + + if (timeout_msec > 0 && VbExGetTimer() > show_until) { + VB2_DEBUG("Alt OS picker: timed out\n"); + break; + } + } + return VBERROR_SUCCESS; +} + +VbError_t vb2_alt_os_ui(struct vb2_context *ctx, VbCommonParams *cparams) +{ + /* Note that OPROM will not be disabled until the next reboot. */ + const int picker_timeout_msec = 20 * 1000; + VbSharedDataHeader *shared = + (VbSharedDataHeader *)cparams->shared_data_blob; + int boot_alt_os = 0; /* 0 = Chrome OS; 1 = Alt OS */ + uint8_t tpm_flags; + + /* Confirm enabling Alt OS */ + if (shared->flags & VBSD_ALT_OS_CONFIRM_ENABLE) { + VB2_DEBUG("Alt OS UI: Picker screen without timeout\n"); + VbError_t ret = vb2_alt_os_picker(ctx, cparams, + 0, &boot_alt_os); + if (ret != VBERROR_SUCCESS) { + VB2_DEBUG("Error from Alt OS picker screen\n"); + return ret; + } + } + + /* Enable if Alt OS is chosen */ + if (boot_alt_os) { + if (GetAltOSFlags(&tpm_flags)) { + VB2_DEBUG("Unable to read Alt OS flags from TPM\n"); + return VBERROR_TPM_ALT_OS; + } + tpm_flags |= ALT_OS_ENABLE; + if (SetAltOSFlags(tpm_flags)) { + VB2_DEBUG("Unable to write Alt OS flags to TPM\n"); + return VBERROR_TPM_ALT_OS; + } + } + + /* Show Alt OS picker screen */ + else if (shared->flags & VBSD_ALT_OS_SHOW_PICKER) { + VB2_DEBUG("Alt OS UI: Picker screen with timeout\n"); + VbError_t ret = vb2_alt_os_picker(ctx, cparams, + picker_timeout_msec, + &boot_alt_os); + if (ret != VBERROR_SUCCESS) { + VB2_DEBUG("Error from Alt OS picker screen\n"); + return ret; + } + } + + if (boot_alt_os) { + /* Will only return on failure */ + VbTryLegacy(ctx, 1); + } + + /* Will only return on failure */ + return VbBootNormal(ctx, cparams); +} + +VbError_t VbBootAltOS(struct vb2_context *ctx, VbCommonParams *cparams) +{ + VbError_t retval = vb2_alt_os_ui(ctx, cparams); + return retval; +} + static const char dev_disable_msg[] = "Developer mode is disabled on this device by system policy.\n" "For more information, see http://dev.chromium.org/chromium-os/fwmp\n" diff --git a/firmware/stub/vboot_api_stub_switches.c b/firmware/stub/vboot_api_stub_switches.c new file mode 100644 index 00000000..29ab8e0d --- /dev/null +++ b/firmware/stub/vboot_api_stub_switches.c @@ -0,0 +1,14 @@ +/* Copyright 2018 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Stub implementations of switch APIs. + */ + +#include "vboot_api.h" + + +int vb2ex_get_alt_os_hotkey(void) +{ + return 0; +} diff --git a/tests/vboot_api_kernel2_tests.c b/tests/vboot_api_kernel2_tests.c index f6776e9e..cbad7827 100644 --- a/tests/vboot_api_kernel2_tests.c +++ b/tests/vboot_api_kernel2_tests.c @@ -51,6 +51,8 @@ static uint32_t screens_displayed[8]; static uint32_t screens_count = 0; static uint32_t mock_num_disks[8]; static uint32_t mock_num_disks_count; +static uint8_t gaf_val, saf_val; +static int gaf_retval, saf_retval; extern enum VbEcBootMode_t VbGetMode(void); extern struct RollbackSpaceFwmp *VbApiKernelGetFwmp(void); @@ -89,6 +91,8 @@ static void ResetMocks(void) trust_ec = 0; virtdev_set = 0; virtdev_retval = 0; + gaf_val = saf_val = 0; + gaf_retval = saf_retval = VBERROR_SUCCESS; memset(screens_displayed, 0, sizeof(screens_displayed)); screens_count = 0; @@ -204,6 +208,18 @@ uint32_t SetVirtualDevMode(int val) return virtdev_retval; } +uint32_t GetAltOSFlags(uint8_t *val) +{ + *val = gaf_val; + return gaf_retval; +} + +uint32_t SetAltOSFlags(uint8_t val) +{ + saf_val = val; + return saf_retval; +} + /* Tests */ static void VbUserConfirmsTest(void) @@ -733,12 +749,38 @@ static void VbBootRecTest(void) } +static void VbBootAltOSTest(void) +{ + printf("Testing VbBootAltOS()...\n"); + + /* Right arrow then enter means enable */ + ResetMocks(); + shared->flags = VBSD_ALT_OS_CONFIRM_ENABLE; + mock_keypress[0] = 0x103; + mock_keypress[1] = '\r'; + TEST_EQ(VbBootAltOS(&ctx, &cparams), 1002, "Enable Alt OS failure"); + TEST_EQ(vbexlegacy_called, 1, " boot legacy"); + TEST_TRUE(saf_val & ALT_OS_ENABLE, " enable flag"); + + /* Right arrow then enter means boot Chrome OS */ + ResetMocks(); + shared->flags = VBSD_ALT_OS_SHOW_PICKER; + mock_keypress[0] = 0x102; + mock_keypress[1] = '\r'; + TEST_EQ(VbBootAltOS(&ctx, &cparams), 1002, "Boot Chrome OS failure"); + TEST_EQ(vbexlegacy_called, 0, " boot normal"); + + printf("...done.\n"); +} + + int main(void) { VbUserConfirmsTest(); VbBootTest(); VbBootDevTest(); VbBootRecTest(); + VbBootAltOSTest(); return gTestSuccess ? 0 : 255; } diff --git a/tests/vboot_api_kernel4_tests.c b/tests/vboot_api_kernel4_tests.c index 41e58c8e..1edea80a 100644 --- a/tests/vboot_api_kernel4_tests.c +++ b/tests/vboot_api_kernel4_tests.c @@ -34,6 +34,8 @@ static uint32_t rkr_version; static uint32_t new_version; static struct RollbackSpaceFwmp rfr_fwmp; static int rkr_retval, rkw_retval, rkl_retval, rfr_retval; +static uint8_t gaf_val, saf_val; +static int gah_retval, gaf_retval, saf_retval; static VbError_t vbboot_retval; /* Reset mock data (for use before each test) */ @@ -65,6 +67,9 @@ static void ResetMocks(void) ecsync_retval = VBERROR_SUCCESS; rkr_version = new_version = 0x10002; rkr_retval = rkw_retval = rkl_retval = VBERROR_SUCCESS; + gaf_val = saf_val = 0; + gaf_retval = saf_retval = VBERROR_SUCCESS; + gah_retval = 0; vbboot_retval = VBERROR_SUCCESS; } @@ -87,6 +92,28 @@ VbError_t VbExEcRunningRW(int devidx, int *in_rw) return ecsync_retval; } +int VbExTrustEC(int devidx) +{ + return !ecsync_retval; +} + +int vb2ex_get_alt_os_hotkey(void) +{ + return gah_retval; +} + +uint32_t GetAltOSFlags(uint8_t *val) +{ + *val = gaf_val; + return gaf_retval; +} + +uint32_t SetAltOSFlags(uint8_t val) +{ + saf_val = val; + return saf_retval; +} + uint32_t RollbackKernelRead(uint32_t *version) { *version = rkr_version; @@ -142,6 +169,14 @@ VbError_t VbBootRecovery(struct vb2_context *ctx, VbCommonParams *cparams) return vbboot_retval; } +VbError_t VbBootAltOS(struct vb2_context *ctx, VbCommonParams *cparams) +{ + if (vbboot_retval == -4) + return VBERROR_SIMULATED; + + return vbboot_retval; +} + static void test_slk(VbError_t retval, int recovery_reason, const char *desc) { uint32_t u; @@ -258,7 +293,86 @@ static void VbSlkTest(void) // todo: rkr/w/l fail ignored if recovery + /* Boot alt OS */ + uint32_t oprom_needed; + /* + * Enable request without OPROM + * oprom matters: Y + * oprom loaded: N + * current hotkey: Y + * stored hotkey: N + * enable request: Y + * disable request: N + * enabled: N + * result: request reboot for OPROM + */ + ResetMocks(); + shared->flags |= VBSD_OPROM_MATTERS; + gah_retval = 1; + VbNvSet(&vnc, VBNV_ENABLE_ALT_OS_REQUEST, 1); + VbNvTeardown(&vnc); + test_slk(VBERROR_VGA_OPROM_MISMATCH, 0, "Alt OS doesn't request OPROM"); + VbNvGet(&vnc, VBNV_OPROM_NEEDED, &oprom_needed); + TEST_EQ(oprom_needed, 1, "Alt OS doesn't request OPROM"); + + /* + * Enable request with OPROM + * oprom matters: Y + * oprom loaded: Y + * current hotkey: N + * stored hotkey: Y + * enable request: Y + * disable request: N + * enabled: N + * result: run VbBootAltOS + */ + ResetMocks(); + shared->flags |= VBSD_OPROM_MATTERS; + shared->flags |= VBSD_OPROM_LOADED; + gaf_val |= ALT_OS_HOTKEY; + VbNvSet(&vnc, VBNV_ENABLE_ALT_OS_REQUEST, 1); + VbNvTeardown(&vnc); + vbboot_retval = -4; + test_slk(VBERROR_SIMULATED, 0, "Alt OS enable bad"); + + /* + * Enabled with OPROM + * oprom matters: Y + * oprom loaded: Y + * current hotkey: N + * stored hotkey: Y + * enable request: N + * disable request: N + * enabled: Y + * result: run VbBootAltOS + */ + ResetMocks(); + shared->flags |= VBSD_OPROM_MATTERS; + shared->flags |= VBSD_OPROM_LOADED; + gaf_val |= ALT_OS_ENABLE; + vbboot_retval = -4; + test_slk(VBERROR_SIMULATED, 0, "Alt OS boot bad"); + + /* + * Disable request without OPROM + * oprom matters: Y + * oprom loaded: N + * current hotkey: N + * stored hotkey: N + * enable request: N + * disable request: Y + * enabled: Y + * result: disable Alt OS and boot normal mode + */ + ResetMocks(); + shared->flags |= VBSD_OPROM_MATTERS; + gaf_val |= ALT_OS_ENABLE; + VbNvSet(&vnc, VBNV_DISABLE_ALT_OS_REQUEST, 1); + VbNvTeardown(&vnc); + vbboot_retval = -1; + test_slk(VBERROR_SIMULATED, 0, "Alt OS incorrect boot after disable"); + TEST_FALSE(saf_val & ALT_OS_ENABLE, "Alt OS doesn't disable"); } int main(void) |