summaryrefslogtreecommitdiff
path: root/src/security
diff options
context:
space:
mode:
authorPhilipp Deppenwiese <zaolin@das-labor.org>2017-10-17 17:02:29 +0200
committerMartin Roth <martinroth@google.com>2017-10-22 02:14:46 +0000
commitfea2429e254c41b192dd0856966d5f80eb15a07a (patch)
tree40e81b3ca7ee3c1f9985af9a8a24a3a9b21e8700 /src/security
parent9e0d69bf1e0194509108eba23511a12d597ae49e (diff)
downloadcoreboot-fea2429e254c41b192dd0856966d5f80eb15a07a.tar.xz
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')
-rw-r--r--src/security/Kconfig2
-rw-r--r--src/security/Makefile.inc1
-rw-r--r--src/security/vboot/Kconfig334
-rw-r--r--src/security/vboot/Makefile.inc263
-rw-r--r--src/security/vboot/bootmode.c171
-rw-r--r--src/security/vboot/common.c182
-rw-r--r--src/security/vboot/misc.h40
-rw-r--r--src/security/vboot/secdata_mock.c85
-rw-r--r--src/security/vboot/secdata_tpm.c585
-rw-r--r--src/security/vboot/symbols.h28
-rw-r--r--src/security/vboot/vbnv.c155
-rw-r--r--src/security/vboot/vbnv.h51
-rw-r--r--src/security/vboot/vbnv_cmos.c114
-rw-r--r--src/security/vboot/vbnv_ec.c30
-rw-r--r--src/security/vboot/vbnv_flash.c181
-rw-r--r--src/security/vboot/vbnv_layout.h50
-rw-r--r--src/security/vboot/vboot_common.c115
-rw-r--r--src/security/vboot/vboot_common.h118
-rw-r--r--src/security/vboot/vboot_handoff.c190
-rw-r--r--src/security/vboot/vboot_loader.c158
-rw-r--r--src/security/vboot/vboot_logic.c438
-rw-r--r--src/security/vboot/verstage.c39
22 files changed, 3330 insertions, 0 deletions
diff --git a/src/security/Kconfig b/src/security/Kconfig
index 7ec295220c..7d105b8cf8 100644
--- a/src/security/Kconfig
+++ b/src/security/Kconfig
@@ -11,3 +11,5 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
+
+source "src/security/vboot/Kconfig"
diff --git a/src/security/Makefile.inc b/src/security/Makefile.inc
new file mode 100644
index 0000000000..d2e1e60f44
--- /dev/null
+++ b/src/security/Makefile.inc
@@ -0,0 +1 @@
+subdirs-y += vboot
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();
+ }
+}