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>
This commit is contained in:
committed by
Martin Roth
parent
9e0d69bf1e
commit
fea2429e25
@@ -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"
|
||||
|
1
src/security/Makefile.inc
Normal file
1
src/security/Makefile.inc
Normal file
@@ -0,0 +1 @@
|
||||
subdirs-y += vboot
|
334
src/security/vboot/Kconfig
Normal file
334
src/security/vboot/Kconfig
Normal file
@@ -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)
|
263
src/security/vboot/Makefile.inc
Normal file
263
src/security/vboot/Makefile.inc
Normal file
@@ -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
|
171
src/security/vboot/bootmode.c
Normal file
171
src/security/vboot/bootmode.c
Normal file
@@ -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;
|
||||
}
|
182
src/security/vboot/common.c
Normal file
182
src/security/vboot/common.c
Normal file
@@ -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
|
40
src/security/vboot/misc.h
Normal file
40
src/security/vboot/misc.h
Normal file
@@ -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__ */
|
85
src/security/vboot/secdata_mock.c
Normal file
85
src/security/vboot/secdata_mock.c
Normal file
@@ -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;
|
||||
}
|
585
src/security/vboot/secdata_tpm.c
Normal file
585
src/security/vboot/secdata_tpm.c
Normal file
@@ -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);
|
||||
}
|
28
src/security/vboot/symbols.h
Normal file
28
src/security/vboot/symbols.h
Normal file
@@ -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__ */
|
155
src/security/vboot/vbnv.c
Normal file
155
src/security/vboot/vbnv.c
Normal file
@@ -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);
|
||||
}
|
51
src/security/vboot/vbnv.h
Normal file
51
src/security/vboot/vbnv.h
Normal file
@@ -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__ */
|
114
src/security/vboot/vbnv_cmos.c
Normal file
114
src/security/vboot/vbnv_cmos.c
Normal file
@@ -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
|
30
src/security/vboot/vbnv_ec.c
Normal file
30
src/security/vboot/vbnv_ec.c
Normal file
@@ -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);
|
||||
}
|
181
src/security/vboot/vbnv_flash.c
Normal file
181
src/security/vboot/vbnv_flash.c
Normal file
@@ -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");
|
||||
}
|
||||
}
|
50
src/security/vboot/vbnv_layout.h
Normal file
50
src/security/vboot/vbnv_layout.h
Normal file
@@ -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__ */
|
115
src/security/vboot/vboot_common.c
Normal file
115
src/security/vboot/vboot_common.c
Normal file
@@ -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");
|
||||
}
|
118
src/security/vboot/vboot_common.h
Normal file
118
src/security/vboot/vboot_common.h
Normal file
@@ -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__ */
|
190
src/security/vboot/vboot_handoff.c
Normal file
190
src/security/vboot/vboot_handoff.c
Normal file
@@ -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
|
158
src/security/vboot/vboot_loader.c
Normal file
158
src/security/vboot/vboot_loader.c
Normal file
@@ -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,
|
||||
};
|
438
src/security/vboot/vboot_logic.c
Normal file
438
src/security/vboot/vboot_logic.c
Normal file
@@ -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);
|
||||
}
|
39
src/security/vboot/verstage.c
Normal file
39
src/security/vboot/verstage.c
Normal file
@@ -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();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user