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:
Philipp Deppenwiese
2017-10-17 17:02:29 +02:00
committed by Martin Roth
parent 9e0d69bf1e
commit fea2429e25
55 changed files with 65 additions and 63 deletions

View File

@@ -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"

View File

@@ -0,0 +1 @@
subdirs-y += vboot

334
src/security/vboot/Kconfig Normal file
View 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)

View 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

View 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
View 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
View 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__ */

View 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;
}

View 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);
}

View 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
View 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
View 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__ */

View 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

View 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);
}

View 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");
}
}

View 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__ */

View 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");
}

View 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__ */

View 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

View 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,
};

View 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);
}

View 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();
}
}