From b7b419d2fe421aff64fff31f785da8e26f5bf867 Mon Sep 17 00:00:00 2001 From: Hsuan Ting Chen Date: Tue, 10 Mar 2020 14:47:38 +0800 Subject: vboot: Implement dev mode boot options for menu UI Add dev default boot retrieval and dev boot related allowance check api in 2misc along with their tests in vb2_misc_tests: - vb2_get_dev_boot_target - vb2_dev_boot_allowed - vb2_dev_boot_legacy_allowed - vb2_dev_boot_usb_allowed Implement parts of vb2_developer_menu functionalities along with tests: - Retrieve the default boot option. - Try to boot with the specified option. BRANCH=none BUG=b:146399181, chromium:1033815 TEST=USE="menu_ui" emerge-nami vboot_reference depthcharge TEST=make runtests Change-Id: Ie82076f93b86ba5abe26a9e3e25076892684855d Signed-off-by: Hsuan Ting Chen Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/2094508 Reviewed-by: Joel Kitching --- firmware/2lib/2misc.c | 40 +++++++++ firmware/2lib/2ui.c | 27 +++++- firmware/2lib/include/2misc.h | 44 ++++++++++ tests/vb2_misc_tests.c | 119 ++++++++++++++++++++++++++ tests/vb2_ui_tests.c | 189 +++++++++++++++++++++++++++++++++++++++++- 5 files changed, 416 insertions(+), 3 deletions(-) diff --git a/firmware/2lib/2misc.c b/firmware/2lib/2misc.c index faf2da7e..ac9d0f24 100644 --- a/firmware/2lib/2misc.c +++ b/firmware/2lib/2misc.c @@ -495,3 +495,43 @@ void vb2api_export_vbsd(struct vb2_context *ctx, void *dest) } _Static_assert(VB2_VBSD_SIZE == sizeof(VbSharedDataHeader), "VB2_VBSD_SIZE incorrect"); + +enum vb2_dev_default_boot vb2_get_dev_boot_target( + struct vb2_context *ctx) +{ + struct vb2_gbb_header *gbb = vb2_get_gbb(ctx); + + if (gbb->flags & VB2_GBB_FLAG_DEFAULT_DEV_BOOT_LEGACY) + return VB2_DEV_DEFAULT_BOOT_LEGACY; + + return vb2_nv_get(ctx, VB2_NV_DEV_DEFAULT_BOOT); +} + +int vb2_dev_boot_allowed(struct vb2_context *ctx) +{ + struct vb2_gbb_header *gbb = vb2_get_gbb(ctx); + + if (vb2_secdata_fwmp_get_flag(ctx, VB2_SECDATA_FWMP_DEV_DISABLE_BOOT)) + return !!(gbb->flags & VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON); + + return 1; +} + +int vb2_dev_boot_legacy_allowed(struct vb2_context *ctx) +{ + struct vb2_gbb_header *gbb = vb2_get_gbb(ctx); + + return vb2_nv_get(ctx, VB2_NV_DEV_BOOT_LEGACY) || + (gbb->flags & VB2_GBB_FLAG_FORCE_DEV_BOOT_LEGACY) || + vb2_secdata_fwmp_get_flag(ctx, + VB2_SECDATA_FWMP_DEV_ENABLE_LEGACY); +} + +int vb2_dev_boot_usb_allowed(struct vb2_context *ctx) +{ + struct vb2_gbb_header *gbb = vb2_get_gbb(ctx); + + return vb2_nv_get(ctx, VB2_NV_DEV_BOOT_USB) || + (gbb->flags & VB2_GBB_FLAG_FORCE_DEV_BOOT_USB) || + vb2_secdata_fwmp_get_flag(ctx, VB2_SECDATA_FWMP_DEV_ENABLE_USB); +} diff --git a/firmware/2lib/2ui.c b/firmware/2lib/2ui.c index b6892cb9..25037c94 100644 --- a/firmware/2lib/2ui.c +++ b/firmware/2lib/2ui.c @@ -7,19 +7,42 @@ #include "2api.h" #include "2common.h" +#include "2misc.h" +#include "2nvstorage.h" +#include "2return_codes.h" +#include "2secdata.h" #include "2ui.h" +#include "vboot_kernel.h" /*****************************************************************************/ /* Entry points */ vb2_error_t vb2_developer_menu(struct vb2_context *ctx) { + enum vb2_dev_default_boot default_boot; + /* TODO(roccochen): Init, wait for user, and boot. */ vb2ex_display_ui(VB2_SCREEN_BLANK, 0); - while (1); + /* If dev mode was disabled, loop forever. */ + if (!vb2_dev_boot_allowed(ctx)) + while (1); - return VB2_SUCCESS; + /* Boot from the default option. */ + default_boot = vb2_get_dev_boot_target(ctx); + + /* Boot legacy does not return on success */ + if (default_boot == VB2_DEV_DEFAULT_BOOT_LEGACY && + vb2_dev_boot_legacy_allowed(ctx) && + VbExLegacy(VB_ALTFW_DEFAULT) == VB2_SUCCESS) + return VB2_SUCCESS; + + if (default_boot == VB2_DEV_DEFAULT_BOOT_USB && + vb2_dev_boot_usb_allowed(ctx) && + VbTryLoadKernel(ctx, VB_DISK_FLAG_REMOVABLE) == VB2_SUCCESS) + return VB2_SUCCESS; + + return VbTryLoadKernel(ctx, VB_DISK_FLAG_FIXED); } vb2_error_t vb2_broken_recovery_menu(struct vb2_context *ctx) diff --git a/firmware/2lib/include/2misc.h b/firmware/2lib/include/2misc.h index b80f6906..94a93d40 100644 --- a/firmware/2lib/include/2misc.h +++ b/firmware/2lib/include/2misc.h @@ -210,4 +210,48 @@ int vb2_allow_recovery(struct vb2_context *ctx); */ void vb2_clear_recovery(struct vb2_context *ctx); +/** + * Return the developer mode default boot option; see vb2_dev_default_boot. + * + * @param ctx Vboot context + * @return The developer mode default boot option, or + * VB2_DEV_DEFAULT_BOOT_DISK if not specified. + */ +enum vb2_dev_default_boot vb2_get_dev_boot_target(struct vb2_context *ctx); + +/** + * Determine if developer mode is allowed. + * + * Developer boot is not allowed if and only if FWMP_DEV_DISABLE_BOOT is set and + * GBB_FORCE_DEV_SWITCH_ON is not set. + * + * @param ctx Vboot context + * @return 1 if allowed, or 0 otherwise. + */ +int vb2_dev_boot_allowed(struct vb2_context *ctx); + +/** + * Determine if booting from legacy BIOS is allowed. + * + * Legacy BIOS is allowed if one of the legacy-related flags is set: + * VB2_NV_DEV_BOOT_LEGACY, VB2_GBB_FLAG_FORCE_DEV_BOOT_LEGACY, and + * VB2_SECDATA_FWMP_DEV_ENABLE_LEGACY. + * + * @param ctx Vboot context + * @return 1 if allowed, or 0 otherwise. + */ +int vb2_dev_boot_legacy_allowed(struct vb2_context *ctx); + +/** + * Determine if booting from USB or SD card is allowed. + * + * Booting from USB is allowed if one of the USB-related flags is set: + * VB2_NV_DEV_BOOT_USB, VB2_GBB_FLAG_FORCE_DEV_BOOT_USB, and + * VB2_SECDATA_FWMP_DEV_ENABLE_USB. + * + * @param ctx Vboot context + * @return 1 if allowed, or 0 otherwise. + */ +int vb2_dev_boot_usb_allowed(struct vb2_context *ctx); + #endif /* VBOOT_REFERENCE_2MISC_H_ */ diff --git a/tests/vb2_misc_tests.c b/tests/vb2_misc_tests.c index 89f534d7..1d8b42ed 100644 --- a/tests/vb2_misc_tests.c +++ b/tests/vb2_misc_tests.c @@ -21,6 +21,7 @@ static uint8_t workbuf2[VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE] static struct vb2_context *ctx; static struct vb2_shared_data *sd; static struct vb2_gbb_header gbb; +static struct vb2_secdata_fwmp *fwmp; /* Mocked function data */ static enum vb2_resource_index mock_resource_index; @@ -39,6 +40,7 @@ static void reset_common_data(void) "vb2api_init failed"); sd = vb2_get_sd(ctx); + sd->status = VB2_SD_STATUS_SECDATA_FWMP_INIT; memset(&gbb, 0, sizeof(gbb)); @@ -47,6 +49,8 @@ static void reset_common_data(void) vb2api_secdata_firmware_create(ctx); vb2_secdata_firmware_init(ctx); + fwmp = (struct vb2_secdata_fwmp *)&ctx->secdata_fwmp; + mock_tpm_clear_called = 0; mock_tpm_clear_retval = VB2_SUCCESS; allow_recovery_retval = 0; @@ -792,6 +796,119 @@ static void get_recovery_reason_tests(void) TEST_EQ(vb2api_get_recovery_reason(ctx), 4, "correct recovery reason"); } +static void dev_default_boot_tests(void) +{ + /* No default boot */ + reset_common_data(); + TEST_EQ(vb2_get_dev_boot_target(ctx), VB2_DEV_DEFAULT_BOOT_DISK, + "no default boot, boot disk"); + + /* Set boot legacy by GBB */ + reset_common_data(); + gbb.flags |= VB2_GBB_FLAG_DEFAULT_DEV_BOOT_LEGACY; + vb2_nv_set(ctx, VB2_NV_DEV_DEFAULT_BOOT, VB2_DEV_DEFAULT_BOOT_USB); + TEST_EQ(vb2_get_dev_boot_target(ctx), VB2_DEV_DEFAULT_BOOT_LEGACY, + "GBB set default boot legacy"); + + /* Boot from disk */ + reset_common_data(); + vb2_nv_set(ctx, VB2_NV_DEV_DEFAULT_BOOT, VB2_DEV_DEFAULT_BOOT_DISK); + TEST_EQ(vb2_get_dev_boot_target(ctx), VB2_DEV_DEFAULT_BOOT_DISK, + "set default boot disk"); + + /* Boot from usb */ + reset_common_data(); + vb2_nv_set(ctx, VB2_NV_DEV_DEFAULT_BOOT, VB2_DEV_DEFAULT_BOOT_USB); + TEST_EQ(vb2_get_dev_boot_target(ctx), + VB2_DEV_DEFAULT_BOOT_USB, "set default boot usb"); + + /* Boot legacy */ + reset_common_data(); + vb2_nv_set(ctx, VB2_NV_DEV_DEFAULT_BOOT, VB2_DEV_DEFAULT_BOOT_LEGACY); + TEST_EQ(vb2_get_dev_boot_target(ctx), + VB2_DEV_DEFAULT_BOOT_LEGACY, "set default boot legacy"); +} + +static void dev_boot_allowed_tests(void) +{ + /* Dev boot - allowed by default */ + reset_common_data(); + TEST_EQ(vb2_dev_boot_allowed(ctx), 1, "dev boot - allowed by default"); + + /* Dev boot - disabled by FWMP */ + reset_common_data(); + fwmp->flags |= VB2_SECDATA_FWMP_DEV_DISABLE_BOOT; + TEST_EQ(vb2_dev_boot_allowed(ctx), 0, "dev boot - FWMP disabled"); + + /* Dev boot - force enabled by GBB */ + reset_common_data(); + fwmp->flags |= VB2_SECDATA_FWMP_DEV_DISABLE_BOOT; + gbb.flags |= VB2_GBB_FLAG_FORCE_DEV_SWITCH_ON; + TEST_EQ(vb2_dev_boot_allowed(ctx), 1, "dev boot - GBB force dev on"); + + /* Legacy boot - not allowed by default */ + reset_common_data(); + TEST_EQ(vb2_dev_boot_legacy_allowed(ctx), 0, + "dev boot legacy - not allowed by default"); + + /* Legacy boot - enabled by nvdata */ + reset_common_data(); + vb2_nv_set(ctx, VB2_NV_DEV_BOOT_LEGACY, 1); + TEST_EQ(vb2_dev_boot_legacy_allowed(ctx), 1, + "dev boot legacy - nvdata enabled"); + + /* Legacy boot - enabled by FWMP */ + reset_common_data(); + fwmp->flags |= VB2_SECDATA_FWMP_DEV_ENABLE_LEGACY; + TEST_EQ(vb2_dev_boot_legacy_allowed(ctx), 1, + "dev boot legacy - secdata enabled"); + + /* Legacy boot - force enabled by GBB */ + reset_common_data(); + gbb.flags |= VB2_GBB_FLAG_FORCE_DEV_BOOT_LEGACY; + TEST_EQ(vb2_dev_boot_legacy_allowed(ctx), 1, + "dev boot legacy - GBB force enabled"); + + /* Legacy boot - set all flags */ + reset_common_data(); + vb2_nv_set(ctx, VB2_NV_DEV_BOOT_LEGACY, 1); + fwmp->flags |= VB2_SECDATA_FWMP_DEV_ENABLE_LEGACY; + gbb.flags |= VB2_GBB_FLAG_FORCE_DEV_BOOT_LEGACY; + TEST_EQ(vb2_dev_boot_legacy_allowed(ctx), 1, + "dev boot legacy - all flags set"); + + /* USB boot - not allowed by default */ + reset_common_data(); + TEST_EQ(vb2_dev_boot_usb_allowed(ctx), 0, + "dev boot usb - not allowed by default"); + + /* USB boot - enabled by nvdata */ + reset_common_data(); + vb2_nv_set(ctx, VB2_NV_DEV_BOOT_USB, 1); + TEST_EQ(vb2_dev_boot_usb_allowed(ctx), 1, "dev boot usb -" + " nvdata enabled"); + + /* USB boot - enabled by FWMP */ + reset_common_data(); + fwmp->flags |= VB2_SECDATA_FWMP_DEV_ENABLE_USB; + TEST_EQ(vb2_dev_boot_usb_allowed(ctx), 1, + "dev boot usb - secdata enabled"); + + /* USB boot - force enabled by GBB */ + reset_common_data(); + gbb.flags |= VB2_GBB_FLAG_FORCE_DEV_BOOT_USB; + TEST_EQ(vb2_dev_boot_usb_allowed(ctx), 1, + "dev boot usb - GBB force enabled"); + + /* USB boot - set all flags */ + reset_common_data(); + vb2_nv_set(ctx, VB2_NV_DEV_BOOT_USB, 1); + fwmp->flags |= VB2_SECDATA_FWMP_DEV_ENABLE_USB; + gbb.flags |= VB2_GBB_FLAG_FORCE_DEV_BOOT_USB; + TEST_EQ(vb2_dev_boot_usb_allowed(ctx), 1, + "dev boot usb - all flags set"); +} + int main(int argc, char* argv[]) { init_workbuf_tests(); @@ -805,6 +922,8 @@ int main(int argc, char* argv[]) need_reboot_for_display_tests(); clear_recovery_tests(); get_recovery_reason_tests(); + dev_default_boot_tests(); + dev_boot_allowed_tests(); return gTestSuccess ? 0 : 255; } diff --git a/tests/vb2_ui_tests.c b/tests/vb2_ui_tests.c index ffcb7d60..eb7de299 100644 --- a/tests/vb2_ui_tests.c +++ b/tests/vb2_ui_tests.c @@ -5,13 +5,200 @@ * Tests for developer and recovery mode UIs. */ +#include "2api.h" +#include "2common.h" +#include "2misc.h" +#include "2nvstorage.h" +#include "2ui.h" #include "test_common.h" +#include "vboot_api.h" +#include "vboot_kernel.h" + +/* Mock data */ +static uint8_t workbuf[VB2_KERNEL_WORKBUF_RECOMMENDED_SIZE] + __attribute__((aligned(VB2_WORKBUF_ALIGN))); +static struct vb2_context *ctx; + +static enum vb2_screen mock_screens_displayed[64]; +static uint32_t mock_locales_displayed[64]; +static uint32_t mock_screens_count = 0; + +static enum vb2_dev_default_boot mock_default_boot; +static int mock_dev_boot_allowed; +static int mock_dev_boot_legacy_allowed; +static int mock_dev_boot_usb_allowed; + +static int mock_vbexlegacy_called; +static enum VbAltFwIndex_t mock_altfw_num; + +static vb2_error_t mock_vbtlk_retval[5]; +static uint32_t mock_vbtlk_expected_flag[5]; +static int mock_vbtlk_count; +static int mock_vbtlk_total; + +static void add_mock_vbtlk(vb2_error_t retval, uint32_t get_info_flags) +{ + if (mock_vbtlk_total >= ARRAY_SIZE(mock_vbtlk_retval) || + mock_vbtlk_total >= ARRAY_SIZE(mock_vbtlk_expected_flag)) { + TEST_TRUE(0, "Test failed as mock_vbtlk ran out of entries!"); + return; + } + + mock_vbtlk_retval[mock_vbtlk_total] = retval; + mock_vbtlk_expected_flag[mock_vbtlk_total] = get_info_flags; + mock_vbtlk_total++; +} + +/* Reset mock data (for use before each test) */ +static void reset_common_data() +{ + TEST_SUCC(vb2api_init(workbuf, sizeof(workbuf), &ctx), + "vb2api_init failed"); + vb2_nv_init(ctx); + + memset(mock_screens_displayed, 0, sizeof(mock_screens_displayed)); + mock_screens_count = 0; + + mock_default_boot = VB2_DEV_DEFAULT_BOOT_DISK; + mock_dev_boot_allowed = 1; + mock_dev_boot_legacy_allowed = 0; + mock_dev_boot_usb_allowed = 0; + + mock_vbexlegacy_called = 0; + mock_altfw_num = -100; + + memset(mock_vbtlk_retval, 0, sizeof(mock_vbtlk_retval)); + memset(mock_vbtlk_expected_flag, 0, sizeof(mock_vbtlk_expected_flag)); + mock_vbtlk_count = 0; + mock_vbtlk_total = 0; +} + +/* Mock functions */ + +enum vb2_dev_default_boot vb2_get_dev_boot_target(struct vb2_context *c) +{ + return mock_default_boot; +} + +int vb2_dev_boot_allowed(struct vb2_context *c) +{ + return mock_dev_boot_allowed; +} + +int vb2_dev_boot_legacy_allowed(struct vb2_context *c) +{ + return mock_dev_boot_legacy_allowed; +} + +int vb2_dev_boot_usb_allowed(struct vb2_context *c) +{ + return mock_dev_boot_usb_allowed; +} + +vb2_error_t VbExLegacy(enum VbAltFwIndex_t altfw_num) +{ + mock_vbexlegacy_called++; + mock_altfw_num = altfw_num; + + return VB2_SUCCESS; +} + +vb2_error_t VbTryLoadKernel(struct vb2_context *c, uint32_t get_info_flags) +{ + if (mock_vbtlk_count >= mock_vbtlk_total) { + TEST_TRUE(0, " VbTryLoadKernel called too many times."); + return VB2_ERROR_MOCK; + } + + TEST_EQ(mock_vbtlk_expected_flag[mock_vbtlk_count], get_info_flags, + " unexpected get_info_flags"); + + return mock_vbtlk_retval[mock_vbtlk_count++]; +} + +vb2_error_t vb2ex_display_ui(enum vb2_screen screen, uint32_t locale) +{ + VB2_DEBUG("screens %d: screen = %#x, locale = %u\n", + mock_screens_count, screen, locale); + + if (mock_screens_count >= ARRAY_SIZE(mock_screens_displayed) || + mock_screens_count >= ARRAY_SIZE(mock_locales_displayed)) { + TEST_TRUE(0, "Test failed as mock vb2ex_display_ui ran out of" + " entries!"); + return VB2_ERROR_MOCK; + } + + mock_screens_displayed[mock_screens_count] = screen; + mock_locales_displayed[mock_screens_count] = locale; + mock_screens_count++; + + return VB2_SUCCESS; +} /* Tests */ static void developer_tests(void) { - /* TODO(roccochen) */ + /* Proceed */ + reset_common_data(); + add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_FIXED); + TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS, "proceed"); + TEST_EQ(mock_screens_displayed[0], VB2_SCREEN_BLANK, + " final blank screen"); + TEST_EQ(mock_screens_count, 1, " no extra screens"); + TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST), 0, + " recovery reason"); + TEST_EQ(mock_vbtlk_count, mock_vbtlk_total, " used up mock_vbtlk"); + + /* Proceed to legacy */ + reset_common_data(); + mock_default_boot = VB2_DEV_DEFAULT_BOOT_LEGACY; + mock_dev_boot_legacy_allowed = 1; + TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS, "proceed to legacy"); + TEST_EQ(mock_vbexlegacy_called, 1, " try legacy"); + TEST_EQ(mock_altfw_num, 0, " check altfw_num"); + TEST_EQ(mock_screens_displayed[0], VB2_SCREEN_BLANK, + " final blank screen"); + TEST_EQ(mock_screens_count, 1, " no extra screens"); + TEST_EQ(mock_vbtlk_count, mock_vbtlk_total, " used up mock_vbtlk"); + + /* Proceed to legacy only if enabled */ + reset_common_data(); + add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_FIXED); + mock_default_boot = VB2_DEV_DEFAULT_BOOT_LEGACY; + TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS, + "default legacy not enabled"); + TEST_EQ(mock_vbexlegacy_called, 0, " not legacy"); + TEST_EQ(mock_screens_displayed[0], VB2_SCREEN_BLANK, + " final blank screen"); + TEST_EQ(mock_screens_count, 1, " no extra screens"); + TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST), 0, + " no recovery"); + TEST_EQ(mock_vbtlk_count, mock_vbtlk_total, " used up mock_vbtlk"); + + /* Proceed to usb */ + reset_common_data(); + add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_REMOVABLE); + mock_default_boot = VB2_DEV_DEFAULT_BOOT_USB; + mock_dev_boot_usb_allowed = 1; + TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS, "proceed to usb"); + TEST_EQ(mock_screens_displayed[0], VB2_SCREEN_BLANK, + " final blank screen"); + TEST_EQ(mock_screens_count, 1, " no extra screens"); + TEST_EQ(mock_vbtlk_count, mock_vbtlk_total, " used up mock_vbtlk"); + + /* Proceed to usb only if enabled */ + reset_common_data(); + add_mock_vbtlk(VB2_SUCCESS, VB_DISK_FLAG_FIXED); + mock_default_boot = VB2_DEV_DEFAULT_BOOT_USB; + TEST_EQ(vb2_developer_menu(ctx), VB2_SUCCESS, + "default usb not enabled"); + TEST_EQ(mock_screens_displayed[0], VB2_SCREEN_BLANK, + " final blank screen"); + TEST_EQ(mock_screens_count, 1, " no extra screens"); + TEST_EQ(vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST), 0, + " no recovery"); + TEST_EQ(mock_vbtlk_count, mock_vbtlk_total, " used up mock_vbtlk"); } static void broken_recovery_tests(void) -- cgit v1.2.1