diff options
author | Philipp Deppenwiese <zaolin@das-labor.org> | 2017-10-17 17:02:29 +0200 |
---|---|---|
committer | Martin Roth <martinroth@google.com> | 2017-10-22 02:14:46 +0000 |
commit | fea2429e254c41b192dd0856966d5f80eb15a07a (patch) | |
tree | 40e81b3ca7ee3c1f9985af9a8a24a3a9b21e8700 /src/security/vboot | |
parent | 9e0d69bf1e0194509108eba23511a12d597ae49e (diff) |
security/vboot: Move vboot2 to security kconfig section
This commit just moves the vboot sources into
the security directory and fixes kconfig/makefile paths.
Fix vboot2 headers
Change-Id: Icd87f95640186f7a625242a3937e1dd13347eb60
Signed-off-by: Philipp Deppenwiese <zaolin@das-labor.org>
Reviewed-on: https://review.coreboot.org/22074
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Martin Roth <martinroth@google.com>
Diffstat (limited to 'src/security/vboot')
-rw-r--r-- | src/security/vboot/Kconfig | 334 | ||||
-rw-r--r-- | src/security/vboot/Makefile.inc | 263 | ||||
-rw-r--r-- | src/security/vboot/bootmode.c | 171 | ||||
-rw-r--r-- | src/security/vboot/common.c | 182 | ||||
-rw-r--r-- | src/security/vboot/misc.h | 40 | ||||
-rw-r--r-- | src/security/vboot/secdata_mock.c | 85 | ||||
-rw-r--r-- | src/security/vboot/secdata_tpm.c | 585 | ||||
-rw-r--r-- | src/security/vboot/symbols.h | 28 | ||||
-rw-r--r-- | src/security/vboot/vbnv.c | 155 | ||||
-rw-r--r-- | src/security/vboot/vbnv.h | 51 | ||||
-rw-r--r-- | src/security/vboot/vbnv_cmos.c | 114 | ||||
-rw-r--r-- | src/security/vboot/vbnv_ec.c | 30 | ||||
-rw-r--r-- | src/security/vboot/vbnv_flash.c | 181 | ||||
-rw-r--r-- | src/security/vboot/vbnv_layout.h | 50 | ||||
-rw-r--r-- | src/security/vboot/vboot_common.c | 115 | ||||
-rw-r--r-- | src/security/vboot/vboot_common.h | 118 | ||||
-rw-r--r-- | src/security/vboot/vboot_handoff.c | 190 | ||||
-rw-r--r-- | src/security/vboot/vboot_loader.c | 158 | ||||
-rw-r--r-- | src/security/vboot/vboot_logic.c | 438 | ||||
-rw-r--r-- | src/security/vboot/verstage.c | 39 |
20 files changed, 3327 insertions, 0 deletions
diff --git a/src/security/vboot/Kconfig b/src/security/vboot/Kconfig new file mode 100644 index 0000000000..d5b5de291e --- /dev/null +++ b/src/security/vboot/Kconfig @@ -0,0 +1,334 @@ +## This file is part of the coreboot project. +## +## Copyright (C) 2014 The ChromiumOS Authors. All rights reserved. +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; version 2 of the License. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## + +menu "Verified Boot (vboot)" + +config VBOOT + bool "Verify firmware with vboot." + default n + select TPM if !MAINBOARD_HAS_TPM2 && !VBOOT_MOCK_SECDATA + select TPM2 if MAINBOARD_HAS_TPM2 && !VBOOT_MOCK_SECDATA + select TPM_INIT_FAILURE_IS_FATAL if PC80_SYSTEM && LPC_TPM + select SKIP_TPM_STARTUP_ON_NORMAL_BOOT if PC80_SYSTEM && LPC_TPM + depends on HAVE_HARD_RESET + help + Enabling VBOOT will use vboot to verify the components of the firmware + (stages, payload, etc). + +if VBOOT + +config VBOOT_VBNV_CMOS + bool + default n + depends on PC80_SYSTEM + help + VBNV is stored in CMOS + +config VBOOT_VBNV_OFFSET + hex + default 0x26 + depends on VBOOT_VBNV_CMOS + help + CMOS offset for VbNv data. This value must match cmos.layout + in the mainboard directory, minus 14 bytes for the RTC. + +config VBOOT_VBNV_CMOS_BACKUP_TO_FLASH + bool + default n + depends on VBOOT_VBNV_CMOS && BOOT_DEVICE_SUPPORTS_WRITES + help + Vboot non-volatile storage data will be backed up from CMOS to flash + and restored from flash if the CMOS is invalid due to power loss. + +config VBOOT_VBNV_EC + bool + default n + help + VBNV is stored in EC + +config VBOOT_VBNV_FLASH + bool + default n + depends on BOOT_DEVICE_SUPPORTS_WRITES + help + VBNV is stored in flash storage + +config VBOOT_STARTS_IN_BOOTBLOCK + bool + default n + help + Firmware verification happens during the end of or right after the + bootblock. This implies that a static VBOOT2_WORK() buffer must be + allocated in memlayout. + +config VBOOT_STARTS_IN_ROMSTAGE + bool + default n + depends on !VBOOT_STARTS_IN_BOOTBLOCK + help + Firmware verification happens during the end of romstage (after + memory initialization). This implies that vboot working data is + allocated in CBMEM. + +config VBOOT_MOCK_SECDATA + bool "Mock secdata for firmware verification" + default n + help + Enabling VBOOT_MOCK_SECDATA will mock secdata for the firmware + verification to avoid access to a secdata storage (typically TPM). + All operations for a secdata storage will be successful. This option + can be used during development when a TPM is not present or broken. + THIS SHOULD NOT BE LEFT ON FOR PRODUCTION DEVICES. + +config VBOOT_DISABLE_DEV_ON_RECOVERY + bool + default n + help + When this option is enabled, the Chrome OS device leaves the + developer mode as soon as recovery request is detected. This is + handy on embedded devices with limited input capabilities. + +config VBOOT_SEPARATE_VERSTAGE + bool + default n + depends on VBOOT_STARTS_IN_BOOTBLOCK + help + If this option is set, vboot verification runs in a standalone stage + that is loaded from the bootblock and exits into romstage. If it is + not set, the verification code is linked directly into the bootblock + or the romstage and runs as part of that stage (cf. related options + VBOOT_STARTS_IN_BOOTBLOCK/_ROMSTAGE and VBOOT_RETURN_FROM_VERSTAGE). + +config VBOOT_RETURN_FROM_VERSTAGE + bool + default n + depends on VBOOT_SEPARATE_VERSTAGE + help + If this is set, the verstage returns back to the calling stage instead + of exiting to the succeeding stage so that the verstage space can be + reused by the succeeding stage. This is useful if a RAM space is too + small to fit both the verstage and the succeeding stage. + +config VBOOT_SAVE_RECOVERY_REASON_ON_REBOOT + bool + default n + help + This option ensures that the recovery request is not lost because of + reboots caused after vboot verification is run. e.g. reboots caused by + FSP components on Intel platforms. + +config VBOOT_OPROM_MATTERS + bool + default n + help + Set this option to indicate to vboot that this platform will skip its + display initialization on a normal (non-recovery, non-developer) boot. + Vboot calls this "oprom matters" because on x86 devices this + traditionally meant that the video option ROM will not be loaded, but + it works functionally the same for other platforms that can skip their + native display initialization code instead. + +config VBOOT_HAS_REC_HASH_SPACE + bool + default n + help + Set this option to indicate to vboot that recovery data hash space + is present in TPM. + +config VBOOT_SOFT_REBOOT_WORKAROUND + bool + default n + +config VBOOT_EC_SOFTWARE_SYNC + bool "Enable EC software sync" + default y if EC_GOOGLE_CHROMEEC + default n + help + EC software sync is a mechanism where the AP helps the EC verify its + firmware similar to how vboot verifies the main system firmware. This + option selects whether vboot should support EC software sync. + +config VBOOT_EC_SLOW_UPDATE + bool + default n + depends on VBOOT_EC_SOFTWARE_SYNC + help + Whether the EC (or PD) is slow to update and needs to display a + screen that informs the user the update is happening. + +config VBOOT_EC_EFS + bool + default n + depends on VBOOT_EC_SOFTWARE_SYNC + help + CrosEC can support EFS: Early Firmware Selection. If it's enabled, + software sync need to also support it. This setting tells vboot to + perform EFS software sync. + +config VBOOT_PHYSICAL_DEV_SWITCH + bool + default n + help + Whether this platform has a physical developer switch. Note that this + disables virtual dev switch functionality (through secdata). Operation + where both a physical pin and the virtual switch get sampled is not + supported by coreboot. + +config VBOOT_PHYSICAL_REC_SWITCH + bool + default n + help + Whether this platform has a physical recovery switch. + +config VBOOT_LID_SWITCH + bool + default n + help + Whether this platform has a lid switch. If it does, vboot will not + decrement try counters for boot failures if the lid is closed. + +config VBOOT_WIPEOUT_SUPPORTED + bool + default n + help + When this option is enabled, the firmware provides the ability to + signal the application the need for factory reset (a.k.a. wipe + out) of the device + +config VBOOT_FWID_MODEL + string "Firmware ID model" + default "Google_$(CONFIG_MAINBOARD_PART_NUMBER)" if CHROMEOS + default "$(CONFIG_MAINBOARD_VENDOR)_$(CONFIG_MAINBOARD_PART_NUMBER)" + help + This is the first part of the FWID written to various regions of a + vboot firmware image to identify its version. + +config VBOOT_FWID_VERSION + string "Firmware ID version" + default ".$(KERNELVERSION)" + help + This is the second part of the FWID written to various regions of a + vboot firmware image to identify its version. + +config RO_REGION_ONLY + string "Additional files that should not be copied to RW" + default "" + help + Add a space delimited list of filenames that should only be in the + RO section. + +menu "GBB configuration" + +config GBB_HWID + string "Hardware ID" + default "NOCONF HWID" + +config GBB_BMPFV_FILE + string "Path to bmpfv image" + default "" + +config GBB_FLAG_DEV_SCREEN_SHORT_DELAY + bool "Reduce dev screen delay" + default n + +config GBB_FLAG_LOAD_OPTION_ROMS + bool "Load option ROMs" + default n + +config GBB_FLAG_ENABLE_ALTERNATE_OS + bool "Allow booting a non-Chrome OS kernel if dev switch is on" + default n + +config GBB_FLAG_FORCE_DEV_SWITCH_ON + bool "Force dev switch on" + default n + +config GBB_FLAG_FORCE_DEV_BOOT_USB + bool "Allow booting from USB in dev mode even if dev_boot_usb=0" + default y + +config GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK + bool "Disable firmware rollback protection" + default y + +config GBB_FLAG_ENTER_TRIGGERS_TONORM + bool "Return to normal boot with Enter" + default n + +config GBB_FLAG_FORCE_DEV_BOOT_LEGACY + bool "Allow booting to legacy in dev mode even if dev_boot_legacy=0" + default n + +config GBB_FLAG_FAFT_KEY_OVERIDE + bool "Allow booting using alternative keys for FAFT servo testing" + default n + +config GBB_FLAG_DISABLE_EC_SOFTWARE_SYNC + bool "Disable EC software sync" + default n + +config GBB_FLAG_DEFAULT_DEV_BOOT_LEGACY + bool "Default to booting to legacy in dev mode" + default n + +config GBB_FLAG_DISABLE_PD_SOFTWARE_SYNC + bool "Disable PD software sync" + default n + +config GBB_FLAG_DISABLE_LID_SHUTDOWN + bool "Disable shutdown on closed lid" + default n + +config GBB_FLAG_FORCE_DEV_BOOT_FASTBOOT_FULL_CAP + bool "Allow fastboot even if dev_boot_fastboot_full_cap=0" + default n + +config GBB_FLAG_ENABLE_SERIAL + bool "Tell vboot to enable serial console" + default n + +endmenu # GBB + +menu "Vboot Keys" +config VBOOT_ROOT_KEY + string "Root key (public)" + default "$(VBOOT_SOURCE)/tests/devkeys/root_key.vbpubk" + +config VBOOT_RECOVERY_KEY + string "Recovery key (public)" + default "$(VBOOT_SOURCE)/tests/devkeys/recovery_key.vbpubk" + +config VBOOT_FIRMWARE_PRIVKEY + string "Firmware key (private)" + default "$(VBOOT_SOURCE)/tests/devkeys/firmware_data_key.vbprivk" + +config VBOOT_KERNEL_KEY + string "Kernel subkey (public)" + default "$(VBOOT_SOURCE)/tests/devkeys/kernel_subkey.vbpubk" + +config VBOOT_KEYBLOCK + string "Keyblock to use for the RW regions" + default "$(VBOOT_SOURCE)/tests/devkeys/firmware.keyblock" + +config VBOOT_KEYBLOCK_VERSION + int "Keyblock version number" + default 1 + +config VBOOT_KEYBLOCK_PREAMBLE_FLAGS + hex "Keyblock preamble flags" + default 0x0 + +endmenu # Keys +endif # VBOOT +endmenu # Verified Boot (vboot) diff --git a/src/security/vboot/Makefile.inc b/src/security/vboot/Makefile.inc new file mode 100644 index 0000000000..75ecff3b21 --- /dev/null +++ b/src/security/vboot/Makefile.inc @@ -0,0 +1,263 @@ +## +## This file is part of the coreboot project. +## +## Copyright (C) 2014 The ChromiumOS Authors. All rights reserved. +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; version 2 of the License. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## + +ifeq ($(CONFIG_VBOOT),y) + +bootblock-y += bootmode.c +romstage-y += bootmode.c +ramstage-y += bootmode.c +verstage-y += bootmode.c +postcar-y += bootmode.c + +verstage-generic-ccopts += -D__PRE_RAM__ -D__VERSTAGE__ + +bootblock-y += vbnv.c +verstage-y += vbnv.c +romstage-y += vbnv.c +ramstage-y += vbnv.c + +bootblock-$(CONFIG_VBOOT_VBNV_CMOS) += vbnv_cmos.c +verstage-$(CONFIG_VBOOT_VBNV_CMOS) += vbnv_cmos.c +romstage-$(CONFIG_VBOOT_VBNV_CMOS) += vbnv_cmos.c +ramstage-$(CONFIG_VBOOT_VBNV_CMOS) += vbnv_cmos.c + +bootblock-$(CONFIG_VBOOT_VBNV_CMOS_BACKUP_TO_FLASH) += vbnv_flash.c +verstage-$(CONFIG_VBOOT_VBNV_CMOS_BACKUP_TO_FLASH) += vbnv_flash.c +romstage-$(CONFIG_VBOOT_VBNV_CMOS_BACKUP_TO_FLASH) += vbnv_flash.c +ramstage-$(CONFIG_VBOOT_VBNV_CMOS_BACKUP_TO_FLASH) += vbnv_flash.c + +bootblock-$(CONFIG_VBOOT_VBNV_EC) += vbnv_ec.c +verstage-$(CONFIG_VBOOT_VBNV_EC) += vbnv_ec.c +romstage-$(CONFIG_VBOOT_VBNV_EC) += vbnv_ec.c +ramstage-$(CONFIG_VBOOT_VBNV_EC) += vbnv_ec.c + +bootblock-$(CONFIG_VBOOT_VBNV_FLASH) += vbnv_flash.c +verstage-$(CONFIG_VBOOT_VBNV_FLASH) += vbnv_flash.c +romstage-$(CONFIG_VBOOT_VBNV_FLASH) += vbnv_flash.c +ramstage-$(CONFIG_VBOOT_VBNV_FLASH) += vbnv_flash.c + +bootblock-y += vboot_loader.c +romstage-y += vboot_loader.c +ramstage-y += vboot_loader.c +verstage-y += vboot_loader.c +postcar-y += vboot_loader.c + +bootblock-y += vboot_common.c +verstage-y += vboot_common.c +romstage-y += vboot_common.c +ramstage-y += vboot_common.c +postcar-y += vboot_common.c + +bootblock-y += common.c +verstage-y += vboot_logic.c +verstage-y += common.c +verstage-$(CONFIG_VBOOT_SEPARATE_VERSTAGE) += verstage.c +ifeq (${CONFIG_VBOOT_MOCK_SECDATA},y) +verstage-y += secdata_mock.c +romstage-$(CONFIG_VBOOT_SEPARATE_VERSTAGE) += secdata_mock.c +else +verstage-y += secdata_tpm.c +romstage-$(CONFIG_VBOOT_SEPARATE_VERSTAGE) += secdata_tpm.c +endif +romstage-y += vboot_handoff.c common.c + +ramstage-y += common.c +postcar-y += common.c + +ifeq ($(CONFIG_VBOOT_SEPARATE_VERSTAGE),y) +VB_FIRMWARE_ARCH := $(ARCHDIR-$(ARCH-verstage-y)) +else +ifeq ($(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK),y) +VB_FIRMWARE_ARCH := $(ARCHDIR-$(ARCH-bootblock-y)) +else +VB_FIRMWARE_ARCH := $(ARCHDIR-$(ARCH-romstage-y)) +endif +endif # CONFIG_VBOOT_SEPARATE_VERSTAGE + +VB2_LIB = $(obj)/external/vboot_reference/vboot_fw20.a +VBOOT_CFLAGS += $(patsubst -I%,-I$(top)/%, $(filter-out -I$(obj), $(filter-out -include $(src)/include/kconfig.h, $(CPPFLAGS_verstage)))) +VBOOT_CFLAGS += $(CFLAGS_verstage) +VBOOT_CFLAGS += $(verstage-c-ccopts) +VBOOT_CFLAGS += -I$(abspath $(obj)) -include $(top)/src/include/kconfig.h -Wno-missing-prototypes +VBOOT_CFLAGS += -DVBOOT_DEBUG + +$(VB2_LIB): $(obj)/config.h + printf " MAKE $(subst $(obj)/,,$(@))\n" + +FIRMWARE_ARCH=$(VB_FIRMWARE_ARCH) \ + CC="$(CC_verstage)" \ + CFLAGS="$(VBOOT_CFLAGS)" VBOOT2="y" \ + $(MAKE) -C $(VBOOT_SOURCE) \ + BUILD=$(abspath $(dir $(VB2_LIB))) \ + V=$(V) \ + fwlib20 + +verstage-srcs += $(VB2_LIB) + +ifeq ($(CONFIG_VBOOT_SEPARATE_VERSTAGE),y) + +# This works under the assumption that romstage and verstage use the same +# architecture and thus CC_verstage is the same as CC_romstage. If this is not +# true, VB2_LIB needs to ensure that correct CC is being used. +ifeq ($(CONFIG_VBOOT_HAS_REC_HASH_SPACE),y) +romstage-srcs += $(VB2_LIB) +endif + +cbfs-files-$(CONFIG_VBOOT_SEPARATE_VERSTAGE) += $(CONFIG_CBFS_PREFIX)/verstage +$(CONFIG_CBFS_PREFIX)/verstage-file := $(objcbfs)/verstage.elf +$(CONFIG_CBFS_PREFIX)/verstage-type := stage +$(CONFIG_CBFS_PREFIX)/verstage-compression := $(CBFS_PRERAM_COMPRESS_FLAG) + +ifeq ($(CONFIG_ARCH_VERSTAGE_X86_32)$(CONFIG_ARCH_VERSTAGE_X86_64),y) +$(CONFIG_CBFS_PREFIX)/verstage-options := -a 64 -S ".car.data" + +# If CAR does not support execution of code, verstage on x86 is expected to be +# xip. +ifneq ($(CONFIG_NO_XIP_EARLY_STAGES),y) +$(CONFIG_CBFS_PREFIX)/verstage-options += --xip +endif + +endif + +else # CONFIG_VBOOT_SEPARATE_VERSTAGE +ifeq ($(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK),y) +postinclude-hooks += $$(eval bootblock-srcs += $$(verstage-srcs)) +else +postinclude-hooks += $$(eval romstage-srcs += $$(verstage-srcs)) +endif +endif # CONFIG_VBOOT_SEPARATE_VERSTAGE + +# Define a list of files that need to be in RO only. +# All other files will be installed into RO and RW regions +# Use $(sort) to cut down on extra spaces that would be translated to commas +regions-for-file = $(subst $(spc),$(comma),$(sort \ + $(if $(filter \ + $(if $(filter y,$(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK)),, \ + %/romstage) \ + mts \ + %/verstage \ + locales \ + locale_%.bin \ + font.bin \ + vbgfx.bin \ + rmu.bin \ + $(call strip_quotes,$(CONFIG_RO_REGION_ONLY)) \ + ,$(1)),COREBOOT,COREBOOT FW_MAIN_A FW_MAIN_B))) + +CONFIG_GBB_HWID := $(call strip_quotes,$(CONFIG_GBB_HWID)) +CONFIG_GBB_BMPFV_FILE := $(call strip_quotes,$(CONFIG_GBB_BMPFV_FILE)) +CONFIG_VBOOT_KEYBLOCK := $(call strip_quotes,$(CONFIG_VBOOT_KEYBLOCK)) +CONFIG_VBOOT_FIRMWARE_PRIVKEY := $(call strip_quotes,$(CONFIG_VBOOT_FIRMWARE_PRIVKEY)) +CONFIG_VBOOT_KERNEL_KEY := $(call strip_quotes,$(CONFIG_VBOOT_KERNEL_KEY)) +CONFIG_VBOOT_FWID_MODEL := $(call strip_quotes,$(CONFIG_VBOOT_FWID_MODEL)) +CONFIG_VBOOT_FWID_VERSION := $(call strip_quotes,$(CONFIG_VBOOT_FWID_VERSION)) + +# bool-to-mask(var, value) +# return "value" if var is "y", 0 otherwise +bool-to-mask = $(if $(filter y,$(1)),$(2),0) + +GBB_FLAGS := $(call int-add, \ + $(call bool-to-mask,$(CONFIG_GBB_FLAG_DEV_SCREEN_SHORT_DELAY),0x1) \ + $(call bool-to-mask,$(CONFIG_GBB_FLAG_LOAD_OPTION_ROMS),0x2) \ + $(call bool-to-mask,$(CONFIG_GBB_FLAG_ENABLE_ALTERNATE_OS),0x4) \ + $(call bool-to-mask,$(CONFIG_GBB_FLAG_FORCE_DEV_SWITCH_ON),0x8) \ + $(call bool-to-mask,$(CONFIG_GBB_FLAG_FORCE_DEV_BOOT_USB),0x10) \ + $(call bool-to-mask,$(CONFIG_GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK),0x20) \ + $(call bool-to-mask,$(CONFIG_GBB_FLAG_ENTER_TRIGGERS_TONORM),0x40) \ + $(call bool-to-mask,$(CONFIG_GBB_FLAG_FORCE_DEV_BOOT_LEGACY),0x80) \ + $(call bool-to-mask,$(CONFIG_GBB_FLAG_FAFT_KEY_OVERIDE),0x100) \ + $(call bool-to-mask,$(CONFIG_GBB_FLAG_DISABLE_EC_SOFTWARE_SYNC),0x200) \ + $(call bool-to-mask,$(CONFIG_GBB_FLAG_DEFAULT_DEV_BOOT_LEGACY),0x400) \ + $(call bool-to-mask,$(CONFIG_GBB_FLAG_DISABLE_PD_SOFTWARE_SYNC),0x800) \ + $(call bool-to-mask,$(CONFIG_GBB_FLAG_DISABLE_LID_SHUTDOWN),0x1000) \ + $(call bool-to-mask,$(CONFIG_GBB_FLAG_FORCE_DEV_BOOT_FASTBOOT_FULL_CAP),0x2000) \ + $(call bool-to-mask,$(CONFIG_GBB_FLAG_ENABLE_SERIAL),0x4000) \ + ) + +ifneq ($(CONFIG_GBB_BMPFV_FILE),) +$(obj)/gbb.sizetmp: $(obj)/coreboot.rom + $(CBFSTOOL) $< read -r GBB -f $@ + +$(obj)/gbb.stub: $(obj)/coreboot.rom $(FUTILITY) $(obj)/gbb.sizetmp + @printf " CREATE GBB (with BMPFV)\n" + $(FUTILITY) gbb_utility -c 0x100,0x1000,$(call int-subtract,$(call file-size,$(obj)/gbb.sizetmp) 0x2180),0x1000 $@.tmp + mv $@.tmp $@ +else +$(obj)/gbb.stub: $(obj)/coreboot.rom $(FUTILITY) + @printf " CREATE GBB (without BMPFV)\n" + $(FUTILITY) gbb_utility -c 0x100,0x1000,0,0x1000 $@.tmp + mv $@.tmp $@ +endif + +$(obj)/gbb.region: $(obj)/gbb.stub + @printf " SETUP GBB\n" + cp $< $@.tmp + $(FUTILITY) gbb_utility -s \ + --hwid="$(CONFIG_GBB_HWID)" \ + --rootkey="$(CONFIG_VBOOT_ROOT_KEY)" \ + --recoverykey="$(CONFIG_VBOOT_RECOVERY_KEY)" \ + --flags=$(GBB_FLAGS) \ + $@.tmp +ifneq ($(CONFIG_GBB_BMPFV_FILE),) + $(FUTILITY) gbb_utility -s \ + --bmpfv="$(CONFIG_GBB_BMPFV_FILE)" \ + $@.tmp +endif + mv $@.tmp $@ + +$(obj)/fwid.region: + printf "$(CONFIG_VBOOT_FWID_MODEL)$(CONFIG_VBOOT_FWID_VERSION)\0" > $@ + +build_complete:: $(obj)/gbb.region $(obj)/fwid.region + @printf " WRITE GBB\n" + $(CBFSTOOL) $(obj)/coreboot.rom write -u -r GBB -i 0 -f $(obj)/gbb.region + $(CBFSTOOL) $(obj)/coreboot.rom write -u -r RO_FRID -i 0 -f $(obj)/fwid.region + $(CBFSTOOL) $(obj)/coreboot.rom write -u -r RW_FWID_A -i 0 -f $(obj)/fwid.region + $(CBFSTOOL) $(obj)/coreboot.rom write -u -r RW_FWID_B -i 0 -f $(obj)/fwid.region + +ifneq ($(shell grep "SHARED_DATA" "$(CONFIG_FMDFILE)"),) +build_complete:: + printf "\0" > $(obj)/shared_data.region + $(CBFSTOOL) $(obj)/coreboot.rom write -u -r SHARED_DATA -i 0 -f $(obj)/shared_data.region +endif + +# Extract FW_MAIN_? region and minimize it if the last file is empty, so it +# doesn't contain this empty file (that can have a significant size), +# improving a lot on hash times due to a smaller amount of data loaded from +# firmware storage. +# When passing the minimized image to vbutil_firmware, its length is recorded +# in the keyblock, and coreboot's vboot code clips the region_device to match, +# which prevents any potential extension attacks. +$(obj)/FW_MAIN_%.bin: $(obj)/coreboot.rom + $(CBFSTOOL) $< truncate -r $(basename $(notdir $@)) > $@.tmp.size + $(CBFSTOOL) $< read -r $(basename $(notdir $@)) -f $@.tmp + head -c $$( printf "%d" $$(cat $@.tmp.size)) $@.tmp > $@.tmp2 + mv $@.tmp2 $@ + rm -f $@.tmp $@.tmp.size + +$(obj)/VBLOCK_%.bin: $(obj)/FW_MAIN_%.bin $(FUTILITY) + $(FUTILITY) vbutil_firmware \ + --vblock $@ \ + --keyblock "$(CONFIG_VBOOT_KEYBLOCK)" \ + --signprivate "$(CONFIG_VBOOT_FIRMWARE_PRIVKEY)" \ + --version $(CONFIG_VBOOT_KEYBLOCK_VERSION) \ + --fv $< \ + --kernelkey "$(CONFIG_VBOOT_KERNEL_KEY)" \ + --flags $(CONFIG_VBOOT_KEYBLOCK_PREAMBLE_FLAGS) + +files_added:: $(obj)/VBLOCK_A.bin $(obj)/VBLOCK_B.bin + $(CBFSTOOL) $(obj)/coreboot.rom write -u -r VBLOCK_A -f $(obj)/VBLOCK_A.bin + $(CBFSTOOL) $(obj)/coreboot.rom write -u -r VBLOCK_B -f $(obj)/VBLOCK_B.bin + +endif # CONFIG_VBOOT diff --git a/src/security/vboot/bootmode.c b/src/security/vboot/bootmode.c new file mode 100644 index 0000000000..834bc481e1 --- /dev/null +++ b/src/security/vboot/bootmode.c @@ -0,0 +1,171 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2016 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <assert.h> +#include <bootmode.h> +#include <bootstate.h> +#include <cbmem.h> +#include <rules.h> +#include <string.h> +#include <vb2_api.h> +#include <security/vboot/misc.h> +#include <security/vboot/vbnv.h> +#include <security/vboot/vboot_common.h> + +static int vb2_get_recovery_reason_shared_data(void) +{ + /* Shared data does not exist for Ramstage and Post-CAR stage. */ + if (ENV_RAMSTAGE || ENV_POSTCAR) + return 0; + + struct vb2_shared_data *sd = vb2_get_shared_data(); + assert(sd); + return sd->recovery_reason; +} + +void vb2_save_recovery_reason_vbnv(void) +{ + if (!IS_ENABLED(CONFIG_VBOOT_SAVE_RECOVERY_REASON_ON_REBOOT)) + return; + + int reason = vb2_get_recovery_reason_shared_data(); + if (!reason) + return; + + set_recovery_mode_into_vbnv(reason); +} + +static void vb2_clear_recovery_reason_vbnv(void *unused) +{ + if (!IS_ENABLED(CONFIG_VBOOT_SAVE_RECOVERY_REASON_ON_REBOOT)) + return; + + set_recovery_mode_into_vbnv(0); +} + +/* + * Recovery reason stored in VBNV needs to be cleared before the state of VBNV + * is backed-up anywhere or jumping to the payload (whichever occurs + * first). Currently, vbnv_cmos.c backs up VBNV on POST_DEVICE. Thus, we need to + * make sure that the stored recovery reason is cleared off before that + * happens. + * IMPORTANT: Any reboot occurring after BS_DEV_INIT state will cause loss of + * recovery reason on reboot. Until now, we have seen reboots occuring on x86 + * only in FSP stages which run before BS_DEV_INIT. + */ +BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_EXIT, + vb2_clear_recovery_reason_vbnv, NULL); + +/* + * Returns 1 if vboot is being used and currently in a stage which might have + * already executed vboot verification. + */ +static int vboot_possibly_executed(void) +{ + if (IS_ENABLED(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK)) { + if (ENV_BOOTBLOCK && IS_ENABLED(CONFIG_VBOOT_SEPARATE_VERSTAGE)) + return 0; + return 1; + } + + if (IS_ENABLED(CONFIG_VBOOT_STARTS_IN_ROMSTAGE)) { + if (ENV_BOOTBLOCK) + return 0; + return 1; + } + + return 0; +} + +/* + * vb2_check_recovery_request looks up different components to identify if there + * is a recovery request and returns appropriate reason code: + * 1. Checks if recovery mode is initiated by EC. If yes, returns + * VB2_RECOVERY_RO_MANUAL. + * 2. Checks if recovery request is present in VBNV and returns the code read + * from it. + * 3. Checks recovery request in handoff for stages post-cbmem. + * 4. For non-CBMEM stages, check if vboot verification is done and look-up + * selected region to identify if vboot_refence library has requested recovery + * path. If yes, return the reason code from shared data. + * 5. If nothing applies, return 0 indicating no recovery request. + */ +int vboot_check_recovery_request(void) +{ + int reason = 0; + + /* EC-initiated recovery. */ + if (get_recovery_mode_switch()) + return VB2_RECOVERY_RO_MANUAL; + + /* Recovery request in VBNV. */ + if ((reason = get_recovery_mode_from_vbnv()) != 0) + return reason; + + /* + * Check recovery flag in vboot_handoff for stages post CBMEM coming + * online. Since for some stages there is no way to know if cbmem has + * already come online, try looking up handoff anyways. If it fails, + * flow will fallback to looking up shared data. + */ + if (cbmem_possibly_online() && + ((reason = vboot_handoff_get_recovery_reason()) != 0)) + return reason; + + /* + * For stages where CBMEM might not be online, identify if vboot + * verification is already complete and no slot was selected + * i.e. recovery path was requested. + */ + if (vboot_possibly_executed() && vb2_logic_executed() && + !vb2_is_slot_selected()) + return vb2_get_recovery_reason_shared_data(); + + return 0; +} + +int vboot_recovery_mode_enabled(void) +{ + return !!vboot_check_recovery_request(); +} + +int __attribute__((weak)) clear_recovery_mode_switch(void) +{ + // Weak implementation. Nothing to do. + return 0; +} + +void __attribute__((weak)) log_recovery_mode_switch(void) +{ + // Weak implementation. Nothing to do. +} + +int __attribute__((weak)) get_recovery_mode_retrain_switch(void) +{ + return 0; +} + +int vboot_recovery_mode_memory_retrain(void) +{ + return get_recovery_mode_retrain_switch(); +} + +int vboot_developer_mode_enabled(void) +{ + if (cbmem_possibly_online() && vboot_handoff_check_developer_flag()) + return 1; + + return 0; +} diff --git a/src/security/vboot/common.c b/src/security/vboot/common.c new file mode 100644 index 0000000000..72228e41d0 --- /dev/null +++ b/src/security/vboot/common.c @@ -0,0 +1,182 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <assert.h> +#include <cbfs.h> +#include <cbmem.h> +#include <console/console.h> +#include <reset.h> +#include <string.h> +#include <vb2_api.h> +#include <security/vboot/misc.h> +#include <security/vboot/symbols.h> +#include <security/vboot/vboot_common.h> + +struct selected_region { + uint32_t offset; + uint32_t size; +}; + +/* + * this is placed at the start of the vboot work buffer. selected_region is used + * for the verstage to return the location of the selected slot. buffer is used + * by the vboot2 core. Keep the struct CPU architecture agnostic as it crosses + * stage boundaries. + */ +struct vb2_working_data { + struct selected_region selected_region; + /* offset of the buffer from the start of this struct */ + uint32_t buffer_offset; + uint32_t buffer_size; +}; + +static const size_t vb_work_buf_size = 16 * KiB; + +static struct vb2_working_data * const vboot_get_working_data(void) +{ + if (IS_ENABLED(CONFIG_VBOOT_STARTS_IN_ROMSTAGE)) + /* cbmem_add() does a cbmem_find() first. */ + return cbmem_add(CBMEM_ID_VBOOT_WORKBUF, vb_work_buf_size); + else + return (struct vb2_working_data *)_vboot2_work; +} + +static size_t vb2_working_data_size(void) +{ + if (IS_ENABLED(CONFIG_VBOOT_STARTS_IN_ROMSTAGE)) + return vb_work_buf_size; + else + return _vboot2_work_size; +} + +static struct selected_region *vb2_selected_region(void) +{ + struct selected_region *sel_reg = NULL; + + /* Ramstage and postcar always uses cbmem as a source of truth. */ + if (ENV_RAMSTAGE || ENV_POSTCAR) + sel_reg = cbmem_find(CBMEM_ID_VBOOT_SEL_REG); + else if (ENV_ROMSTAGE) { + /* Try cbmem first. Fall back on working data if not found. */ + sel_reg = cbmem_find(CBMEM_ID_VBOOT_SEL_REG); + + if (sel_reg == NULL) { + struct vb2_working_data *wd = vboot_get_working_data(); + sel_reg = &wd->selected_region; + } + } else { + /* Stages such as bootblock and verstage use working data. */ + struct vb2_working_data *wd = vboot_get_working_data(); + sel_reg = &wd->selected_region; + } + + return sel_reg; +} + +void vb2_init_work_context(struct vb2_context *ctx) +{ + struct vb2_working_data *wd; + size_t work_size; + + /* First initialize the working data region. */ + work_size = vb2_working_data_size(); + wd = vboot_get_working_data(); + memset(wd, 0, work_size); + + /* + * vboot prefers 16-byte alignment. This takes away 16 bytes + * from the VBOOT2_WORK region, but the vboot devs said that's okay. + */ + wd->buffer_offset = ALIGN_UP(sizeof(*wd), 16); + wd->buffer_size = work_size - wd->buffer_offset; + + /* Initialize the vb2_context. */ + memset(ctx, 0, sizeof(*ctx)); + ctx->workbuf = (void *)vb2_get_shared_data(); + ctx->workbuf_size = wd->buffer_size; + +} + +struct vb2_shared_data *vb2_get_shared_data(void) +{ + struct vb2_working_data *wd = vboot_get_working_data(); + return (void *)((uintptr_t)wd + wd->buffer_offset); +} + +int vb2_get_selected_region(struct region *region) +{ + const struct selected_region *reg = vb2_selected_region(); + + if (reg == NULL) + return -1; + + if (reg->offset == 0 && reg->size == 0) + return -1; + + region->offset = reg->offset; + region->size = reg->size; + + return 0; +} + +void vb2_set_selected_region(const struct region *region) +{ + struct selected_region *reg = vb2_selected_region(); + + assert(reg != NULL); + + reg->offset = region_offset(region); + reg->size = region_sz(region); +} + +int vb2_is_slot_selected(void) +{ + const struct selected_region *reg = vb2_selected_region(); + + assert(reg != NULL); + + return reg->size > 0; +} + +void vb2_store_selected_region(void) +{ + const struct vb2_working_data *wd; + struct selected_region *sel_reg; + + /* Always use the working data in this path since it's the object + * which has the result.. */ + wd = vboot_get_working_data(); + + sel_reg = cbmem_add(CBMEM_ID_VBOOT_SEL_REG, sizeof(*sel_reg)); + + assert(sel_reg != NULL); + + sel_reg->offset = wd->selected_region.offset; + sel_reg->size = wd->selected_region.size; +} + +/* + * For platforms that employ VBOOT_STARTS_IN_ROMSTAGE, the vboot + * verification doesn't happen until after cbmem is brought online. + * Therefore, the selected region contents would not be initialized + * so don't automatically add results when cbmem comes online. + */ +#if !IS_ENABLED(CONFIG_VBOOT_STARTS_IN_ROMSTAGE) +static void vb2_store_selected_region_cbmem(int unused) +{ + vb2_store_selected_region(); +} +ROMSTAGE_CBMEM_INIT_HOOK(vb2_store_selected_region_cbmem) +#endif diff --git a/src/security/vboot/misc.h b/src/security/vboot/misc.h new file mode 100644 index 0000000000..b5e3fcfe94 --- /dev/null +++ b/src/security/vboot/misc.h @@ -0,0 +1,40 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __VBOOT_MISC_H__ +#define __VBOOT_MISC_H__ + +#include <security/vboot/vboot_common.h> + +struct vb2_context; +struct vb2_shared_data; + +void vboot_fill_handoff(void); + +void vb2_init_work_context(struct vb2_context *ctx); +struct vb2_shared_data *vb2_get_shared_data(void); + +/* Returns 0 on success. < 0 on failure. */ +int vb2_get_selected_region(struct region *region); +void vb2_set_selected_region(const struct region *region); +int vb2_is_slot_selected(void); +int vb2_logic_executed(void); + +/* Store the selected region in cbmem for later use. */ +void vb2_store_selected_region(void); + +void vb2_save_recovery_reason_vbnv(void); + +#endif /* __VBOOT_MISC_H__ */ diff --git a/src/security/vboot/secdata_mock.c b/src/security/vboot/secdata_mock.c new file mode 100644 index 0000000000..72ee5b3c11 --- /dev/null +++ b/src/security/vboot/secdata_mock.c @@ -0,0 +1,85 @@ +/* Copyright (c) 2015 The Chromium OS Authors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Functions for querying, manipulating and locking rollback indices + * stored in the TPM NVRAM. + */ + +#include <antirollback.h> +#include <stdlib.h> +#include <tpm_lite/tlcl.h> +#include <vb2_api.h> + +uint32_t tpm_extend_pcr(struct vb2_context *ctx, int pcr, + enum vb2_pcr_digest which_digest) +{ + return TPM_SUCCESS; +} + +uint32_t tpm_clear_and_reenable(void) +{ + return TPM_SUCCESS; +} + +uint32_t antirollback_read_space_firmware(struct vb2_context *ctx) +{ + vb2api_secdata_create(ctx); + return TPM_SUCCESS; +} + +uint32_t antirollback_write_space_firmware(struct vb2_context *ctx) +{ + return TPM_SUCCESS; +} + +uint32_t antirollback_lock_space_firmware() +{ + return TPM_SUCCESS; +} + +uint32_t antirollback_lock_space_rec_hash(void) +{ + return TPM_SUCCESS; +} + +uint32_t antirollback_read_space_rec_hash(uint8_t *data, uint32_t size) +{ + return TPM_SUCCESS; +} + +uint32_t antirollback_write_space_rec_hash(const uint8_t *data, uint32_t size) +{ + return TPM_SUCCESS; +} + +uint32_t tlcl_lib_init(void) +{ + return VB2_SUCCESS; +} diff --git a/src/security/vboot/secdata_tpm.c b/src/security/vboot/secdata_tpm.c new file mode 100644 index 0000000000..be9d680cc0 --- /dev/null +++ b/src/security/vboot/secdata_tpm.c @@ -0,0 +1,585 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Functions for querying, manipulating and locking rollback indices + * stored in the TPM NVRAM. + */ + +#include <antirollback.h> +#include <stdlib.h> +#include <string.h> +#include <tpm_lite/tlcl.h> +#include <vb2_api.h> +#include <console/console.h> + +#ifndef offsetof +#define offsetof(A,B) __builtin_offsetof(A,B) +#endif + +#ifdef FOR_TEST +#include <stdio.h> +#define VBDEBUG(format, args...) printf(format, ## args) +#else +#include <console/console.h> +#define VBDEBUG(format, args...) \ + printk(BIOS_INFO, "%s():%d: " format, __func__, __LINE__, ## args) +#endif + +#define RETURN_ON_FAILURE(tpm_cmd) do { \ + uint32_t result_; \ + if ((result_ = (tpm_cmd)) != TPM_SUCCESS) { \ + VBDEBUG("Antirollback: %08x returned by " #tpm_cmd \ + "\n", (int)result_); \ + return result_; \ + } \ + } while (0) + + +static uint32_t safe_write(uint32_t index, const void *data, uint32_t length); + +uint32_t tpm_extend_pcr(struct vb2_context *ctx, int pcr, + enum vb2_pcr_digest which_digest) +{ + uint8_t buffer[VB2_PCR_DIGEST_RECOMMENDED_SIZE]; + uint32_t size = sizeof(buffer); + int rv; + + rv = vb2api_get_pcr_digest(ctx, which_digest, buffer, &size); + if (rv != VB2_SUCCESS) + return rv; + if (size < TPM_PCR_DIGEST) + return VB2_ERROR_UNKNOWN; + + return tlcl_extend(pcr, buffer, NULL); +} + +static uint32_t read_space_firmware(struct vb2_context *ctx) +{ + int attempts = 3; + + while (attempts--) { + RETURN_ON_FAILURE(tlcl_read(FIRMWARE_NV_INDEX, ctx->secdata, + VB2_SECDATA_SIZE)); + + if (vb2api_secdata_check(ctx) == VB2_SUCCESS) + return TPM_SUCCESS; + + VBDEBUG("TPM: %s() - bad CRC\n", __func__); + } + + VBDEBUG("TPM: %s() - too many bad CRCs, giving up\n", __func__); + return TPM_E_CORRUPTED_STATE; +} + +static uint32_t read_space_rec_hash(uint8_t *data) +{ + RETURN_ON_FAILURE(tlcl_read(REC_HASH_NV_INDEX, data, + REC_HASH_NV_SIZE)); + return TPM_SUCCESS; +} + +static uint32_t write_secdata(uint32_t index, + const uint8_t *secdata, + uint32_t len) +{ + uint8_t sd[32]; + uint32_t rv; + int attempts = 3; + + if (len > sizeof(sd)) { + VBDEBUG("TPM: %s() - data is too large\n", __func__); + return TPM_E_WRITE_FAILURE; + } + + while (attempts--) { + rv = safe_write(index, secdata, len); + /* Can't write, not gonna try again */ + if (rv != TPM_SUCCESS) + return rv; + + /* Read it back to be sure it got the right values. */ + rv = tlcl_read(index, sd, len); + if (rv == TPM_SUCCESS && memcmp(secdata, sd, len) == 0) + return rv; + + VBDEBUG("TPM: %s() failed. trying again\n", __func__); + /* Try writing it again. Maybe it was garbled on the way out. */ + } + + VBDEBUG("TPM: %s() - too many failures, giving up\n", __func__); + + return TPM_E_CORRUPTED_STATE; +} + +/* + * This is derived from rollback_index.h of vboot_reference. see struct + * RollbackSpaceKernel for details. + */ +static const uint8_t secdata_kernel[] = { + 0x02, + 0x4C, 0x57, 0x52, 0x47, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0xE8, +}; + +/* + * This is used to initialize the TPM space for recovery hash after defining + * it. Since there is no data available to calculate hash at the point where TPM + * space is defined, initialize it to all 0s. + */ +static const uint8_t rec_hash_data[REC_HASH_NV_SIZE] = { }; + +#if IS_ENABLED(CONFIG_TPM2) + +/* Nothing special in the TPM2 path yet. */ +static uint32_t safe_write(uint32_t index, const void *data, uint32_t length) +{ + return tlcl_write(index, data, length); +} + +static uint32_t set_firmware_space(const void *firmware_blob) +{ + RETURN_ON_FAILURE(tlcl_define_space(FIRMWARE_NV_INDEX, + VB2_SECDATA_SIZE)); + RETURN_ON_FAILURE(safe_write(FIRMWARE_NV_INDEX, firmware_blob, + VB2_SECDATA_SIZE)); + return TPM_SUCCESS; +} + +static uint32_t set_kernel_space(const void *kernel_blob) +{ + uint32_t rv; + + rv = tlcl_define_space(KERNEL_NV_INDEX, sizeof(secdata_kernel)); + if (rv == TPM_E_NV_DEFINED) { + VBDEBUG("%s: kernel space already exists\n", __func__); + return TPM_SUCCESS; + } + + if (rv != TPM_SUCCESS) + return rv; + + return safe_write(KERNEL_NV_INDEX, kernel_blob, sizeof(secdata_kernel)); +} + +static uint32_t set_rec_hash_space(const uint8_t *data) +{ + uint32_t rv; + + rv = tlcl_define_space(REC_HASH_NV_INDEX, REC_HASH_NV_SIZE); + if (rv == TPM_E_NV_DEFINED) { + VBDEBUG("%s: MRC Hash space already exists\n", __func__); + return TPM_SUCCESS; + } + + if (rv != TPM_SUCCESS) + return rv; + + return safe_write(REC_HASH_NV_INDEX, data, REC_HASH_NV_SIZE); +} + +static uint32_t _factory_initialize_tpm(struct vb2_context *ctx) +{ + RETURN_ON_FAILURE(tlcl_force_clear()); + + /* + * Of all NVRAM spaces defined by this function the firmware space + * must be defined last, because its existence is considered an + * indication that TPM factory initialization was successfully + * completed. + */ + RETURN_ON_FAILURE(set_kernel_space(secdata_kernel)); + + if (IS_ENABLED(CONFIG_VBOOT_HAS_REC_HASH_SPACE)) + RETURN_ON_FAILURE(set_rec_hash_space(rec_hash_data)); + + RETURN_ON_FAILURE(set_firmware_space(ctx->secdata)); + + return TPM_SUCCESS; +} + +uint32_t tpm_clear_and_reenable(void) +{ + VBDEBUG("TPM: Clear and re-enable\n"); + RETURN_ON_FAILURE(tlcl_force_clear()); + return TPM_SUCCESS; +} + +uint32_t antirollback_lock_space_firmware(void) +{ + return tlcl_lock_nv_write(FIRMWARE_NV_INDEX); +} + +uint32_t antirollback_lock_space_rec_hash(void) +{ + return tlcl_lock_nv_write(REC_HASH_NV_INDEX); +} + +#else + +uint32_t tpm_clear_and_reenable(void) +{ + VBDEBUG("TPM: Clear and re-enable\n"); + RETURN_ON_FAILURE(tlcl_force_clear()); + RETURN_ON_FAILURE(tlcl_set_enable()); + RETURN_ON_FAILURE(tlcl_set_deactivated(0)); + + return TPM_SUCCESS; +} + +/** + * Like tlcl_write(), but checks for write errors due to hitting the 64-write + * limit and clears the TPM when that happens. This can only happen when the + * TPM is unowned, so it is OK to clear it (and we really have no choice). + * This is not expected to happen frequently, but it could happen. + */ + +static uint32_t safe_write(uint32_t index, const void *data, uint32_t length) +{ + uint32_t result = tlcl_write(index, data, length); + if (result == TPM_E_MAXNVWRITES) { + RETURN_ON_FAILURE(tpm_clear_and_reenable()); + return tlcl_write(index, data, length); + } else { + return result; + } +} + +/** + * Similarly to safe_write(), this ensures we don't fail a DefineSpace because + * we hit the TPM write limit. This is even less likely to happen than with + * writes because we only define spaces once at initialization, but we'd + * rather be paranoid about this. + */ +static uint32_t safe_define_space(uint32_t index, uint32_t perm, uint32_t size) +{ + uint32_t result = tlcl_define_space(index, perm, size); + if (result == TPM_E_MAXNVWRITES) { + RETURN_ON_FAILURE(tpm_clear_and_reenable()); + return tlcl_define_space(index, perm, size); + } else { + return result; + } +} + +static uint32_t set_rec_hash_space(const uint8_t *data) +{ + RETURN_ON_FAILURE(safe_define_space(REC_HASH_NV_INDEX, + TPM_NV_PER_GLOBALLOCK | + TPM_NV_PER_PPWRITE, + REC_HASH_NV_SIZE)); + RETURN_ON_FAILURE(write_secdata(REC_HASH_NV_INDEX, data, + REC_HASH_NV_SIZE)); + + return TPM_SUCCESS; +} + +static uint32_t _factory_initialize_tpm(struct vb2_context *ctx) +{ + TPM_PERMANENT_FLAGS pflags; + uint32_t result; + + result = tlcl_get_permanent_flags(&pflags); + if (result != TPM_SUCCESS) + return result; + + /* + * TPM may come from the factory without physical presence finalized. + * Fix if necessary. + */ + VBDEBUG("TPM: physicalPresenceLifetimeLock=%d\n", + pflags.physicalPresenceLifetimeLock); + if (!pflags.physicalPresenceLifetimeLock) { + VBDEBUG("TPM: Finalizing physical presence\n"); + RETURN_ON_FAILURE(tlcl_finalize_physical_presence()); + } + + /* + * The TPM will not enforce the NV authorization restrictions until the + * execution of a TPM_NV_DefineSpace with the handle of + * TPM_NV_INDEX_LOCK. Here we create that space if it doesn't already + * exist. */ + VBDEBUG("TPM: nvLocked=%d\n", pflags.nvLocked); + if (!pflags.nvLocked) { + VBDEBUG("TPM: Enabling NV locking\n"); + RETURN_ON_FAILURE(tlcl_set_nv_locked()); + } + + /* Clear TPM owner, in case the TPM is already owned for some reason. */ + VBDEBUG("TPM: Clearing owner\n"); + RETURN_ON_FAILURE(tpm_clear_and_reenable()); + + /* Define and initialize the kernel space */ + RETURN_ON_FAILURE(safe_define_space(KERNEL_NV_INDEX, + TPM_NV_PER_PPWRITE, + sizeof(secdata_kernel))); + RETURN_ON_FAILURE(write_secdata(KERNEL_NV_INDEX, + secdata_kernel, + sizeof(secdata_kernel))); + + /* Defines and sets vb2 secdata space */ + vb2api_secdata_create(ctx); + RETURN_ON_FAILURE(safe_define_space(FIRMWARE_NV_INDEX, + TPM_NV_PER_GLOBALLOCK | + TPM_NV_PER_PPWRITE, + VB2_SECDATA_SIZE)); + RETURN_ON_FAILURE(write_secdata(FIRMWARE_NV_INDEX, + ctx->secdata, + VB2_SECDATA_SIZE)); + + /* Define and set rec hash space, if available. */ + if (IS_ENABLED(CONFIG_VBOOT_HAS_REC_HASH_SPACE)) + RETURN_ON_FAILURE(set_rec_hash_space(rec_hash_data)); + + return TPM_SUCCESS; +} + +uint32_t antirollback_lock_space_firmware(void) +{ + return tlcl_set_global_lock(); +} + +uint32_t antirollback_lock_space_rec_hash(void) +{ + /* + * Nothing needs to be done here, since global lock is already set while + * locking firmware space. + */ + return TPM_SUCCESS; +} +#endif + +/** + * Perform one-time initializations. + * + * Create the NVRAM spaces, and set their initial values as needed. Sets the + * nvLocked bit and ensures the physical presence command is enabled and + * locked. + */ +static uint32_t factory_initialize_tpm(struct vb2_context *ctx) +{ + uint32_t result; + + /* Defines and sets vb2 secdata space */ + vb2api_secdata_create(ctx); + + VBDEBUG("TPM: factory initialization\n"); + + /* + * Do a full test. This only happens the first time the device is + * turned on in the factory, so performance is not an issue. This is + * almost certainly not necessary, but it gives us more confidence + * about some code paths below that are difficult to + * test---specifically the ones that set lifetime flags, and are only + * executed once per physical TPM. + */ + result = tlcl_self_test_full(); + if (result != TPM_SUCCESS) + return result; + + result = _factory_initialize_tpm(ctx); + if (result != TPM_SUCCESS) + return result; + + VBDEBUG("TPM: factory initialization successful\n"); + + return TPM_SUCCESS; +} + +/* + * SetupTPM starts the TPM and establishes the root of trust for the + * anti-rollback mechanism. SetupTPM can fail for three reasons. 1 A bug. 2 a + * TPM hardware failure. 3 An unexpected TPM state due to some attack. In + * general we cannot easily distinguish the kind of failure, so our strategy is + * to reboot in recovery mode in all cases. The recovery mode calls SetupTPM + * again, which executes (almost) the same sequence of operations. There is a + * good chance that, if recovery mode was entered because of a TPM failure, the + * failure will repeat itself. (In general this is impossible to guarantee + * because we have no way of creating the exact TPM initial state at the + * previous boot.) In recovery mode, we ignore the failure and continue, thus + * giving the recovery kernel a chance to fix things (that's why we don't set + * bGlobalLock). The choice is between a knowingly insecure device and a + * bricked device. + * + * As a side note, observe that we go through considerable hoops to avoid using + * the STCLEAR permissions for the index spaces. We do this to avoid writing + * to the TPM flashram at every reboot or wake-up, because of concerns about + * the durability of the NVRAM. + */ +uint32_t setup_tpm(struct vb2_context *ctx) +{ + uint8_t disable; + uint8_t deactivated; + uint32_t result; + + RETURN_ON_FAILURE(tlcl_lib_init()); + + /* Handle special init for S3 resume path */ + if (ctx->flags & VB2_CONTEXT_S3_RESUME) { + result = tlcl_resume(); + if (result == TPM_E_INVALID_POSTINIT) + printk(BIOS_DEBUG, "TPM: Already initialized.\n"); + return TPM_SUCCESS; + } + + if (IS_ENABLED(CONFIG_VBOOT_SOFT_REBOOT_WORKAROUND)) { + result = tlcl_startup(); + if (result == TPM_E_INVALID_POSTINIT) { + /* + * Some prototype hardware doesn't reset the TPM on a CPU + * reset. We do a hard reset to get around this. + */ + VBDEBUG("TPM: soft reset detected\n"); + ctx->flags |= VB2_CONTEXT_SECDATA_WANTS_REBOOT; + return TPM_E_MUST_REBOOT; + } else if (result != TPM_SUCCESS) { + VBDEBUG("TPM: tlcl_startup returned %08x\n", result); + return result; + } + } else + RETURN_ON_FAILURE(tlcl_startup()); + + /* + * Some TPMs start the self test automatically at power on. In that case + * we don't need to call ContinueSelfTest. On some (other) TPMs, + * continue_self_test may block. In that case, we definitely don't want + * to call it here. For TPMs in the intersection of these two sets, we + * are screwed. (In other words: TPMs that require manually starting the + * self-test AND block will have poor performance until we split + * tlcl_send_receive() into send() and receive(), and have a state + * machine to control setup.) + * + * This comment is likely to become obsolete in the near future, so + * don't trust it. It may have not been updated. + */ +#ifdef TPM_MANUAL_SELFTEST +#ifdef TPM_BLOCKING_CONTINUESELFTEST +#warning "lousy TPM!" +#endif + RETURN_ON_FAILURE(tlcl_continue_self_test()); +#endif + result = tlcl_assert_physical_presence(); + if (result != TPM_SUCCESS) { + /* + * It is possible that the TPM was delivered with the physical + * presence command disabled. This tries enabling it, then + * tries asserting PP again. + */ + RETURN_ON_FAILURE(tlcl_physical_presence_cmd_enable()); + RETURN_ON_FAILURE(tlcl_assert_physical_presence()); + } + + /* Check that the TPM is enabled and activated. */ + RETURN_ON_FAILURE(tlcl_get_flags(&disable, &deactivated, NULL)); + if (disable || deactivated) { + VBDEBUG("TPM: disabled (%d) or deactivated (%d). Fixing...\n", + disable, deactivated); + RETURN_ON_FAILURE(tlcl_set_enable()); + RETURN_ON_FAILURE(tlcl_set_deactivated(0)); + VBDEBUG("TPM: Must reboot to re-enable\n"); + ctx->flags |= VB2_CONTEXT_SECDATA_WANTS_REBOOT; + return TPM_E_MUST_REBOOT; + } + + VBDEBUG("TPM: SetupTPM() succeeded\n"); + return TPM_SUCCESS; +} + +uint32_t antirollback_read_space_firmware(struct vb2_context *ctx) +{ + uint32_t rv; + + rv = setup_tpm(ctx); + if (rv) + return rv; + + /* Read the firmware space. */ + rv = read_space_firmware(ctx); + if (rv == TPM_E_BADINDEX) { + /* + * This seems the first time we've run. Initialize the TPM. + */ + VBDEBUG("TPM: Not initialized yet.\n"); + RETURN_ON_FAILURE(factory_initialize_tpm(ctx)); + } else if (rv != TPM_SUCCESS) { + VBDEBUG("TPM: Firmware space in a bad state; giving up.\n"); + //RETURN_ON_FAILURE(factory_initialize_tpm(ctx)); + return TPM_E_CORRUPTED_STATE; + } + + return TPM_SUCCESS; +} + +uint32_t antirollback_write_space_firmware(struct vb2_context *ctx) +{ + if (IS_ENABLED(CONFIG_CR50_IMMEDIATELY_COMMIT_FW_SECDATA)) + tlcl_cr50_enable_nvcommits(); + return write_secdata(FIRMWARE_NV_INDEX, ctx->secdata, VB2_SECDATA_SIZE); +} + +uint32_t antirollback_read_space_rec_hash(uint8_t *data, uint32_t size) +{ + if (size != REC_HASH_NV_SIZE) { + VBDEBUG("TPM: Incorrect buffer size for rec hash. " + "(Expected=0x%x Actual=0x%x).\n", REC_HASH_NV_SIZE, + size); + return TPM_E_READ_FAILURE; + } + return read_space_rec_hash(data); +} + +uint32_t antirollback_write_space_rec_hash(const uint8_t *data, uint32_t size) +{ + uint8_t spc_data[REC_HASH_NV_SIZE]; + uint32_t rv; + + if (size != REC_HASH_NV_SIZE) { + VBDEBUG("TPM: Incorrect buffer size for rec hash. " + "(Expected=0x%x Actual=0x%x).\n", REC_HASH_NV_SIZE, + size); + return TPM_E_WRITE_FAILURE; + } + + rv = read_space_rec_hash(spc_data); + if (rv == TPM_E_BADINDEX) { + /* + * If space is not defined already for recovery hash, define + * new space. + */ + VBDEBUG("TPM: Initializing recovery hash space.\n"); + return set_rec_hash_space(data); + } + + if (rv != TPM_SUCCESS) + return rv; + + return write_secdata(REC_HASH_NV_INDEX, data, size); +} diff --git a/src/security/vboot/symbols.h b/src/security/vboot/symbols.h new file mode 100644 index 0000000000..fb3d6545f8 --- /dev/null +++ b/src/security/vboot/symbols.h @@ -0,0 +1,28 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2016 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __VBOOT_SYMBOLS_H__ +#define __VBOOT_SYMBOLS_H__ + +extern u8 _vboot2_work[]; +extern u8 _evboot2_work[]; +#define _vboot2_work_size (_evboot2_work - _vboot2_work) + +/* Careful: _e<stage> and _<stage>_size only defined for the current stage! */ +extern u8 _verstage[]; +extern u8 _everstage[]; +#define _verstage_size (_everstage - _verstage) + +#endif /* __VBOOT_SYMBOLS_H__ */ diff --git a/src/security/vboot/vbnv.c b/src/security/vboot/vbnv.c new file mode 100644 index 0000000000..0fecacd4d2 --- /dev/null +++ b/src/security/vboot/vbnv.c @@ -0,0 +1,155 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2016 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <arch/early_variables.h> +#include <string.h> +#include <types.h> +#include <security/vboot/vbnv.h> +#include <security/vboot/vbnv_layout.h> + +static int vbnv_initialized CAR_GLOBAL; +static uint8_t vbnv[VBOOT_VBNV_BLOCK_SIZE] CAR_GLOBAL; + +/* Wrappers for accessing the variables marked as CAR_GLOBAL. */ +static inline int is_vbnv_initialized(void) +{ + return car_get_var(vbnv_initialized); +} + +static inline uint8_t *vbnv_data_addr(int index) +{ + uint8_t *vbnv_arr = car_get_var_ptr(vbnv); + + return &vbnv_arr[index]; +} + +static inline uint8_t vbnv_data(int index) +{ + return *vbnv_data_addr(index); +} + +/* Return CRC-8 of the data, using x^8 + x^2 + x + 1 polynomial. */ +static uint8_t crc8_vbnv(const uint8_t *data, int len) +{ + unsigned crc = 0; + int i, j; + + for (j = len; j; j--, data++) { + crc ^= (*data << 8); + for (i = 8; i; i--) { + if (crc & 0x8000) + crc ^= (0x1070 << 3); + crc <<= 1; + } + } + + return (uint8_t) (crc >> 8); +} + +void vbnv_reset(uint8_t *vbnv_copy) +{ + memset(vbnv_copy, 0, VBOOT_VBNV_BLOCK_SIZE); +} + +/* Read VBNV data into cache. */ +static void vbnv_setup(void) +{ + if (!is_vbnv_initialized()) { + read_vbnv(vbnv_data_addr(0)); + car_set_var(vbnv_initialized, 1); + } +} + +/* Verify VBNV header and checksum. */ +int verify_vbnv(uint8_t *vbnv_copy) +{ + return (HEADER_SIGNATURE == (vbnv_copy[HEADER_OFFSET] & HEADER_MASK)) && + (crc8_vbnv(vbnv_copy, CRC_OFFSET) == vbnv_copy[CRC_OFFSET]); +} + +/* Re-generate VBNV checksum. */ +void regen_vbnv_crc(uint8_t *vbnv_copy) +{ + vbnv_copy[CRC_OFFSET] = crc8_vbnv(vbnv_copy, CRC_OFFSET); +} + +/* + * Read VBNV data from configured storage backend. + * If VBNV verification fails, reset the vbnv copy. + */ +void read_vbnv(uint8_t *vbnv_copy) +{ + if (IS_ENABLED(CONFIG_VBOOT_VBNV_CMOS)) + read_vbnv_cmos(vbnv_copy); + else if (IS_ENABLED(CONFIG_VBOOT_VBNV_EC)) + read_vbnv_ec(vbnv_copy); + else if (IS_ENABLED(CONFIG_VBOOT_VBNV_FLASH)) + read_vbnv_flash(vbnv_copy); + + /* Check data for consistency */ + if (!verify_vbnv(vbnv_copy)) + vbnv_reset(vbnv_copy); +} + +/* + * Write VBNV data to configured storage backend. + * This assumes that the caller has updated the CRC already. + */ +void save_vbnv(const uint8_t *vbnv_copy) +{ + if (IS_ENABLED(CONFIG_VBOOT_VBNV_CMOS)) + save_vbnv_cmos(vbnv_copy); + else if (IS_ENABLED(CONFIG_VBOOT_VBNV_EC)) + save_vbnv_ec(vbnv_copy); + else if (IS_ENABLED(CONFIG_VBOOT_VBNV_FLASH)) + save_vbnv_flash(vbnv_copy); + + /* Clear initialized flag to force cached data to be updated */ + car_set_var(vbnv_initialized, 0); +} + +/* Save a recovery reason into VBNV. */ +void set_recovery_mode_into_vbnv(int recovery_reason) +{ + uint8_t vbnv_copy[VBOOT_VBNV_BLOCK_SIZE]; + + read_vbnv(vbnv_copy); + + vbnv_copy[RECOVERY_OFFSET] = recovery_reason; + vbnv_copy[CRC_OFFSET] = crc8_vbnv(vbnv_copy, CRC_OFFSET); + + save_vbnv(vbnv_copy); +} + +/* Read the recovery reason from VBNV. */ +int get_recovery_mode_from_vbnv(void) +{ + vbnv_setup(); + return vbnv_data(RECOVERY_OFFSET); +} + +/* Read the BOOT_OPROM_NEEDED flag from VBNV. */ +int vboot_wants_oprom(void) +{ + vbnv_setup(); + return (vbnv_data(BOOT_OFFSET) & BOOT_OPROM_NEEDED) ? 1 : 0; +} + +void vbnv_init(uint8_t *vbnv_copy) +{ + if (IS_ENABLED(CONFIG_VBOOT_VBNV_CMOS)) + vbnv_init_cmos(vbnv_copy); + read_vbnv(vbnv_copy); +} diff --git a/src/security/vboot/vbnv.h b/src/security/vboot/vbnv.h new file mode 100644 index 0000000000..0288d0df4d --- /dev/null +++ b/src/security/vboot/vbnv.h @@ -0,0 +1,51 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2016 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __VBOOT_VBNV_H__ +#define __VBOOT_VBNV_H__ + +#include <types.h> + +/* Generic functions */ +void read_vbnv(uint8_t *vbnv_copy); +void save_vbnv(const uint8_t *vbnv_copy); +int verify_vbnv(uint8_t *vbnv_copy); +void regen_vbnv_crc(uint8_t *vbnv_copy); +int get_recovery_mode_from_vbnv(void); +void set_recovery_mode_into_vbnv(int recovery_reason); +int vboot_wants_oprom(void); +/* Initialize and read vbnv. This is used in the main vboot logic path. */ +void vbnv_init(uint8_t *vbnv_copy); +/* Reset vbnv snapshot to a known state. */ +void vbnv_reset(uint8_t *vbnv_copy); + +/* CMOS backend */ +/* Initialize the vbnv cmos backing store. The vbnv_copy pointer is used for + optional temporary storage in the init function. */ +void vbnv_init_cmos(uint8_t *vbnv_copy); +/* Return non-zero if cmos power was lost. */ +int vbnv_cmos_failed(void); +void read_vbnv_cmos(uint8_t *vbnv_copy); +void save_vbnv_cmos(const uint8_t *vbnv_copy); + +/* Flash backend */ +void read_vbnv_flash(uint8_t *vbnv_copy); +void save_vbnv_flash(const uint8_t *vbnv_copy); + +/* EC backend */ +void read_vbnv_ec(uint8_t *vbnv_copy); +void save_vbnv_ec(const uint8_t *vbnv_copy); + +#endif /* __VBOOT_VBNV_H__ */ diff --git a/src/security/vboot/vbnv_cmos.c b/src/security/vboot/vbnv_cmos.c new file mode 100644 index 0000000000..9c801d84fe --- /dev/null +++ b/src/security/vboot/vbnv_cmos.c @@ -0,0 +1,114 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <bootstate.h> +#include <console/console.h> +#include <types.h> +#include <pc80/mc146818rtc.h> +#include <security/vboot/vbnv.h> +#include <security/vboot/vbnv_layout.h> + +static void clear_vbnv_battery_cutoff_flag(uint8_t *vbnv_copy) +{ + /* + * Currently battery cutoff is done in payload stage, which does not + * update backup VBNV. And doing battery cutoff will invalidate CMOS. + * This means for every reboot after cutoff, read_vbnv_cmos will reload + * backup VBNV and try to cutoff again, causing endless reboot loop. + * So we should always clear battery cutoff flag from loaded backup. + */ + if (vbnv_copy[MISC_FLAGS_OFFSET] & MISC_FLAGS_BATTERY_CUTOFF_MASK) { + printk(BIOS_INFO, "VBNV: Remove battery cut-off request.\n"); + vbnv_copy[MISC_FLAGS_OFFSET] &= ~MISC_FLAGS_BATTERY_CUTOFF_MASK; + regen_vbnv_crc(vbnv_copy); + } +} + +/* Return non-zero if backup was used. */ +static int restore_from_backup(uint8_t *vbnv_copy) +{ + if (!IS_ENABLED(CONFIG_VBOOT_VBNV_CMOS_BACKUP_TO_FLASH)) + return 0; + + printk(BIOS_INFO, "VBNV: CMOS invalid, restoring from flash\n"); + read_vbnv_flash(vbnv_copy); + + if (verify_vbnv(vbnv_copy)) { + clear_vbnv_battery_cutoff_flag(vbnv_copy); + save_vbnv_cmos(vbnv_copy); + printk(BIOS_INFO, "VBNV: Flash backup restored\n"); + return 1; + } + + printk(BIOS_INFO, "VBNV: Restore from flash failed\n"); + + return 0; +} + +void read_vbnv_cmos(uint8_t *vbnv_copy) +{ + int i; + + for (i = 0; i < VBOOT_VBNV_BLOCK_SIZE; i++) + vbnv_copy[i] = cmos_read(CONFIG_VBOOT_VBNV_OFFSET + 14 + i); + + /* Verify contents before attempting a restore from backup storage. */ + if (verify_vbnv(vbnv_copy)) + return; + + restore_from_backup(vbnv_copy); +} + +void save_vbnv_cmos(const uint8_t *vbnv_copy) +{ + int i; + + for (i = 0; i < VBOOT_VBNV_BLOCK_SIZE; i++) + cmos_write(vbnv_copy[i], CONFIG_VBOOT_VBNV_OFFSET + 14 + i); +} + +void vbnv_init_cmos(uint8_t *vbnv_copy) +{ + /* If no cmos failure just defer to the normal read path for checking + vbnv contents' integrity. */ + if (!vbnv_cmos_failed()) + return; + + /* In the case of cmos failure force the backup. If backup wasn't used + force the vbnv cmos to be reset. */ + if (!restore_from_backup(vbnv_copy)) { + vbnv_reset(vbnv_copy); + /* This parallels the vboot_reference implementation. */ + vbnv_copy[HEADER_OFFSET] = HEADER_SIGNATURE | + HEADER_FIRMWARE_SETTINGS_RESET | + HEADER_KERNEL_SETTINGS_RESET; + regen_vbnv_crc(vbnv_copy); + save_vbnv_cmos(vbnv_copy); + } +} + +#if IS_ENABLED(CONFIG_VBOOT_VBNV_CMOS_BACKUP_TO_FLASH) +static void back_up_vbnv_cmos(void *unused) +{ + uint8_t vbnv_cmos[VBOOT_VBNV_BLOCK_SIZE]; + + /* Read current VBNV from CMOS. */ + read_vbnv_cmos(vbnv_cmos); + + /* Save to flash, will only be saved if different. */ + save_vbnv_flash(vbnv_cmos); +} +BOOT_STATE_INIT_ENTRY(BS_POST_DEVICE, BS_ON_EXIT, back_up_vbnv_cmos, NULL); +#endif diff --git a/src/security/vboot/vbnv_ec.c b/src/security/vboot/vbnv_ec.c new file mode 100644 index 0000000000..d73423eb75 --- /dev/null +++ b/src/security/vboot/vbnv_ec.c @@ -0,0 +1,30 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <types.h> +#include <ec/google/chromeec/ec.h> +#include <security/vboot/vbnv.h> +#include <security/vboot/vbnv_layout.h> + +void read_vbnv_ec(uint8_t *vbnv_copy) +{ + google_chromeec_vbnv_context(1, vbnv_copy, VBOOT_VBNV_BLOCK_SIZE); +} + +void save_vbnv_ec(const uint8_t *vbnv_copy) +{ + google_chromeec_vbnv_context(0, (uint8_t *)vbnv_copy, + VBOOT_VBNV_BLOCK_SIZE); +} diff --git a/src/security/vboot/vbnv_flash.c b/src/security/vboot/vbnv_flash.c new file mode 100644 index 0000000000..07569fa48d --- /dev/null +++ b/src/security/vboot/vbnv_flash.c @@ -0,0 +1,181 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <arch/early_variables.h> +#include <assert.h> +#include <commonlib/region.h> +#include <console/console.h> +#include <string.h> +#include <vb2_api.h> +#include <vboot_nvstorage.h> +#include <security/vboot/vboot_common.h> +#include <security/vboot/vbnv.h> +#include <security/vboot/vbnv_layout.h> + +#define BLOB_SIZE VB2_NVDATA_SIZE + +struct vbnv_flash_ctx { + /* VBNV flash is initialized */ + int initialized; + + /* Offset of the current nvdata in flash */ + int blob_offset; + + /* Offset of the topmost nvdata blob in flash */ + int top_offset; + + /* Region to store and retrieve the VBNV contents. */ + struct region_device vbnv_dev; + + /* Cache of the current nvdata */ + uint8_t cache[BLOB_SIZE]; +}; +static struct vbnv_flash_ctx vbnv_flash CAR_GLOBAL; + +/* + * This code assumes that flash is erased to 1-bits, and write operations can + * only change 1-bits to 0-bits. So if the new contents only change 1-bits to + * 0-bits, we can reuse the current blob. + */ +static inline uint8_t erase_value(void) +{ + return 0xff; +} + +static inline int can_overwrite(uint8_t current, uint8_t new) +{ + return (current & new) == new; +} + +static int init_vbnv(void) +{ + struct vbnv_flash_ctx *ctx = car_get_var_ptr(&vbnv_flash); + struct region_device *rdev = &ctx->vbnv_dev; + uint8_t buf[BLOB_SIZE]; + uint8_t empty_blob[BLOB_SIZE]; + int used_below, empty_above; + int offset; + int i; + + if (vboot_named_region_device_rw("RW_NVRAM", rdev) || + region_device_sz(rdev) < BLOB_SIZE) { + printk(BIOS_ERR, "%s: failed to locate NVRAM\n", __func__); + return 1; + } + + /* Prepare an empty blob to compare against. */ + for (i = 0; i < BLOB_SIZE; i++) + empty_blob[i] = erase_value(); + + ctx->top_offset = region_device_sz(rdev) - BLOB_SIZE; + + /* Binary search for the border between used and empty */ + used_below = 0; + empty_above = region_device_sz(rdev) / BLOB_SIZE; + + while (used_below + 1 < empty_above) { + int guess = (used_below + empty_above) / 2; + if (rdev_readat(rdev, buf, guess * BLOB_SIZE, BLOB_SIZE) < 0) { + printk(BIOS_ERR, "failed to read nvdata\n"); + return 1; + } + if (!memcmp(buf, empty_blob, BLOB_SIZE)) + empty_above = guess; + else + used_below = guess; + } + + /* + * Offset points to the last non-empty blob. Or if all blobs are empty + * (nvram is totally erased), point to the first blob. + */ + offset = used_below * BLOB_SIZE; + + /* reread the nvdata and write it to the cache */ + if (rdev_readat(rdev, ctx->cache, offset, BLOB_SIZE) < 0) { + printk(BIOS_ERR, "failed to read nvdata\n"); + return 1; + } + + ctx->blob_offset = offset; + ctx->initialized = 1; + + return 0; +} + +static int erase_nvram(void) +{ + struct vbnv_flash_ctx *ctx = car_get_var_ptr(&vbnv_flash); + const struct region_device *rdev = &ctx->vbnv_dev; + + if (rdev_eraseat(rdev, 0, region_device_sz(rdev)) < 0) { + printk(BIOS_ERR, "failed to erase nvram\n"); + return 1; + } + + printk(BIOS_INFO, "nvram is cleared\n"); + return 0; +} + +void read_vbnv_flash(uint8_t *vbnv_copy) +{ + struct vbnv_flash_ctx *ctx = car_get_var_ptr(&vbnv_flash); + + if (!ctx->initialized) + if (init_vbnv()) + return; /* error */ + + memcpy(vbnv_copy, ctx->cache, BLOB_SIZE); +} + +void save_vbnv_flash(const uint8_t *vbnv_copy) +{ + struct vbnv_flash_ctx *ctx = car_get_var_ptr(&vbnv_flash); + int new_offset; + int i; + const struct region_device *rdev = &ctx->vbnv_dev; + + if (!ctx->initialized) + if (init_vbnv()) + return; /* error */ + + /* Bail out if there have been no changes. */ + if (!memcmp(vbnv_copy, ctx->cache, BLOB_SIZE)) + return; + + new_offset = ctx->blob_offset; + + /* See if we can overwrite the current blob with the new one */ + for (i = 0; i < BLOB_SIZE; i++) { + if (!can_overwrite(ctx->cache[i], vbnv_copy[i])) { + /* unable to overwrite. need to use the next blob */ + new_offset += BLOB_SIZE; + if (new_offset > ctx->top_offset) { + if (erase_nvram()) + return; /* error */ + new_offset = 0; + } + break; + } + } + + if (rdev_writeat(rdev, vbnv_copy, new_offset, BLOB_SIZE) == BLOB_SIZE) { + /* write was successful. safely move pointer forward */ + ctx->blob_offset = new_offset; + memcpy(ctx->cache, vbnv_copy, BLOB_SIZE); + } else { + printk(BIOS_ERR, "failed to save nvdata\n"); + } +} diff --git a/src/security/vboot/vbnv_layout.h b/src/security/vboot/vbnv_layout.h new file mode 100644 index 0000000000..1dc01c909a --- /dev/null +++ b/src/security/vboot/vbnv_layout.h @@ -0,0 +1,50 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2015 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __VBOOT_VBNV_LAYOUT_H__ +#define __VBOOT_VBNV_LAYOUT_H__ + +#define VBOOT_VBNV_BLOCK_SIZE 16 /* Size of NV storage block in bytes */ + +/* Constants for NV storage. We use this rather than structs and + * bitfields so the data format is consistent across platforms and + * compilers. + */ +#define HEADER_OFFSET 0 +#define HEADER_MASK 0xC0 +#define HEADER_SIGNATURE 0x40 +#define HEADER_FIRMWARE_SETTINGS_RESET 0x20 +#define HEADER_KERNEL_SETTINGS_RESET 0x10 + +#define BOOT_OFFSET 1 +#define BOOT_DEBUG_RESET_MODE 0x80 +#define BOOT_DISABLE_DEV_REQUEST 0x40 +#define BOOT_OPROM_NEEDED 0x20 +#define BOOT_TRY_B_COUNT_MASK 0x0F + +#define RECOVERY_OFFSET 2 +#define LOCALIZATION_OFFSET 3 + +#define DEV_FLAGS_OFFSET 4 +#define DEV_BOOT_USB_MASK 0x01 +#define DEV_BOOT_SIGNED_ONLY_MASK 0x02 + +#define MISC_FLAGS_OFFSET 8 +#define MISC_FLAGS_BATTERY_CUTOFF_MASK 0x08 + +#define KERNEL_FIELD_OFFSET 11 +#define CRC_OFFSET 15 + +#endif /* __VBOOT_VBNV_LAYOUT_H__ */ diff --git a/src/security/vboot/vboot_common.c b/src/security/vboot/vboot_common.c new file mode 100644 index 0000000000..3ef90706ed --- /dev/null +++ b/src/security/vboot/vboot_common.c @@ -0,0 +1,115 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <boot/coreboot_tables.h> +#include <boot_device.h> +#include <cbmem.h> +#include <console/cbmem_console.h> +#include <console/console.h> +#include <fmap.h> +#include <reset.h> +#include <rules.h> +#include <stddef.h> +#include <string.h> +#include <security/vboot/vboot_common.h> + +int vboot_named_region_device(const char *name, struct region_device *rdev) +{ + return fmap_locate_area_as_rdev(name, rdev); +} + +int vboot_named_region_device_rw(const char *name, struct region_device *rdev) +{ + return fmap_locate_area_as_rdev_rw(name, rdev); +} + +/* ========================== VBOOT HANDOFF APIs =========================== */ +int vboot_get_handoff_info(void **addr, uint32_t *size) +{ + /* + * vboot_handoff is present only after cbmem comes online. If we are in + * pre-ram stage, then bail out early. + */ + if (ENV_BOOTBLOCK || + (ENV_VERSTAGE && IS_ENABLED(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK))) + return -1; + + struct vboot_handoff *vboot_handoff; + vboot_handoff = cbmem_find(CBMEM_ID_VBOOT_HANDOFF); + + if (vboot_handoff == NULL) + return -1; + + *addr = vboot_handoff; + + if (size) + *size = sizeof(*vboot_handoff); + return 0; +} + +static int vboot_get_handoff_flag(uint32_t flag) +{ + struct vboot_handoff *vbho; + + /* + * If vboot_handoff cannot be found, return default value of flag as 0. + */ + if (vboot_get_handoff_info((void **)&vbho, NULL)) + return 0; + + return !!(vbho->init_params.out_flags & flag); +} + +int vboot_handoff_skip_display_init(void) +{ + return !vboot_get_handoff_flag(VB_INIT_OUT_ENABLE_DISPLAY); +} + +int vboot_handoff_check_developer_flag(void) +{ + return vboot_get_handoff_flag(VB_INIT_OUT_ENABLE_DEVELOPER); +} + +int vboot_handoff_check_recovery_flag(void) +{ + return vboot_get_handoff_flag(VB_INIT_OUT_ENABLE_RECOVERY); +} + +int vboot_handoff_get_recovery_reason(void) +{ + struct vboot_handoff *vbho; + VbSharedDataHeader *sd; + + if (vboot_get_handoff_info((void **)&vbho, NULL)) + return 0; + + sd = (VbSharedDataHeader *)vbho->shared_data; + + return sd->recovery_reason; +} + +/* ============================ VBOOT REBOOT ============================== */ +void __attribute__((weak)) vboot_platform_prepare_reboot(void) +{ +} + +void vboot_reboot(void) +{ + if (IS_ENABLED(CONFIG_CONSOLE_CBMEM_DUMP_TO_UART)) + cbmem_dump_console(); + vboot_platform_prepare_reboot(); + hard_reset(); + die("failed to reboot"); +} diff --git a/src/security/vboot/vboot_common.h b/src/security/vboot/vboot_common.h new file mode 100644 index 0000000000..8c92f7f131 --- /dev/null +++ b/src/security/vboot/vboot_common.h @@ -0,0 +1,118 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __VBOOT_VBOOT_COMMON_H__ +#define __VBOOT_VBOOT_COMMON_H__ + +#include <commonlib/region.h> +#include <stdint.h> +#include <compiler.h> +#include <vboot_api.h> +#include <vboot_struct.h> + +/* Locate vboot area by name. Returns 0 on success and -1 on error. */ +int vboot_named_region_device(const char *name, struct region_device *rdev); + +/* Like vboot_named_region_device() but provides a RW region device. */ +int vboot_named_region_device_rw(const char *name, struct region_device *rdev); + +/* + * Function to check if there is a request to enter recovery mode. Returns + * reason code if request to enter recovery mode is present, otherwise 0. + */ +int vboot_check_recovery_request(void); + +/* ========================== VBOOT HANDOFF APIs =========================== */ +/* + * The vboot_handoff structure contains the data to be consumed by downstream + * firmware after firmware selection has been completed. Namely it provides + * vboot shared data as well as the flags from VbInit. + */ +struct vboot_handoff { + VbInitParams init_params; + uint32_t selected_firmware; + char shared_data[VB_SHARED_DATA_MIN_SIZE]; +} __packed; + +/* + * vboot_get_handoff_info returns pointer to the vboot_handoff structure if + * available. vboot_handoff is available only after CBMEM comes online. If size + * is not NULL, size of the vboot_handoff structure is returned in it. + * Returns 0 on success and -1 on error. + */ +int vboot_get_handoff_info(void **addr, uint32_t *size); + +/* + * The following functions read vboot_handoff structure to obtain requested + * information. If vboot handoff is not available, 0 is returned by default. + * If vboot handoff is available: + * Returns 1 for flag if true + * Returns 0 for flag if false + * Returns value read for other fields + */ +int vboot_handoff_skip_display_init(void); +int vboot_handoff_check_recovery_flag(void); +int vboot_handoff_check_developer_flag(void); +int vboot_handoff_get_recovery_reason(void); + +/* ============================ VBOOT REBOOT ============================== */ +/* + * vboot_reboot handles the reboot requests made by vboot_reference library. It + * allows the platform to run any preparation steps before the reboot and then + * does a hard reset. + */ +void vboot_reboot(void); + +/* Allow the platform to do any clean up work when vboot requests a reboot. */ +void vboot_platform_prepare_reboot(void); + +/* ============================ VBOOT RESUME ============================== */ +/* + * Save the provided hash digest to a secure location to check against in + * the resume path. Returns 0 on success, < 0 on error. + */ +int vboot_save_hash(void *digest, size_t digest_size); + +/* + * Retrieve the previously saved hash digest. Returns 0 on success, + * < 0 on error. + */ +int vboot_retrieve_hash(void *digest, size_t digest_size); + +/* + * Determine if the platform is resuming from suspend. Returns 0 when + * not resuming, > 0 if resuming, and < 0 on error. + */ +int vboot_platform_is_resuming(void); + +/* ============================= VERSTAGE ================================== */ +/* + * Main logic for verified boot. verstage_main() is just the core vboot logic. + * If the verstage is a separate stage, it should be entered via main(). + */ +void verstage_main(void); +void verstage_mainboard_init(void); + +/* Check boot modes */ +#if IS_ENABLED(CONFIG_VBOOT) +int vboot_developer_mode_enabled(void); +int vboot_recovery_mode_enabled(void); +int vboot_recovery_mode_memory_retrain(void); +#else /* !CONFIG_VBOOT */ +static inline int vboot_developer_mode_enabled(void) { return 0; } +static inline int vboot_recovery_mode_enabled(void) { return 0; } +static inline int vboot_recovery_mode_memory_retrain(void) { return 0; } +#endif + +#endif /* __VBOOT_VBOOT_COMMON_H__ */ diff --git a/src/security/vboot/vboot_handoff.c b/src/security/vboot/vboot_handoff.c new file mode 100644 index 0000000000..9fecc1a1a8 --- /dev/null +++ b/src/security/vboot/vboot_handoff.c @@ -0,0 +1,190 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2013 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* This needs to be pulled in first so that the handoff code below and + * peek into the vb2 data structures. Additionally, vboot doesn't currently + * include what it uses in its own headers. Provide the types it's after. + * TODO: fix this necessity. */ +#define NEED_VB20_INTERNALS +#include <stddef.h> +#include <stdint.h> +#include <vb2_api.h> + +#include <arch/stages.h> +#include <assert.h> +#include <bootmode.h> +#include <string.h> +#include <cbfs.h> +#include <cbmem.h> +#include <console/console.h> +#include <console/vtxprintf.h> +#include <fmap.h> +#include <stdlib.h> +#include <timestamp.h> +#include <vboot_struct.h> +#include <security/vboot/vbnv.h> +#include <security/vboot/misc.h> + +/** + * Sets vboot_handoff based on the information in vb2_shared_data + */ +static void fill_vboot_handoff(struct vboot_handoff *vboot_handoff, + struct vb2_shared_data *vb2_sd) +{ + VbSharedDataHeader *vb_sd = + (VbSharedDataHeader *)vboot_handoff->shared_data; + uint32_t *oflags = &vboot_handoff->init_params.out_flags; + + vb_sd->flags |= VBSD_BOOT_FIRMWARE_VBOOT2; + + vboot_handoff->selected_firmware = vb2_sd->fw_slot; + + vb_sd->firmware_index = vb2_sd->fw_slot; + + vb_sd->magic = VB_SHARED_DATA_MAGIC; + vb_sd->struct_version = VB_SHARED_DATA_VERSION; + vb_sd->struct_size = sizeof(VbSharedDataHeader); + vb_sd->data_size = VB_SHARED_DATA_MIN_SIZE; + vb_sd->data_used = sizeof(VbSharedDataHeader); + vb_sd->fw_version_tpm = vb2_sd->fw_version_secdata; + + if (get_write_protect_state()) + vb_sd->flags |= VBSD_BOOT_FIRMWARE_WP_ENABLED; + + if (vb2_sd->recovery_reason) { + vb_sd->firmware_index = 0xFF; + if (vb2_sd->flags & VB2_SD_FLAG_MANUAL_RECOVERY) + vb_sd->flags |= VBSD_BOOT_REC_SWITCH_ON; + *oflags |= VB_INIT_OUT_ENABLE_RECOVERY; + *oflags |= VB_INIT_OUT_CLEAR_RAM; + *oflags |= VB_INIT_OUT_ENABLE_DISPLAY; + *oflags |= VB_INIT_OUT_ENABLE_USB_STORAGE; + } + if (vb2_sd->flags & VB2_SD_DEV_MODE_ENABLED) { + *oflags |= VB_INIT_OUT_ENABLE_DEVELOPER; + *oflags |= VB_INIT_OUT_CLEAR_RAM; + *oflags |= VB_INIT_OUT_ENABLE_DISPLAY; + *oflags |= VB_INIT_OUT_ENABLE_USB_STORAGE; + vb_sd->flags |= VBSD_BOOT_DEV_SWITCH_ON; + vb_sd->flags |= VBSD_LF_DEV_SWITCH_ON; + } + /* TODO: Set these in depthcharge */ + if (!IS_ENABLED(CONFIG_VBOOT_PHYSICAL_DEV_SWITCH)) + vb_sd->flags |= VBSD_HONOR_VIRT_DEV_SWITCH; + if (IS_ENABLED(CONFIG_VBOOT_EC_SOFTWARE_SYNC)) { + vb_sd->flags |= VBSD_EC_SOFTWARE_SYNC; + if (IS_ENABLED(CONFIG_VBOOT_EC_SLOW_UPDATE)) + vb_sd->flags |= VBSD_EC_SLOW_UPDATE; + if (IS_ENABLED(CONFIG_VBOOT_EC_EFS)) + vb_sd->flags |= VBSD_EC_EFS; + } + if (!IS_ENABLED(CONFIG_VBOOT_PHYSICAL_REC_SWITCH)) + vb_sd->flags |= VBSD_BOOT_REC_SWITCH_VIRTUAL; + if (IS_ENABLED(CONFIG_VBOOT_OPROM_MATTERS)) { + vb_sd->flags |= VBSD_OPROM_MATTERS; + /* + * Inform vboot if the display was enabled by dev/rec + * mode or was requested by vboot kernel phase. + */ + if ((*oflags & VB_INIT_OUT_ENABLE_DISPLAY) || + vboot_wants_oprom()) { + vb_sd->flags |= VBSD_OPROM_LOADED; + *oflags |= VB_INIT_OUT_ENABLE_DISPLAY; + } + } + + /* In vboot1, VBSD_FWB_TRIED is + * set only if B is booted as explicitly requested. Therefore, if B is + * booted because A was found bad, the flag should not be set. It's + * better not to touch it if we can only ambiguously control it. */ + /* if (vb2_sd->fw_slot) + vb_sd->flags |= VBSD_FWB_TRIED; */ + + /* copy kernel subkey if it's found */ + if (vb2_sd->workbuf_preamble_size) { + struct vb2_fw_preamble *fp; + uintptr_t dst, src; + printk(BIOS_INFO, "Copying FW preamble\n"); + fp = (struct vb2_fw_preamble *)((uintptr_t)vb2_sd + + vb2_sd->workbuf_preamble_offset); + src = (uintptr_t)&fp->kernel_subkey + + fp->kernel_subkey.key_offset; + dst = (uintptr_t)vb_sd + sizeof(VbSharedDataHeader); + assert(dst + fp->kernel_subkey.key_size <= + (uintptr_t)vboot_handoff + sizeof(*vboot_handoff)); + memcpy((void *)dst, (void *)src, + fp->kernel_subkey.key_size); + vb_sd->data_used += fp->kernel_subkey.key_size; + vb_sd->kernel_subkey.key_offset = + dst - (uintptr_t)&vb_sd->kernel_subkey; + vb_sd->kernel_subkey.key_size = fp->kernel_subkey.key_size; + vb_sd->kernel_subkey.algorithm = fp->kernel_subkey.algorithm; + vb_sd->kernel_subkey.key_version = + fp->kernel_subkey.key_version; + } + + vb_sd->recovery_reason = vb2_sd->recovery_reason; +} + +void vboot_fill_handoff(void) +{ + struct vboot_handoff *vh; + struct vb2_shared_data *sd; + + sd = vb2_get_shared_data(); + sd->workbuf_hash_offset = 0; + sd->workbuf_hash_size = 0; + + printk(BIOS_INFO, "creating vboot_handoff structure\n"); + vh = cbmem_add(CBMEM_ID_VBOOT_HANDOFF, sizeof(*vh)); + if (vh == NULL) + /* we don't need to failover gracefully here because this + * shouldn't happen with the image that has passed QA. */ + die("failed to allocate vboot_handoff structure\n"); + + memset(vh, 0, sizeof(*vh)); + + /* needed until we finish transtion to vboot2 for kernel verification */ + fill_vboot_handoff(vh, sd); + + + /* Log the recovery mode switches if required, before clearing them. */ + log_recovery_mode_switch(); + + /* + * The recovery mode switch is cleared (typically backed by EC) here + * to allow multiple queries to get_recovery_mode_switch() and have + * them return consistent results during the verified boot path as well + * as dram initialization. x86 systems ignore the saved dram settings + * in the recovery path in order to start from a clean slate. Therefore + * clear the state here since this function is called when memory + * is known to be up. + */ + clear_recovery_mode_switch(); +} + +/* + * For platforms that employ VBOOT_STARTS_IN_ROMSTAGE, the vboot + * verification doesn't happen until after cbmem is brought online. + * Therefore, the vboot results would not be initialized so don't + * automatically add results when cbmem comes online. + */ +#if !IS_ENABLED(CONFIG_VBOOT_STARTS_IN_ROMSTAGE) +static void vb2_fill_handoff_cbmem(int unused) +{ + vboot_fill_handoff(); +} +ROMSTAGE_CBMEM_INIT_HOOK(vb2_fill_handoff_cbmem) +#endif diff --git a/src/security/vboot/vboot_loader.c b/src/security/vboot/vboot_loader.c new file mode 100644 index 0000000000..17ea0a9401 --- /dev/null +++ b/src/security/vboot/vboot_loader.c @@ -0,0 +1,158 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2015 Google, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <arch/early_variables.h> +#include <cbfs.h> +#include <cbmem.h> +#include <console/console.h> +#include <ec/google/chromeec/ec.h> +#include <rmodule.h> +#include <rules.h> +#include <string.h> +#include <security/vboot/misc.h> +#include <security/vboot/symbols.h> +#include <security/vboot/vboot_common.h> + +/* Ensure vboot configuration is valid: */ +_Static_assert(IS_ENABLED(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK) + + IS_ENABLED(CONFIG_VBOOT_STARTS_IN_ROMSTAGE) == 1, + "vboot must either start in bootblock or romstage (not both!)"); +_Static_assert(!IS_ENABLED(CONFIG_VBOOT_SEPARATE_VERSTAGE) || + IS_ENABLED(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK), + "stand-alone verstage must start in (i.e. after) bootblock"); +_Static_assert(!IS_ENABLED(CONFIG_VBOOT_RETURN_FROM_VERSTAGE) || + IS_ENABLED(CONFIG_VBOOT_SEPARATE_VERSTAGE), + "return from verstage only makes sense for separate verstages"); + +/* The stage loading code is compiled and entered from multiple stages. The + * helper functions below attempt to provide more clarity on when certain + * code should be called. */ + +static int verification_should_run(void) +{ + if (IS_ENABLED(CONFIG_VBOOT_SEPARATE_VERSTAGE)) + return ENV_VERSTAGE; + else if (IS_ENABLED(CONFIG_VBOOT_STARTS_IN_ROMSTAGE)) + return ENV_ROMSTAGE; + else if (IS_ENABLED(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK)) + return ENV_BOOTBLOCK; + else + die("impossible!"); +} + +static int verstage_should_load(void) +{ + if (IS_ENABLED(CONFIG_VBOOT_SEPARATE_VERSTAGE)) + return ENV_BOOTBLOCK; + else + return 0; +} + +static int vboot_executed CAR_GLOBAL; + +int vb2_logic_executed(void) +{ + /* If we are in a stage that would load the verstage or execute the + vboot logic directly, we store the answer in a global. */ + if (verstage_should_load() || verification_should_run()) + return car_get_var(vboot_executed); + + if (IS_ENABLED(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK)) { + /* All other stages are "after the bootblock" */ + return !ENV_BOOTBLOCK; + } else if (IS_ENABLED(CONFIG_VBOOT_STARTS_IN_ROMSTAGE)) { + /* Post-RAM stages are "after the romstage" */ +#ifdef __PRE_RAM__ + return 0; +#else + return 1; +#endif + } else { + die("impossible!"); + } +} + +static void vboot_prepare(void) +{ + if (verification_should_run()) { + /* Note: this path is not used for VBOOT_RETURN_FROM_VERSTAGE */ + verstage_main(); + car_set_var(vboot_executed, 1); + vb2_save_recovery_reason_vbnv(); + } else if (verstage_should_load()) { + struct cbfsf file; + struct prog verstage = + PROG_INIT(PROG_VERSTAGE, + CONFIG_CBFS_PREFIX "/verstage"); + + printk(BIOS_DEBUG, "VBOOT: Loading verstage.\n"); + + /* load verstage from RO */ + if (cbfs_boot_locate(&file, prog_name(&verstage), NULL)) + die("failed to load verstage"); + + cbfs_file_data(prog_rdev(&verstage), &file); + + if (cbfs_prog_stage_load(&verstage)) + die("failed to load verstage"); + + /* verify and select a slot */ + prog_run(&verstage); + + /* This is not actually possible to hit this condition at + * runtime, but this provides a hint to the compiler for dead + * code elimination below. */ + if (!IS_ENABLED(CONFIG_VBOOT_RETURN_FROM_VERSTAGE)) + return; + + car_set_var(vboot_executed, 1); + } + + /* + * Fill in vboot cbmem objects before moving to ramstage so all + * downstream users have access to vboot results. This path only + * applies to platforms employing VBOOT_STARTS_IN_ROMSTAGE because + * cbmem comes online prior to vboot verification taking place. For + * other platforms the vboot cbmem objects are initialized when + * cbmem comes online. + */ + if (ENV_ROMSTAGE && IS_ENABLED(CONFIG_VBOOT_STARTS_IN_ROMSTAGE)) { + vb2_store_selected_region(); + vboot_fill_handoff(); + } +} + +static int vboot_locate(struct cbfs_props *props) +{ + struct region selected_region; + + /* Don't honor vboot results until the vboot logic has run. */ + if (!vb2_logic_executed()) + return -1; + + if (vb2_get_selected_region(&selected_region)) + return -1; + + props->offset = region_offset(&selected_region); + props->size = region_sz(&selected_region); + + return 0; +} + +const struct cbfs_locator vboot_locator = { + .name = "VBOOT", + .prepare = vboot_prepare, + .locate = vboot_locate, +}; diff --git a/src/security/vboot/vboot_logic.c b/src/security/vboot/vboot_logic.c new file mode 100644 index 0000000000..e6b97b9c2f --- /dev/null +++ b/src/security/vboot/vboot_logic.c @@ -0,0 +1,438 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2014 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <antirollback.h> +#include <arch/exception.h> +#include <assert.h> +#include <bootmode.h> +#include <console/console.h> +#include <console/vtxprintf.h> +#include <delay.h> +#include <string.h> +#include <timestamp.h> +#include <vb2_api.h> +#include <security/vboot/misc.h> +#include <security/vboot/vbnv.h> + +/* The max hash size to expect is for SHA512. */ +#define VBOOT_MAX_HASH_SIZE VB2_SHA512_DIGEST_SIZE + +#define TODO_BLOCK_SIZE 1024 + +static int is_slot_a(struct vb2_context *ctx) +{ + return !(ctx->flags & VB2_CONTEXT_FW_SLOT_B); +} + +/* exports */ + +void vb2ex_printf(const char *func, const char *fmt, ...) +{ + va_list args; + + if (func) + printk(BIOS_INFO, "VB2:%s() ", func); + + va_start(args, fmt); + do_printk_va_list(BIOS_INFO, fmt, args); + va_end(args); + + return; +} + +int vb2ex_tpm_clear_owner(struct vb2_context *ctx) +{ + uint32_t rv; + printk(BIOS_INFO, "Clearing TPM owner\n"); + rv = tpm_clear_and_reenable(); + if (rv) + return VB2_ERROR_EX_TPM_CLEAR_OWNER; + return VB2_SUCCESS; +} + +int vb2ex_read_resource(struct vb2_context *ctx, + enum vb2_resource_index index, + uint32_t offset, + void *buf, + uint32_t size) +{ + struct region_device rdev; + const char *name; + + switch (index) { + case VB2_RES_GBB: + name = "GBB"; + break; + case VB2_RES_FW_VBLOCK: + if (is_slot_a(ctx)) + name = "VBLOCK_A"; + else + name = "VBLOCK_B"; + break; + default: + return VB2_ERROR_EX_READ_RESOURCE_INDEX; + } + + if (vboot_named_region_device(name, &rdev)) + return VB2_ERROR_EX_READ_RESOURCE_SIZE; + + if (rdev_readat(&rdev, buf, offset, size) != size) + return VB2_ERROR_EX_READ_RESOURCE_SIZE; + + return VB2_SUCCESS; +} + +/* No-op stubs that can be overridden by SoCs with hardware crypto support. */ +__attribute__((weak)) +int vb2ex_hwcrypto_digest_init(enum vb2_hash_algorithm hash_alg, + uint32_t data_size) +{ + return VB2_ERROR_EX_HWCRYPTO_UNSUPPORTED; +} + +__attribute__((weak)) +int vb2ex_hwcrypto_digest_extend(const uint8_t *buf, uint32_t size) +{ + BUG(); /* Should never get called if init() returned an error. */ + return VB2_ERROR_UNKNOWN; +} + +__attribute__((weak)) +int vb2ex_hwcrypto_digest_finalize(uint8_t *digest, uint32_t digest_size) +{ + BUG(); /* Should never get called if init() returned an error. */ + return VB2_ERROR_UNKNOWN; +} + +static int handle_digest_result(void *slot_hash, size_t slot_hash_sz) +{ + int is_resume; + + /* + * Chrome EC is the only support for vboot_save_hash() & + * vboot_retrieve_hash(), if Chrome EC is not enabled then return. + */ + if (!IS_ENABLED(CONFIG_EC_GOOGLE_CHROMEEC)) + return 0; + + /* + * Nothing to do since resuming on the platform doesn't require + * vboot verification again. + */ + if (!IS_ENABLED(CONFIG_RESUME_PATH_SAME_AS_BOOT)) + return 0; + + /* + * Assume that if vboot doesn't start in bootblock verified + * RW memory init code is not employed. i.e. memory init code + * lives in RO CBFS. + */ + if (!IS_ENABLED(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK)) + return 0; + + is_resume = vboot_platform_is_resuming(); + + if (is_resume > 0) { + uint8_t saved_hash[VBOOT_MAX_HASH_SIZE]; + const size_t saved_hash_sz = sizeof(saved_hash); + + assert(slot_hash_sz == saved_hash_sz); + + printk(BIOS_DEBUG, "Platform is resuming.\n"); + + if (vboot_retrieve_hash(saved_hash, saved_hash_sz)) { + printk(BIOS_ERR, "Couldn't retrieve saved hash.\n"); + return -1; + } + + if (memcmp(saved_hash, slot_hash, slot_hash_sz)) { + printk(BIOS_ERR, "Hash mismatch on resume.\n"); + return -1; + } + } else if (is_resume < 0) + printk(BIOS_ERR, "Unable to determine if platform resuming.\n"); + + printk(BIOS_DEBUG, "Saving vboot hash.\n"); + + /* Always save the hash for the current boot. */ + if (vboot_save_hash(slot_hash, slot_hash_sz)) { + printk(BIOS_ERR, "Error saving vboot hash.\n"); + /* Though this is an error don't report it up since it could + * lead to a reboot loop. The consequence of this is that + * we will most likely fail resuming because of EC issues or + * the hash digest not matching. */ + return 0; + } + + return 0; +} + +static int hash_body(struct vb2_context *ctx, struct region_device *fw_main) +{ + uint64_t load_ts; + uint32_t expected_size; + uint8_t block[TODO_BLOCK_SIZE]; + uint8_t hash_digest[VBOOT_MAX_HASH_SIZE]; + const size_t hash_digest_sz = sizeof(hash_digest); + size_t block_size = sizeof(block); + size_t offset; + int rv; + + /* Clear the full digest so that any hash digests less than the + * max have trailing zeros. */ + memset(hash_digest, 0, hash_digest_sz); + + /* + * Since loading the firmware and calculating its hash is intertwined, + * we use this little trick to measure them separately and pretend it + * was first loaded and then hashed in one piece with the timestamps. + * (This split won't make sense with memory-mapped media like on x86.) + */ + load_ts = timestamp_get(); + timestamp_add(TS_START_HASH_BODY, load_ts); + + expected_size = region_device_sz(fw_main); + offset = 0; + + /* Start the body hash */ + rv = vb2api_init_hash(ctx, VB2_HASH_TAG_FW_BODY, &expected_size); + if (rv) + return rv; + + /* + * Honor vboot's RW slot size. The expected size is pulled out of + * the preamble and obtained through vb2api_init_hash() above. By + * creating sub region the RW slot portion of the boot media is + * limited. + */ + if (rdev_chain(fw_main, fw_main, 0, expected_size)) { + printk(BIOS_ERR, "Unable to restrict CBFS size.\n"); + return VB2_ERROR_UNKNOWN; + } + + /* Extend over the body */ + while (expected_size) { + uint64_t temp_ts; + if (block_size > expected_size) + block_size = expected_size; + + temp_ts = timestamp_get(); + if (rdev_readat(fw_main, block, offset, block_size) < 0) + return VB2_ERROR_UNKNOWN; + load_ts += timestamp_get() - temp_ts; + + rv = vb2api_extend_hash(ctx, block, block_size); + if (rv) + return rv; + + expected_size -= block_size; + offset += block_size; + } + + timestamp_add(TS_DONE_LOADING, load_ts); + timestamp_add_now(TS_DONE_HASHING); + + /* Check the result (with RSA signature verification) */ + rv = vb2api_check_hash_get_digest(ctx, hash_digest, hash_digest_sz); + if (rv) + return rv; + + timestamp_add_now(TS_END_HASH_BODY); + + if (handle_digest_result(hash_digest, hash_digest_sz)) + return VB2_ERROR_UNKNOWN; + + return VB2_SUCCESS; +} + +static int locate_firmware(struct vb2_context *ctx, + struct region_device *fw_main) +{ + const char *name; + + if (is_slot_a(ctx)) + name = "FW_MAIN_A"; + else + name = "FW_MAIN_B"; + + return vboot_named_region_device(name, fw_main); +} + +/** + * Save non-volatile and/or secure data if needed. + */ +static void save_if_needed(struct vb2_context *ctx) +{ + if (ctx->flags & VB2_CONTEXT_NVDATA_CHANGED) { + printk(BIOS_INFO, "Saving nvdata\n"); + save_vbnv(ctx->nvdata); + ctx->flags &= ~VB2_CONTEXT_NVDATA_CHANGED; + } + if (ctx->flags & VB2_CONTEXT_SECDATA_CHANGED) { + printk(BIOS_INFO, "Saving secdata\n"); + antirollback_write_space_firmware(ctx); + ctx->flags &= ~VB2_CONTEXT_SECDATA_CHANGED; + } +} + +static uint32_t extend_pcrs(struct vb2_context *ctx) +{ + return tpm_extend_pcr(ctx, 0, BOOT_MODE_PCR) || + tpm_extend_pcr(ctx, 1, HWID_DIGEST_PCR); +} + +/** + * Verify and select the firmware in the RW image + * + * TODO: Avoid loading a stage twice (once in hash_body & again in load_stage). + * when per-stage verification is ready. + */ +void verstage_main(void) +{ + struct vb2_context ctx; + struct region_device fw_main; + int rv; + + timestamp_add_now(TS_START_VBOOT); + + /* Set up context and work buffer */ + vb2_init_work_context(&ctx); + + /* Initialize and read nvdata from non-volatile storage. */ + vbnv_init(ctx.nvdata); + + /* Set S3 resume flag if vboot should behave differently when selecting + * which slot to boot. This is only relevant to vboot if the platform + * does verification of memory init and thus must ensure it resumes with + * the same slot that it booted from. */ + if (IS_ENABLED(CONFIG_RESUME_PATH_SAME_AS_BOOT) && + IS_ENABLED(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK) && + vboot_platform_is_resuming()) + ctx.flags |= VB2_CONTEXT_S3_RESUME; + + /* Read secdata from TPM. Initialize TPM if secdata not found. We don't + * check the return value here because vb2api_fw_phase1 will catch + * invalid secdata and tell us what to do (=reboot). */ + timestamp_add_now(TS_START_TPMINIT); + antirollback_read_space_firmware(&ctx); + timestamp_add_now(TS_END_TPMINIT); + + if (IS_ENABLED(CONFIG_VBOOT_PHYSICAL_DEV_SWITCH) && + get_developer_mode_switch()) + ctx.flags |= VB2_CONTEXT_FORCE_DEVELOPER_MODE; + + if (get_recovery_mode_switch()) { + ctx.flags |= VB2_CONTEXT_FORCE_RECOVERY_MODE; + if (IS_ENABLED(CONFIG_VBOOT_DISABLE_DEV_ON_RECOVERY)) + ctx.flags |= VB2_DISABLE_DEVELOPER_MODE; + } + + if (IS_ENABLED(CONFIG_VBOOT_WIPEOUT_SUPPORTED) && + get_wipeout_mode_switch()) + ctx.flags |= VB2_CONTEXT_FORCE_WIPEOUT_MODE; + + if (IS_ENABLED(CONFIG_VBOOT_LID_SWITCH) && !get_lid_switch()) + ctx.flags |= VB2_CONTEXT_NOFAIL_BOOT; + + /* Do early init (set up secdata and NVRAM, load GBB) */ + printk(BIOS_INFO, "Phase 1\n"); + rv = vb2api_fw_phase1(&ctx); + + if (rv) { + /* + * If vb2api_fw_phase1 fails, check for return value. + * If it is set to VB2_ERROR_API_PHASE1_RECOVERY, then continue + * into recovery mode. + * For any other error code, save context if needed and reboot. + */ + if (rv == VB2_ERROR_API_PHASE1_RECOVERY) { + printk(BIOS_INFO, "Recovery requested (%x)\n", rv); + save_if_needed(&ctx); + extend_pcrs(&ctx); /* ignore failures */ + timestamp_add_now(TS_END_VBOOT); + return; + } + + printk(BIOS_INFO, "Reboot reqested (%x)\n", rv); + save_if_needed(&ctx); + vboot_reboot(); + } + + /* Determine which firmware slot to boot (based on NVRAM) */ + printk(BIOS_INFO, "Phase 2\n"); + rv = vb2api_fw_phase2(&ctx); + if (rv) { + printk(BIOS_INFO, "Reboot requested (%x)\n", rv); + save_if_needed(&ctx); + vboot_reboot(); + } + + /* Try that slot (verify its keyblock and preamble) */ + printk(BIOS_INFO, "Phase 3\n"); + timestamp_add_now(TS_START_VERIFY_SLOT); + rv = vb2api_fw_phase3(&ctx); + timestamp_add_now(TS_END_VERIFY_SLOT); + if (rv) { + printk(BIOS_INFO, "Reboot requested (%x)\n", rv); + save_if_needed(&ctx); + vboot_reboot(); + } + + printk(BIOS_INFO, "Phase 4\n"); + rv = locate_firmware(&ctx, &fw_main); + if (rv) + die("Failed to read FMAP to locate firmware"); + + rv = hash_body(&ctx, &fw_main); + save_if_needed(&ctx); + if (rv) { + printk(BIOS_INFO, "Reboot requested (%x)\n", rv); + vboot_reboot(); + } + + rv = extend_pcrs(&ctx); + if (rv) { + printk(BIOS_WARNING, "Failed to extend TPM PCRs (%#x)\n", rv); + vb2api_fail(&ctx, VB2_RECOVERY_RO_TPM_U_ERROR, rv); + save_if_needed(&ctx); + vboot_reboot(); + } + + /* Lock TPM */ + rv = antirollback_lock_space_firmware(); + if (rv) { + printk(BIOS_INFO, "Failed to lock TPM (%x)\n", rv); + vb2api_fail(&ctx, VB2_RECOVERY_RO_TPM_L_ERROR, 0); + save_if_needed(&ctx); + vboot_reboot(); + } + + /* Lock rec hash space if available. */ + if (IS_ENABLED(CONFIG_VBOOT_HAS_REC_HASH_SPACE)) { + rv = antirollback_lock_space_rec_hash(); + if (rv) { + printk(BIOS_INFO, "Failed to lock rec hash space(%x)\n", + rv); + vb2api_fail(&ctx, VB2_RECOVERY_RO_TPM_REC_HASH_L_ERROR, + 0); + save_if_needed(&ctx); + vboot_reboot(); + } + } + + printk(BIOS_INFO, "Slot %c is selected\n", is_slot_a(&ctx) ? 'A' : 'B'); + vb2_set_selected_region(region_device_region(&fw_main)); + timestamp_add_now(TS_END_VBOOT); +} diff --git a/src/security/vboot/verstage.c b/src/security/vboot/verstage.c new file mode 100644 index 0000000000..c2441840d7 --- /dev/null +++ b/src/security/vboot/verstage.c @@ -0,0 +1,39 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2015 Google Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <arch/exception.h> +#include <arch/hlt.h> +#include <console/console.h> +#include <program_loading.h> +#include <security/vboot/vboot_common.h> + +void __attribute__((weak)) verstage_mainboard_init(void) +{ + /* Default empty implementation. */ +} + +void main(void) +{ + console_init(); + exception_init(); + verstage_mainboard_init(); + + if (IS_ENABLED(CONFIG_VBOOT_RETURN_FROM_VERSTAGE)) { + verstage_main(); + } else { + run_romstage(); + hlt(); + } +} |