| /* Copyright (c) 2012 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. |
| * |
| * Tests for vboot_api_init |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #include "gbb_header.h" |
| #include "host_common.h" |
| #include "rollback_index.h" |
| #include "test_common.h" |
| #include "vboot_common.h" |
| #include "vboot_nvstorage.h" |
| #include "vboot_struct.h" |
| |
| /* Mock data */ |
| static VbCommonParams cparams; |
| static VbInitParams iparams; |
| static VbNvContext vnc; |
| static uint8_t shared_data[VB_SHARED_DATA_MIN_SIZE]; |
| static VbSharedDataHeader* shared = (VbSharedDataHeader*)shared_data; |
| static uint64_t mock_timer; |
| static int rollback_s3_retval; |
| static int nv_write_called; |
| static GoogleBinaryBlockHeader gbb; |
| static int mock_virt_dev_sw; |
| static uint32_t mock_tpm_version; |
| static uint32_t mock_rfs_retval; |
| |
| /* Reset mock data (for use before each test) */ |
| static void ResetMocks(void) { |
| Memset(&cparams, 0, sizeof(cparams)); |
| cparams.shared_data_size = sizeof(shared_data); |
| cparams.shared_data_blob = shared_data; |
| cparams.gbb_data = &gbb; |
| |
| Memset(&gbb, 0, sizeof(gbb)); |
| gbb.major_version = GBB_MAJOR_VER; |
| gbb.minor_version = GBB_MINOR_VER; |
| gbb.flags = 0; |
| |
| Memset(&iparams, 0, sizeof(iparams)); |
| |
| Memset(&vnc, 0, sizeof(vnc)); |
| VbNvSetup(&vnc); |
| VbNvTeardown(&vnc); /* So CRC gets generated */ |
| |
| Memset(&shared_data, 0, sizeof(shared_data)); |
| VbSharedDataInit(shared, sizeof(shared_data)); |
| |
| mock_timer = 10; |
| rollback_s3_retval = TPM_SUCCESS; |
| nv_write_called = 0; |
| |
| mock_virt_dev_sw = 0; |
| mock_tpm_version = 0x10001; |
| mock_rfs_retval = 0; |
| } |
| |
| /****************************************************************************/ |
| /* Mocked verification functions */ |
| |
| VbError_t VbExNvStorageRead(uint8_t* buf) { |
| Memcpy(buf, vnc.raw, sizeof(vnc.raw)); |
| return VBERROR_SUCCESS; |
| } |
| |
| VbError_t VbExNvStorageWrite(const uint8_t* buf) { |
| nv_write_called = 1; |
| Memcpy(vnc.raw, buf, sizeof(vnc.raw)); |
| return VBERROR_SUCCESS; |
| } |
| |
| uint64_t VbExGetTimer(void) { |
| /* Exponential-ish rather than linear time, so that subtracting any |
| * two mock values will yield a unique result. */ |
| uint64_t new_timer = mock_timer * 2 + 1; |
| VbAssert(new_timer > mock_timer); /* Make sure we don't overflow */ |
| mock_timer = new_timer; |
| return mock_timer; |
| } |
| |
| uint32_t RollbackS3Resume(void) { |
| return rollback_s3_retval; |
| } |
| |
| uint32_t RollbackFirmwareSetup(int recovery_mode, int is_hw_dev, |
| int disable_dev_request, |
| int clear_tpm_owner_request, |
| /* two outputs on success */ |
| int *is_virt_dev, uint32_t *version) { |
| *is_virt_dev = mock_virt_dev_sw; |
| *version = mock_tpm_version; |
| return mock_rfs_retval; |
| } |
| |
| /****************************************************************************/ |
| /* Test VbInit() and check expected return value and recovery reason */ |
| static void TestVbInit(VbError_t expected_retval, |
| uint8_t expected_recovery, const char* desc) { |
| uint32_t rr = 256; |
| |
| TEST_EQ(VbInit(&cparams, &iparams), expected_retval, desc); |
| VbNvGet(&vnc, VBNV_RECOVERY_REQUEST, &rr); |
| TEST_EQ(rr, expected_recovery, " (recovery request)"); |
| } |
| |
| /****************************************************************************/ |
| |
| static void VbInitTest(void) { |
| uint32_t u; |
| |
| /* Test passing in too small a shared data area */ |
| ResetMocks(); |
| cparams.shared_data_size = VB_SHARED_DATA_MIN_SIZE - 1; |
| TestVbInit(VBERROR_INIT_SHARED_DATA, 0, "Shared data too small"); |
| |
| /* Normal call; dev=0 rec=0 */ |
| ResetMocks(); |
| TestVbInit(0, 0, "Normal call"); |
| TEST_EQ(shared->timer_vb_init_enter, 21, " time enter"); |
| TEST_EQ(shared->timer_vb_init_exit, 43, " time exit"); |
| TEST_EQ(shared->flags, 0, " shared flags"); |
| TEST_EQ(iparams.out_flags, 0, " out flags"); |
| TEST_EQ(nv_write_called, 0, " NV write not called since nothing changed"); |
| |
| /* If NV data is trashed, we initialize it */ |
| ResetMocks(); |
| VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, 123); |
| /* Note that we're not doing a VbNvTeardown(), so the CRC hasn't |
| * been regenerated yet. So VbInit() should ignore the corrupted |
| * recovery value and boot normally. */ |
| TestVbInit(0, 0, "NV data trashed"); |
| TEST_EQ(nv_write_called, 1, " NV write called"); |
| |
| /* Test boot switch flags which are just passed through to shared |
| * flags, and don't have an effect on VbInit(). */ |
| ResetMocks(); |
| iparams.flags = VB_INIT_FLAG_WP_ENABLED; |
| TestVbInit(0, 0, "Flags test WP"); |
| TEST_EQ(shared->flags, VBSD_BOOT_FIRMWARE_WP_ENABLED, " shared flags WP"); |
| |
| ResetMocks(); |
| iparams.flags = VB_INIT_FLAG_RO_NORMAL_SUPPORT; |
| TestVbInit(0, 0, " flags test RO normal"); |
| TEST_EQ(shared->flags, VBSD_BOOT_RO_NORMAL_SUPPORT, |
| " shared flags RO normal"); |
| |
| /* S3 resume */ |
| ResetMocks(); |
| iparams.flags = VB_INIT_FLAG_S3_RESUME; |
| VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, 123); |
| VbNvTeardown(&vnc); |
| /* S3 resume doesn't clear the recovery request (or act on it) */ |
| TestVbInit(0, 123, "S3 resume"); |
| TEST_EQ(shared->flags, VBSD_BOOT_S3_RESUME, " shared flags S3"); |
| TEST_EQ(iparams.out_flags, 0, " out flags"); |
| TEST_EQ(shared->recovery_reason, 0, " S3 doesn't look at recovery request"); |
| |
| /* S3 resume with TPM resume error */ |
| ResetMocks(); |
| iparams.flags = VB_INIT_FLAG_S3_RESUME; |
| rollback_s3_retval = 1; |
| /* S3 resume doesn't clear the recovery request (or act on it) */ |
| TestVbInit(VBERROR_TPM_S3_RESUME, 0, "S3 resume rollback error"); |
| |
| /* Normal boot doesn't care about TPM resume error because it |
| * doesn't call RollbackS3Resume() */ |
| ResetMocks(); |
| rollback_s3_retval = 1; |
| TestVbInit(0, 0, "Normal doesn't S3 resume"); |
| |
| /* S3 resume with debug reset */ |
| ResetMocks(); |
| iparams.flags = VB_INIT_FLAG_S3_RESUME; |
| VbNvSet(&vnc, VBNV_DEBUG_RESET_MODE, 1); |
| VbNvTeardown(&vnc); |
| TestVbInit(0, 0, "S3 debug reset"); |
| TEST_EQ(iparams.out_flags, VB_INIT_OUT_S3_DEBUG_BOOT, " out flags"); |
| VbNvGet(&vnc, VBNV_DEBUG_RESET_MODE, &u); |
| TEST_EQ(u, 0, " S3 clears nv debug reset mode"); |
| |
| /* Normal boot clears S3 debug reset mode, but doesn't set output flag */ |
| ResetMocks(); |
| VbNvSet(&vnc, VBNV_DEBUG_RESET_MODE, 1); |
| VbNvTeardown(&vnc); |
| TestVbInit(0, 0, "Normal with debug reset mode"); |
| TEST_EQ(iparams.out_flags, 0, " out flags"); |
| VbNvGet(&vnc, VBNV_DEBUG_RESET_MODE, &u); |
| TEST_EQ(u, 0, " normal clears nv debug reset mode"); |
| |
| /* S3 resume with debug reset is a normal boot, so doesn't resume the TPM */ |
| ResetMocks(); |
| iparams.flags = VB_INIT_FLAG_S3_RESUME; |
| rollback_s3_retval = 1; |
| VbNvSet(&vnc, VBNV_DEBUG_RESET_MODE, 1); |
| VbNvTeardown(&vnc); |
| TestVbInit(0, 0, "S3 debug reset rollback error"); |
| |
| /* Developer mode */ |
| ResetMocks(); |
| iparams.flags = VB_INIT_FLAG_DEV_SWITCH_ON; |
| TestVbInit(0, 0, "Dev mode on"); |
| TEST_EQ(shared->recovery_reason, 0, " recovery reason"); |
| TEST_EQ(iparams.out_flags, |
| VB_INIT_OUT_CLEAR_RAM | |
| VB_INIT_OUT_ENABLE_DISPLAY | |
| VB_INIT_OUT_ENABLE_USB_STORAGE | |
| VB_INIT_OUT_ENABLE_DEVELOPER | |
| VB_INIT_OUT_ENABLE_ALTERNATE_OS, " out flags"); |
| TEST_EQ(shared->flags, VBSD_BOOT_DEV_SWITCH_ON, " shared flags"); |
| |
| /* Developer mode forced by GBB flag */ |
| ResetMocks(); |
| iparams.flags = 0; |
| gbb.flags = GBB_FLAG_FORCE_DEV_SWITCH_ON; |
| TestVbInit(0, 0, "Dev mode via GBB"); |
| TEST_EQ(shared->recovery_reason, 0, " recovery reason"); |
| TEST_EQ(iparams.out_flags, |
| VB_INIT_OUT_CLEAR_RAM | |
| VB_INIT_OUT_ENABLE_DISPLAY | |
| VB_INIT_OUT_ENABLE_USB_STORAGE | |
| VB_INIT_OUT_ENABLE_DEVELOPER | |
| VB_INIT_OUT_ENABLE_ALTERNATE_OS, " out flags"); |
| TEST_EQ(shared->flags, VBSD_BOOT_DEV_SWITCH_ON, " shared flags"); |
| |
| /* Recovery mode from NV storage */ |
| ResetMocks(); |
| VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, 123); |
| VbNvTeardown(&vnc); |
| TestVbInit(0, 0, "Recovery mode - from nv"); |
| TEST_EQ(shared->recovery_reason, 123, " recovery reason"); |
| TEST_EQ(iparams.out_flags, |
| VB_INIT_OUT_ENABLE_RECOVERY | |
| VB_INIT_OUT_CLEAR_RAM | |
| VB_INIT_OUT_ENABLE_DISPLAY | |
| VB_INIT_OUT_ENABLE_USB_STORAGE, " out flags"); |
| TEST_EQ(shared->flags, 0, " shared flags"); |
| |
| /* Recovery mode from recovery button */ |
| ResetMocks(); |
| iparams.flags = VB_INIT_FLAG_REC_BUTTON_PRESSED; |
| TestVbInit(0, 0, "Recovery mode - button"); |
| TEST_EQ(shared->recovery_reason, VBNV_RECOVERY_RO_MANUAL, |
| " recovery reason"); |
| TEST_EQ(iparams.out_flags, |
| VB_INIT_OUT_ENABLE_RECOVERY | |
| VB_INIT_OUT_CLEAR_RAM | |
| VB_INIT_OUT_ENABLE_DISPLAY | |
| VB_INIT_OUT_ENABLE_USB_STORAGE, " out flags"); |
| TEST_EQ(shared->flags, VBSD_BOOT_REC_SWITCH_ON, " shared flags"); |
| |
| /* Recovery button reason supersedes NV reason */ |
| ResetMocks(); |
| iparams.flags = VB_INIT_FLAG_REC_BUTTON_PRESSED; |
| VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, 123); |
| VbNvTeardown(&vnc); |
| TestVbInit(0, 0, "Recovery mode - button AND nv"); |
| TEST_EQ(shared->recovery_reason, VBNV_RECOVERY_RO_MANUAL, |
| " recovery reason"); |
| |
| /* Recovery mode from previous boot fail */ |
| ResetMocks(); |
| iparams.flags = VB_INIT_FLAG_PREVIOUS_BOOT_FAIL; |
| TestVbInit(0, 0, "Recovery mode - previous boot fail"); |
| TEST_EQ(shared->recovery_reason, VBNV_RECOVERY_RO_FIRMWARE, |
| " recovery reason"); |
| TEST_EQ(iparams.out_flags, |
| VB_INIT_OUT_ENABLE_RECOVERY | |
| VB_INIT_OUT_CLEAR_RAM | |
| VB_INIT_OUT_ENABLE_DISPLAY | |
| VB_INIT_OUT_ENABLE_USB_STORAGE, " out flags"); |
| TEST_EQ(shared->flags, 0, " shared flags"); |
| |
| /* Recovery mode from NV supersedes previous boot fail */ |
| ResetMocks(); |
| iparams.flags = VB_INIT_FLAG_PREVIOUS_BOOT_FAIL; |
| VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, 123); |
| VbNvTeardown(&vnc); |
| TestVbInit(0, 0, "Recovery mode - previous boot fail AND nv"); |
| TEST_EQ(shared->recovery_reason, 123, " recovery reason"); |
| |
| /* Dev + recovery = recovery */ |
| ResetMocks(); |
| iparams.flags = VB_INIT_FLAG_REC_BUTTON_PRESSED | VB_INIT_FLAG_DEV_SWITCH_ON; |
| TestVbInit(0, 0, "Recovery mode - button"); |
| TEST_EQ(shared->recovery_reason, VBNV_RECOVERY_RO_MANUAL, |
| " recovery reason"); |
| TEST_EQ(iparams.out_flags, |
| VB_INIT_OUT_ENABLE_RECOVERY | |
| VB_INIT_OUT_CLEAR_RAM | |
| VB_INIT_OUT_ENABLE_DISPLAY | |
| VB_INIT_OUT_ENABLE_USB_STORAGE, " out flags"); |
| TEST_EQ(shared->flags, |
| VBSD_BOOT_REC_SWITCH_ON | VBSD_BOOT_DEV_SWITCH_ON, " shared flags"); |
| } |
| |
| static void VbInitTestTPM(void) { |
| |
| /* Rollback setup needs to reboot */ |
| ResetMocks(); |
| mock_rfs_retval = TPM_E_MUST_REBOOT; |
| TestVbInit(VBERROR_TPM_REBOOT_REQUIRED, 0, "Rollback TPM reboot (rec=0)"); |
| ResetMocks(); |
| mock_rfs_retval = TPM_E_MUST_REBOOT; |
| iparams.flags = VB_INIT_FLAG_REC_BUTTON_PRESSED; |
| TestVbInit(VBERROR_TPM_REBOOT_REQUIRED, VBNV_RECOVERY_RO_TPM_REBOOT, |
| "Rollback TPM reboot, in recovery, first time"); |
| /* Ignore if we already tried rebooting */ |
| ResetMocks(); |
| mock_rfs_retval = TPM_E_MUST_REBOOT; |
| VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_REBOOT); |
| VbNvTeardown(&vnc); |
| TestVbInit(0, 0, "Rollback TPM reboot, in recovery, already retried"); |
| TEST_EQ(shared->fw_version_tpm, 0x10001, " shared fw_version_tpm"); |
| |
| /* Other rollback setup errors */ |
| ResetMocks(); |
| mock_rfs_retval = TPM_E_IOERROR; |
| mock_tpm_version = 0x20002; |
| TestVbInit(VBERROR_TPM_FIRMWARE_SETUP, VBNV_RECOVERY_RO_TPM_ERROR, |
| "Rollback TPM setup error - not in recovery"); |
| TEST_EQ(shared->fw_version_tpm, 0, " shared fw_version_tpm not set"); |
| ResetMocks(); |
| mock_rfs_retval = TPM_E_IOERROR; |
| VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_US_TEST); |
| VbNvTeardown(&vnc); |
| TestVbInit(0, 0, "Rollback TPM setup error ignored in recovery"); |
| TEST_EQ(shared->fw_version_tpm, 0x10001, " shared fw_version_tpm"); |
| |
| /* Virtual developer switch, but not enabled. */ |
| ResetMocks(); |
| iparams.flags = VB_INIT_FLAG_VIRTUAL_DEV_SWITCH; |
| TestVbInit(0, 0, "TPM Dev mode off"); |
| TEST_EQ(shared->recovery_reason, 0, " recovery reason"); |
| TEST_EQ(iparams.out_flags, 0, " out flags"); |
| TEST_EQ(shared->flags, VBSD_HONOR_VIRT_DEV_SWITCH, " shared flags"); |
| |
| /* Virtual developer switch, enabled. */ |
| ResetMocks(); |
| iparams.flags = VB_INIT_FLAG_VIRTUAL_DEV_SWITCH; |
| mock_virt_dev_sw = 1; |
| TestVbInit(0, 0, "TPM Dev mode on"); |
| TEST_EQ(shared->recovery_reason, 0, " recovery reason"); |
| TEST_EQ(iparams.out_flags, |
| VB_INIT_OUT_CLEAR_RAM | |
| VB_INIT_OUT_ENABLE_DISPLAY | |
| VB_INIT_OUT_ENABLE_USB_STORAGE | |
| VB_INIT_OUT_ENABLE_DEVELOPER | |
| VB_INIT_OUT_ENABLE_ALTERNATE_OS, " out flags"); |
| TEST_EQ(shared->flags, VBSD_BOOT_DEV_SWITCH_ON | VBSD_HONOR_VIRT_DEV_SWITCH, |
| " shared flags"); |
| |
| /* Ignore virtual developer switch, even though enabled. */ |
| ResetMocks(); |
| mock_virt_dev_sw = 1; |
| TestVbInit(0, 0, "TPM Dev mode on but ignored"); |
| TEST_EQ(shared->recovery_reason, 0, " recovery reason"); |
| TEST_EQ(iparams.out_flags, 0, " out flags"); |
| TEST_EQ(shared->flags, 0, " shared flags"); |
| |
| /* HW dev switch on, no virtual developer switch */ |
| ResetMocks(); |
| iparams.flags = VB_INIT_FLAG_DEV_SWITCH_ON; |
| TestVbInit(0, 0, "HW Dev mode on"); |
| TEST_EQ(shared->recovery_reason, 0, " recovery reason"); |
| TEST_EQ(iparams.out_flags, |
| VB_INIT_OUT_CLEAR_RAM | |
| VB_INIT_OUT_ENABLE_DISPLAY | |
| VB_INIT_OUT_ENABLE_USB_STORAGE | |
| VB_INIT_OUT_ENABLE_DEVELOPER | |
| VB_INIT_OUT_ENABLE_ALTERNATE_OS, " out flags"); |
| TEST_EQ(shared->flags, VBSD_BOOT_DEV_SWITCH_ON, " shared flags"); |
| } |
| |
| |
| /* disable MSVC warnings on unused arguments */ |
| __pragma(warning (disable: 4100)) |
| |
| int main(int argc, char* argv[]) { |
| int error_code = 0; |
| |
| VbInitTest(); |
| VbInitTestTPM(); |
| |
| if (!gTestSuccess) |
| error_code = 255; |
| |
| return error_code; |
| } |