This is a repost of a series of patches providing TPM support to SeaBIOS.
As an addition, this patch series now works on the Acer C720 Chromebook with limitations (S3 not getting invoked; no logging into TCPA table).
The patch series cleanly applies to a checkout of b4581224.
The following set of patches add TPM and Trusted Computing support to SeaBIOS. In particular the patches add:
- a TPM driver for the Qemu's TPM TIS emulation - Support for initialzation of the TPM - init of TCPA logging table - Support for the TCG BIOS extensions (1ah handler [ah = 0xbb]) (used by trusted grub; http://trousers.sourceforge.net/grub.html) - Static Root of Trusted for Measurement (SRTM) support - Support for S3 resume (sends command to TPM upon resume) - Support for sending control messages from the OS to the BIOS and have the BIOS control certain life-cycle aspects of the TPM following those messages - TPM-specific menu for controlling aspects of the TPM
All implementations necessarily follow specifications.
When all patches are applied the following services are available - SSDT ACPI table for TPM support - initialization of the TPM upon VM start and S3 resume - Static root of trust for measurements (SRTM) that measures (some) data of SeaBIOS in TCPA ACPI table - 1ah interrupt handler offering APIs for measuring and sending commands to the TPM (trusted grub uses them) - With an extensions to QEMU's TPM SSDT: The root user in Linux (for example) can send the above mentioned control messages to the BIOS and have the BIOS act upon them - User menu for controlling aspects of the state of the TPM
Stefan Berger (6): Add an implementation of a TPM TIS driver Implementation of the TCG BIOS extensions Support for BIOS interrupt handler Add 'measurement' code to the BIOS Support for TPM Physical Presence Interface Add a menu for TPM control
Makefile | 5 +- src/Kconfig | 7 + src/boot.c | 27 +- src/cdrom.c | 11 + src/clock.c | 13 + src/config.h | 1 + src/hw/tpm_drivers.c | 291 +++++++ src/hw/tpm_drivers.h | 90 +++ src/optionroms.c | 4 + src/post.c | 14 + src/resume.c | 3 + src/sha1.c | 145 ++++ src/sha1.h | 8 + src/std/acpi.h | 20 + src/tcgbios.c | 2186 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 454 +++++++++++ src/util.h | 2 + src/x86.h | 7 + 18 files changed, 3283 insertions(+), 5 deletions(-) create mode 100644 src/hw/tpm_drivers.c create mode 100644 src/hw/tpm_drivers.h create mode 100644 src/sha1.c create mode 100644 src/sha1.h create mode 100644 src/tcgbios.c create mode 100644 src/tcgbios.h
This patch adds an implementation of a TPM TIS driver for the TPM TIS emulation supported by QEMU. The driver is broken up into several small functions that have to be called in proper sequence. The driver is cleanly separated from the rest of the code through an interface holding pointers to the driver's functions. A client using this driver first probes whether the TPM TIS interface is available (probe function) and then invokes the interface function to initialze the interface and send requests and receive responses.
Possible future extensions *could* include a virtio interface for the TPM with a corresponding driver here.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
---
v7: - moving declaration of tpm_drivers[] into tpm_drivers.h
v6: - reworked timeouts; not hardcoded anymore
v5: - introducing a configurable threashold as part of the driver interface structure below which the TPM is used for calculating the sha1
v2: - adapted tpm_drivers.c to be under LGPLv3 --- src/hw/tpm_drivers.c | 258 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/hw/tpm_drivers.h | 90 ++++++++++++++++++ 2 files changed, 348 insertions(+) create mode 100644 src/hw/tpm_drivers.c create mode 100644 src/hw/tpm_drivers.h
diff --git a/src/hw/tpm_drivers.c b/src/hw/tpm_drivers.c new file mode 100644 index 0000000..ea602e8 --- /dev/null +++ b/src/hw/tpm_drivers.c @@ -0,0 +1,258 @@ +// Implementation of a TPM driver for the TPM TIS interface +// +// Copyright (C) 2006-2011 IBM Corporation +// +// Authors: +// Stefan Berger stefanb@linux.vnet.ibm.com +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" // CONFIG_TPM_TIS_SHA1THRESHOLD +#include "string.h" // memcpy +#include "util.h" // msleep +#include "x86.h" // readl +#include "hw/tpm_drivers.h" // struct tpm_driver +#include "tcgbios.h" // TCG_* + +static const u32 tis_default_timeouts[4] = { + TIS_DEFAULT_TIMEOUT_A, + TIS_DEFAULT_TIMEOUT_B, + TIS_DEFAULT_TIMEOUT_C, + TIS_DEFAULT_TIMEOUT_D, +}; + +static const u32 tpm_default_durations[3] = { + TPM_DEFAULT_DURATION_SHORT, + TPM_DEFAULT_DURATION_MEDIUM, + TPM_DEFAULT_DURATION_LONG, +}; + +/* determined values */ +static u32 tpm_default_dur[3]; +static u32 tpm_default_to[4]; + + +/* if device is not there, return '0', '1' otherwise */ +static u32 tis_probe(void) +{ + u32 rc = 0; + u32 didvid = readl(TIS_REG(0, TIS_REG_DID_VID)); + + if ((didvid != 0) && (didvid != 0xffffffff)) + rc = 1; + + return rc; +} + +static u32 tis_init(void) +{ + writeb(TIS_REG(0, TIS_REG_INT_ENABLE), 0); + + if (tpm_drivers[TIS_DRIVER_IDX].durations == NULL) { + u32 *durations = tpm_default_dur; + memcpy(durations, tpm_default_durations, + sizeof(tpm_default_durations)); + tpm_drivers[TIS_DRIVER_IDX].durations = durations; + } + + if (tpm_drivers[TIS_DRIVER_IDX].timeouts == NULL) { + u32 *timeouts = tpm_default_to; + memcpy(timeouts, tis_default_timeouts, + sizeof(tis_default_timeouts)); + tpm_drivers[TIS_DRIVER_IDX].timeouts = timeouts; + } + + return 1; +} + + +static void set_timeouts(u32 timeouts[4], u32 durations[3]) +{ + u32 *tos = tpm_drivers[TIS_DRIVER_IDX].timeouts; + u32 *dus = tpm_drivers[TIS_DRIVER_IDX].durations; + + if (tos && tos != tis_default_timeouts && timeouts) + memcpy(tos, timeouts, 4 * sizeof(u32)); + if (dus && dus != tpm_default_durations && durations) + memcpy(dus, durations, 3 * sizeof(u32)); +} + + +static u32 tis_wait_sts(u8 locty, u32 time, u8 mask, u8 expect) +{ + u32 rc = 1; + + while (time > 0) { + u8 sts = readb(TIS_REG(locty, TIS_REG_STS)); + if ((sts & mask) == expect) { + rc = 0; + break; + } + msleep(1); + time--; + } + return rc; +} + +static u32 tis_activate(u8 locty) +{ + u32 rc = 0; + u8 acc; + int l; + u32 timeout_a = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_A]; + + if (!(readb(TIS_REG(locty, TIS_REG_ACCESS)) & + TIS_ACCESS_ACTIVE_LOCALITY)) { + /* release locality in use top-downwards */ + for (l = 4; l >= 0; l--) + writeb(TIS_REG(l, TIS_REG_ACCESS), + TIS_ACCESS_ACTIVE_LOCALITY); + } + + /* request access to locality */ + writeb(TIS_REG(locty, TIS_REG_ACCESS), TIS_ACCESS_REQUEST_USE); + + acc = readb(TIS_REG(locty, TIS_REG_ACCESS)); + if ((acc & TIS_ACCESS_ACTIVE_LOCALITY)) { + writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY); + rc = tis_wait_sts(locty, timeout_a, + TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY); + } + + return rc; +} + +static u32 tis_find_active_locality(void) +{ + u8 locty; + + for (locty = 0; locty <= 4; locty++) { + if ((readb(TIS_REG(locty, TIS_REG_ACCESS)) & + TIS_ACCESS_ACTIVE_LOCALITY)) + return locty; + } + + tis_activate(0); + + return 0; +} + +static u32 tis_ready(void) +{ + u32 rc = 0; + u8 locty = tis_find_active_locality(); + u32 timeout_b = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_B]; + + writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY); + rc = tis_wait_sts(locty, timeout_b, + TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY); + + return rc; +} + +static u32 tis_senddata(const u8 *const data, u32 len) +{ + u32 rc = 0; + u32 offset = 0; + u32 end = 0; + u16 burst = 0; + u32 ctr = 0; + u8 locty = tis_find_active_locality(); + u32 timeout_d = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_D]; + + do { + while (burst == 0 && ctr < timeout_d) { + burst = readl(TIS_REG(locty, TIS_REG_STS)) >> 8; + if (burst == 0) { + msleep(1); + ctr++; + } + } + + if (burst == 0) { + rc = TCG_RESPONSE_TIMEOUT; + break; + } + + while (1) { + writeb(TIS_REG(locty, TIS_REG_DATA_FIFO), data[offset++]); + burst--; + + if (burst == 0 || offset == len) + break; + } + + if (offset == len) + end = 1; + } while (end == 0); + + return rc; +} + +static u32 tis_readresp(u8 *buffer, u32 *len) +{ + u32 rc = 0; + u32 offset = 0; + u32 sts; + u8 locty = tis_find_active_locality(); + + while (offset < *len) { + buffer[offset] = readb(TIS_REG(locty, TIS_REG_DATA_FIFO)); + offset++; + sts = readb(TIS_REG(locty, TIS_REG_STS)); + /* data left ? */ + if ((sts & TIS_STS_DATA_AVAILABLE) == 0) + break; + } + + *len = offset; + + return rc; +} + + +static u32 tis_waitdatavalid(void) +{ + u32 rc = 0; + u8 locty = tis_find_active_locality(); + u32 timeout_c = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_C]; + + if (tis_wait_sts(locty, timeout_c, TIS_STS_VALID, TIS_STS_VALID) != 0) + rc = TCG_NO_RESPONSE; + + return rc; +} + +static u32 tis_waitrespready(enum tpmDurationType to_t) +{ + u32 rc = 0; + u8 locty = tis_find_active_locality(); + u32 timeout = tpm_drivers[TIS_DRIVER_IDX].durations[to_t]; + + writeb(TIS_REG(locty ,TIS_REG_STS), TIS_STS_TPM_GO); + + if (tis_wait_sts(locty, timeout, + TIS_STS_DATA_AVAILABLE, TIS_STS_DATA_AVAILABLE) != 0) + rc = TCG_NO_RESPONSE; + + return rc; +} + + +struct tpm_driver tpm_drivers[TPM_NUM_DRIVERS] = { + [TIS_DRIVER_IDX] = + { + .timeouts = NULL, + .durations = NULL, + .set_timeouts = set_timeouts, + .probe = tis_probe, + .init = tis_init, + .activate = tis_activate, + .ready = tis_ready, + .senddata = tis_senddata, + .readresp = tis_readresp, + .waitdatavalid = tis_waitdatavalid, + .waitrespready = tis_waitrespready, + .sha1threshold = 100 * 1024, + }, +}; diff --git a/src/hw/tpm_drivers.h b/src/hw/tpm_drivers.h new file mode 100644 index 0000000..34bb12d --- /dev/null +++ b/src/hw/tpm_drivers.h @@ -0,0 +1,90 @@ +#ifndef TPM_DRIVERS_H +#define TPM_DRIVERS_H + +#include "types.h" // u32 + + +enum tpmDurationType { + TPM_DURATION_TYPE_SHORT = 0, + TPM_DURATION_TYPE_MEDIUM, + TPM_DURATION_TYPE_LONG, +}; + +/* low level driver implementation */ +struct tpm_driver { + u32 *timeouts; + u32 *durations; + void (*set_timeouts)(u32 timeouts[4], u32 durations[3]); + u32 (*probe)(void); + u32 (*init)(void); + u32 (*activate)(u8 locty); + u32 (*ready)(void); + u32 (*senddata)(const u8 *const data, u32 len); + u32 (*readresp)(u8 *buffer, u32 *len); + u32 (*waitdatavalid)(void); + u32 (*waitrespready)(enum tpmDurationType to_t); + /* the TPM will be used for buffers of sizes below the sha1threshold + for calculating the hash */ + u32 sha1threshold; +}; + +extern struct tpm_driver tpm_drivers[]; + + +#define TIS_DRIVER_IDX 0 +#define TPM_NUM_DRIVERS 1 + +#define TPM_INVALID_DRIVER -1 + +/* TIS driver */ +/* address of locality 0 (TIS) */ +#define TPM_TIS_BASE_ADDRESS 0xfed40000 + +#define TIS_REG(LOCTY, REG) \ + (void *)(TPM_TIS_BASE_ADDRESS + (LOCTY << 12) + REG) + +/* hardware registers */ +#define TIS_REG_ACCESS 0x0 +#define TIS_REG_INT_ENABLE 0x8 +#define TIS_REG_INT_VECTOR 0xc +#define TIS_REG_INT_STATUS 0x10 +#define TIS_REG_INTF_CAPABILITY 0x14 +#define TIS_REG_STS 0x18 +#define TIS_REG_DATA_FIFO 0x24 +#define TIS_REG_DID_VID 0xf00 +#define TIS_REG_RID 0xf04 + +#define TIS_STS_VALID (1 << 7) /* 0x80 */ +#define TIS_STS_COMMAND_READY (1 << 6) /* 0x40 */ +#define TIS_STS_TPM_GO (1 << 5) /* 0x20 */ +#define TIS_STS_DATA_AVAILABLE (1 << 4) /* 0x10 */ +#define TIS_STS_EXPECT (1 << 3) /* 0x08 */ +#define TIS_STS_RESPONSE_RETRY (1 << 1) /* 0x02 */ + +#define TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) /* 0x80 */ +#define TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) /* 0x20 */ +#define TIS_ACCESS_BEEN_SEIZED (1 << 4) /* 0x10 */ +#define TIS_ACCESS_SEIZE (1 << 3) /* 0x08 */ +#define TIS_ACCESS_PENDING_REQUEST (1 << 2) /* 0x04 */ +#define TIS_ACCESS_REQUEST_USE (1 << 1) /* 0x02 */ +#define TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) /* 0x01 */ + +#define SCALER 10 + +#define TIS_DEFAULT_TIMEOUT_A (750 * SCALER) +#define TIS_DEFAULT_TIMEOUT_B (2000 * SCALER) +#define TIS_DEFAULT_TIMEOUT_C (750 * SCALER) +#define TIS_DEFAULT_TIMEOUT_D (750 * SCALER) + +enum tisTimeoutType { + TIS_TIMEOUT_TYPE_A = 0, + TIS_TIMEOUT_TYPE_B, + TIS_TIMEOUT_TYPE_C, + TIS_TIMEOUT_TYPE_D, +}; + +#define TPM_DEFAULT_DURATION_SHORT (2000 * SCALER) +#define TPM_DEFAULT_DURATION_MEDIUM (20000 * SCALER) +#define TPM_DEFAULT_DURATION_LONG (60000 * SCALER) + +#endif /* TPM_DRIVERS_H */
This patch implements the main part of the TCG BIOS extensions. It provides the following functionality:
- initialization of the TCPA ACPI table used for logging of measurements - initialization of the TPM by sending a sequence of commands to it - proper setup of the TPM before the BIOS hands over control to the bootloader - support for S3 resume; BIOS sends TPM_Startup(ST_STATE) to TPM - enable configuration of SeaBIOS to be built with TCGBIOS extensions All TCG BIOS extensions are activated with CONFIG_TCGBIOS.
Structures that are needed in subsequent patches are also included in tcgbios.h at this point.
The effect of this patch is that it initialized the TPM upon VM start and S3 resume.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
---
v10: - move tpm_startup() and tpm_acpi_init() calls into tpm_start() - move tpm_s3_resume() function before calls to s3_resume_vga so that TPM is started up before we may encounter option roms to measure (in other patch) - addressed all of Kevin's comments on v9
v9: - removal of ACPI table building - merged TPM probing into this patch
v6: - passing durations of commands to the transmission function - acquire timeouts and durations from TPM and use them
v5: - adding the lock flag to the 'not present' Physcial_presence_NOT_PRESENT structure
v4: - return TCG_GENERAL_ERROR if ! has_working_tpm()
v3: - upon S3 resume call timer_setup()
v2: - replace mssleep() with calls to msleep() - Moving Kconfig patch to this file - converting code to call dprintf(DEBUG_tcg, ...) - use the get_rsdp call to get hold of the RSDP - use util.c:checksum() - Adapting tcgbios.c to be under LGPLv3 - using if (!CONFIG_TCGBIOS) everywhere --- Makefile | 5 +- src/Kconfig | 7 + src/config.h | 1 + src/hw/tpm_drivers.c | 33 +++ src/post.c | 7 + src/resume.c | 3 + src/std/acpi.h | 20 ++ src/tcgbios.c | 572 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 386 ++++++++++++++++++++++++++++++++++ 9 files changed, 1032 insertions(+), 2 deletions(-) create mode 100644 src/tcgbios.c create mode 100644 src/tcgbios.h
diff --git a/Makefile b/Makefile index a84a5f7..82dd716 100644 --- a/Makefile +++ b/Makefile @@ -38,11 +38,12 @@ SRCBOTH=misc.c stacks.c output.c string.c block.c cdrom.c disk.c mouse.c kbd.c \ hw/lsi-scsi.c hw/esp-scsi.c hw/megasas.c SRC16=$(SRCBOTH) SRC32FLAT=$(SRCBOTH) post.c memmap.c malloc.c romfile.c x86.c optionroms.c \ - pmm.c font.c boot.c bootsplash.c jpeg.c bmp.c \ + pmm.c font.c boot.c bootsplash.c jpeg.c bmp.c tcgbios.c \ hw/ahci.c hw/pvscsi.c hw/usb-xhci.c hw/usb-hub.c hw/sdcard.c \ fw/coreboot.c fw/lzmadecode.c fw/csm.c fw/biostables.c \ fw/paravirt.c fw/shadow.c fw/pciinit.c fw/smm.c fw/smp.c fw/mtrr.c fw/xen.c \ - fw/acpi.c fw/mptable.c fw/pirtable.c fw/smbios.c fw/romfile_loader.c + fw/acpi.c fw/mptable.c fw/pirtable.c fw/smbios.c fw/romfile_loader.c \ + hw/tpm_drivers.c SRC32SEG=string.c output.c pcibios.c apm.c stacks.c hw/pci.c hw/serialio.c DIRS=src src/hw src/fw vgasrc
diff --git a/src/Kconfig b/src/Kconfig index 45ca59c..19f4db1 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -421,6 +421,13 @@ menu "BIOS interfaces" modified by programs. However, some old DOS high memory managers may require the UMB region to be read-only.
+ config TCGBIOS + depends on S3_RESUME + bool "TPM support and TCG BIOS extensions" + default y + help + Provide TPM support along with TCG BIOS extensions + endmenu
menu "BIOS Tables" diff --git a/src/config.h b/src/config.h index 6da067d..4bfebe8 100644 --- a/src/config.h +++ b/src/config.h @@ -104,5 +104,6 @@ #define DEBUG_unimplemented 2 #define DEBUG_invalid 3 #define DEBUG_thread 2 +#define DEBUG_tcg 20
#endif // config.h diff --git a/src/hw/tpm_drivers.c b/src/hw/tpm_drivers.c index ea602e8..444eac3 100644 --- a/src/hw/tpm_drivers.c +++ b/src/hw/tpm_drivers.c @@ -35,6 +35,9 @@ static u32 tpm_default_to[4]; /* if device is not there, return '0', '1' otherwise */ static u32 tis_probe(void) { + if (!CONFIG_TCGBIOS) + return 0; + u32 rc = 0; u32 didvid = readl(TIS_REG(0, TIS_REG_DID_VID));
@@ -46,6 +49,9 @@ static u32 tis_probe(void)
static u32 tis_init(void) { + if (!CONFIG_TCGBIOS) + return 1; + writeb(TIS_REG(0, TIS_REG_INT_ENABLE), 0);
if (tpm_drivers[TIS_DRIVER_IDX].durations == NULL) { @@ -68,6 +74,9 @@ static u32 tis_init(void)
static void set_timeouts(u32 timeouts[4], u32 durations[3]) { + if (!CONFIG_TCGBIOS) + return; + u32 *tos = tpm_drivers[TIS_DRIVER_IDX].timeouts; u32 *dus = tpm_drivers[TIS_DRIVER_IDX].durations;
@@ -80,6 +89,9 @@ static void set_timeouts(u32 timeouts[4], u32 durations[3])
static u32 tis_wait_sts(u8 locty, u32 time, u8 mask, u8 expect) { + if (!CONFIG_TCGBIOS) + return 0; + u32 rc = 1;
while (time > 0) { @@ -96,6 +108,9 @@ static u32 tis_wait_sts(u8 locty, u32 time, u8 mask, u8 expect)
static u32 tis_activate(u8 locty) { + if (!CONFIG_TCGBIOS) + return 0; + u32 rc = 0; u8 acc; int l; @@ -124,6 +139,9 @@ static u32 tis_activate(u8 locty)
static u32 tis_find_active_locality(void) { + if (!CONFIG_TCGBIOS) + return 0; + u8 locty;
for (locty = 0; locty <= 4; locty++) { @@ -139,6 +157,9 @@ static u32 tis_find_active_locality(void)
static u32 tis_ready(void) { + if (!CONFIG_TCGBIOS) + return 0; + u32 rc = 0; u8 locty = tis_find_active_locality(); u32 timeout_b = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_B]; @@ -152,6 +173,9 @@ static u32 tis_ready(void)
static u32 tis_senddata(const u8 *const data, u32 len) { + if (!CONFIG_TCGBIOS) + return 0; + u32 rc = 0; u32 offset = 0; u32 end = 0; @@ -191,6 +215,9 @@ static u32 tis_senddata(const u8 *const data, u32 len)
static u32 tis_readresp(u8 *buffer, u32 *len) { + if (!CONFIG_TCGBIOS) + return 0; + u32 rc = 0; u32 offset = 0; u32 sts; @@ -213,6 +240,9 @@ static u32 tis_readresp(u8 *buffer, u32 *len)
static u32 tis_waitdatavalid(void) { + if (!CONFIG_TCGBIOS) + return 0; + u32 rc = 0; u8 locty = tis_find_active_locality(); u32 timeout_c = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_C]; @@ -225,6 +255,9 @@ static u32 tis_waitdatavalid(void)
static u32 tis_waitrespready(enum tpmDurationType to_t) { + if (!CONFIG_TCGBIOS) + return 0; + u32 rc = 0; u8 locty = tis_find_active_locality(); u32 timeout = tpm_drivers[TIS_DRIVER_IDX].durations[to_t]; diff --git a/src/post.c b/src/post.c index 9ea5620..0c7b36b 100644 --- a/src/post.c +++ b/src/post.c @@ -28,6 +28,7 @@ #include "output.h" // dprintf #include "string.h" // memset #include "util.h" // kbd_init +#include "tcgbios.h" // tpm_*
/**************************************************************** @@ -220,6 +221,9 @@ maininit(void) if (threads_during_optionroms()) device_hardware_setup();
+ // Initialize TPM + tpm_start(); + // Run vga option rom vgarom_setup();
@@ -236,6 +240,9 @@ maininit(void) interactive_bootmenu(); wait_threads();
+ // Change TPM phys. presence state befor leaving BIOS + tpm_leave_bios(); + // Prepare for boot. prepareboot();
diff --git a/src/resume.c b/src/resume.c index 1903174..a5465d8 100644 --- a/src/resume.c +++ b/src/resume.c @@ -16,6 +16,7 @@ #include "std/bda.h" // struct bios_data_area_s #include "string.h" // memset #include "util.h" // dma_setup +#include "tcgbios.h" // tpm_s3_resume
// Handler for post calls that look like a resume. void VISIBLE16 @@ -99,6 +100,8 @@ s3_resume(void)
pci_resume();
+ /* resume TPM before we may measure option roms */ + tpm_s3_resume(); s3_resume_vga();
make_bios_readonly(); diff --git a/src/std/acpi.h b/src/std/acpi.h index e0d9516..b672bbe 100644 --- a/src/std/acpi.h +++ b/src/std/acpi.h @@ -294,4 +294,24 @@ struct acpi_table_mcfg { struct acpi_mcfg_allocation allocation[0]; } PACKED;
+ +struct rsdt_descriptor { + ACPI_TABLE_HEADER_DEF + u32 entry[1]; +} PACKED; + +#define TCPA_SIGNATURE 0x41504354 +struct tcpa_descriptor_rev2 +{ + ACPI_TABLE_HEADER_DEF + u16 platform_class; + u32 log_area_minimum_length; + u64 log_area_start_address; +} PACKED; + +/* TCPA ACPI definitions */ +#define TCPA_ACPI_CLASS_CLIENT 0 +#define TCPA_ACPI_CLASS_SERVER 1 + + #endif // acpi.h diff --git a/src/tcgbios.c b/src/tcgbios.c new file mode 100644 index 0000000..73ff1f3 --- /dev/null +++ b/src/tcgbios.c @@ -0,0 +1,572 @@ +// Implementation of the TCG BIOS extension according to the specification +// described in +// https://www.trustedcomputinggroup.org/specs/PCClient/TCG_PCClientImplementat... +// +// Copyright (C) 2006-2011, 2014 IBM Corporation +// +// Authors: +// Stefan Berger stefanb@linux.vnet.ibm.com +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + + +#include "config.h" + +#include "types.h" +#include "byteorder.h" // cpu_to_* +#include "hw/tpm_drivers.h" // tpm_drivers[] +#include "farptr.h" // MAKE_FLATPTR +#include "string.h" // checksum +#include "tcgbios.h"// tpm_*, prototypes +#include "util.h" // printf, get_keystroke +#include "output.h" // dprintf +#include "std/acpi.h" // RSDP_SIGNATURE, rsdt_descriptor +#include "bregs.h" // struct bregs + + +static const u8 Startup_ST_CLEAR[2] = { 0x00, TPM_ST_CLEAR }; +static const u8 Startup_ST_STATE[2] = { 0x00, TPM_ST_STATE }; + +static const u8 PhysicalPresence_CMD_ENABLE[2] = { 0x00, 0x20 }; +static const u8 PhysicalPresence_CMD_DISABLE[2] = { 0x01, 0x00 }; +static const u8 PhysicalPresence_PRESENT[2] = { 0x00, 0x08 }; +static const u8 PhysicalPresence_NOT_PRESENT_LOCK[2] = { 0x00, 0x14 }; + +static const u8 CommandFlag_FALSE[1] = { 0x00 }; +static const u8 CommandFlag_TRUE[1] = { 0x01 }; + +static const u8 GetCapability_Permanent_Flags[12] = { + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x08 +}; + +static const u8 GetCapability_OwnerAuth[12] = { + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x11 +}; + +static const u8 GetCapability_Timeouts[] = { + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x15 +}; + +static const u8 GetCapability_Durations[] = { + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x20 +}; + + +#define RSDP_CAST(ptr) ((struct rsdp_descriptor *)ptr) + + +/* helper functions */ + +static inline void *input_buf32(struct bregs *regs) +{ + return MAKE_FLATPTR(regs->es, regs->di); +} + +static inline void *output_buf32(struct bregs *regs) +{ + return MAKE_FLATPTR(regs->ds, regs->si); +} + + +typedef struct { + u8 tpm_probed:1; + u8 tpm_found:1; + u8 tpm_working:1; + u8 if_shutdown:1; + u8 tpm_driver_to_use:4; +} tpm_state_t; + + +static tpm_state_t tpm_state = { + .tpm_driver_to_use = TPM_INVALID_DRIVER, +}; + + +/******************************************************** + Extensions for TCG-enabled BIOS + *******************************************************/ + + +static u32 +is_tpm_present(void) +{ + u32 rc = 0; + unsigned int i; + + for (i = 0; i < TPM_NUM_DRIVERS; i++) { + struct tpm_driver *td = &tpm_drivers[i]; + if (td->probe() != 0) { + td->init(); + tpm_state.tpm_driver_to_use = i; + rc = 1; + break; + } + } + + return rc; +} + +static void +probe_tpm(void) +{ + if (!tpm_state.tpm_probed) { + tpm_state.tpm_probed = 1; + tpm_state.tpm_found = (is_tpm_present() != 0); + tpm_state.tpm_working = tpm_state.tpm_found; + } +} + +static int +has_working_tpm(void) +{ + probe_tpm(); + + return tpm_state.tpm_working; +} + +static struct tcpa_descriptor_rev2 * +find_tcpa_by_rsdp(struct rsdp_descriptor *rsdp) +{ + u32 ctr = 0; + struct tcpa_descriptor_rev2 *tcpa = NULL; + struct rsdt_descriptor *rsdt; + u32 length; + u16 off; + + rsdt = (struct rsdt_descriptor *)rsdp->rsdt_physical_address; + if (!rsdt) + return NULL; + + length = rsdt->length; + off = offsetof(struct rsdt_descriptor, entry); + + while ((off + sizeof(rsdt->entry[0])) <= length) { + /* try all pointers to structures */ + tcpa = (struct tcpa_descriptor_rev2 *)(int)rsdt->entry[ctr]; + + /* valid TCPA ACPI table ? */ + if (tcpa->signature == TCPA_SIGNATURE && + checksum((u8 *)tcpa, tcpa->length) == 0) + break; + + tcpa = NULL; + off += sizeof(rsdt->entry[0]); + ctr++; + } + + return tcpa; +} + + +static struct tcpa_descriptor_rev2 * +find_tcpa_table(void) +{ + struct tcpa_descriptor_rev2 *tcpa = NULL; + struct rsdp_descriptor *rsdp = RsdpAddr; + + if (rsdp) + tcpa = find_tcpa_by_rsdp(rsdp); + else + tpm_state.if_shutdown = 1; + + if (!rsdp) + dprintf(DEBUG_tcg, + "TCGBIOS: RSDP was NOT found! -- Disabling interface.\n"); + else if (!tcpa) + dprintf(DEBUG_tcg, "TCGBIOS: TCPA ACPI was NOT found!\n"); + + return tcpa; +} + + +static u8 * +get_lasa_base_ptr(u32 *log_area_minimum_length) +{ + u8 *log_area_start_address = 0; + struct tcpa_descriptor_rev2 *tcpa = find_tcpa_table(); + + if (tcpa) { + log_area_start_address = (u8 *)(long)tcpa->log_area_start_address; + if (log_area_minimum_length) + *log_area_minimum_length = tcpa->log_area_minimum_length; + } + + return log_area_start_address; +} + + +/* clear the ACPI log */ +static void +reset_acpi_log(void) +{ + u32 log_area_minimum_length; + u8 *log_area_start_address = get_lasa_base_ptr(&log_area_minimum_length); + + if (log_area_start_address) + memset(log_area_start_address, 0x0, log_area_minimum_length); +} + + +/* + initialize the TCPA ACPI subsystem; find the ACPI tables and determine + where the TCPA table is. + */ +static void +tpm_acpi_init(void) +{ + tpm_state.if_shutdown = 0; + tpm_state.tpm_probed = 0; + tpm_state.tpm_found = 0; + tpm_state.tpm_working = 0; + + if (!has_working_tpm()) { + tpm_state.if_shutdown = 1; + return; + } + + reset_acpi_log(); +} + + +static u32 +transmit(u8 locty, const struct iovec iovec[], + u8 *respbuffer, u32 *respbufferlen, + enum tpmDurationType to_t) +{ + u32 rc = 0; + u32 irc; + struct tpm_driver *td; + unsigned int i; + + if (tpm_state.tpm_driver_to_use == TPM_INVALID_DRIVER) + return TCG_FATAL_COM_ERROR; + + td = &tpm_drivers[tpm_state.tpm_driver_to_use]; + + irc = td->activate(locty); + if (irc != 0) { + /* tpm could not be activated */ + return TCG_FATAL_COM_ERROR; + } + + for (i = 0; iovec[i].length; i++) { + irc = td->senddata(iovec[i].data, + iovec[i].length); + if (irc != 0) + return TCG_FATAL_COM_ERROR; + } + + irc = td->waitdatavalid(); + if (irc != 0) + return TCG_FATAL_COM_ERROR; + + irc = td->waitrespready(to_t); + if (irc != 0) + return TCG_FATAL_COM_ERROR; + + irc = td->readresp(respbuffer, + respbufferlen); + if (irc != 0) + return TCG_FATAL_COM_ERROR; + + td->ready(); + + return rc; +} + + +/* + * Send a TPM command with the given ordinal. Append the given buffer + * containing all data in network byte order to the command (this is + * the custom part per command) and expect a response of the given size. + * If a buffer is provided, the response will be copied into it. + */ +static u32 +build_and_send_cmd_od(u32 ordinal, const u8 *append, u32 append_size, + u8 *resbuffer, u32 return_size, u32 *returnCode, + const u8 *otherdata, u32 otherdata_size, + enum tpmDurationType to_t) +{ +#define MAX_APPEND_SIZE 12 +#define MAX_RESPONSE_SIZE sizeof(struct tpm_res_getcap_perm_flags) + u32 rc; + u8 ibuffer[TPM_REQ_HEADER_SIZE + MAX_APPEND_SIZE]; + u8 obuffer[MAX_RESPONSE_SIZE]; + struct tpm_req_header *trqh = (struct tpm_req_header *)ibuffer; + struct tpm_rsp_header *trsh = (struct tpm_rsp_header *)obuffer; + u8 locty = 0; + struct iovec iovec[3]; + u32 obuffer_len = sizeof(obuffer); + u32 idx = 1; + + if (append_size > MAX_APPEND_SIZE || + return_size > MAX_RESPONSE_SIZE) { + dprintf(DEBUG_tcg, "TCGBIOS: size of requested buffers too big."); + return TCG_FIRMWARE_ERROR; + } + + iovec[0].data = trqh; + iovec[0].length = TPM_REQ_HEADER_SIZE + append_size; + + if (otherdata) { + iovec[1].data = (void *)otherdata; + iovec[1].length = otherdata_size; + idx = 2; + } + + iovec[idx].data = NULL; + iovec[idx].length = 0; + + memset(ibuffer, 0x0, sizeof(ibuffer)); + memset(obuffer, 0x0, sizeof(obuffer)); + + trqh->tag = cpu_to_be16(0xc1); + trqh->totlen = cpu_to_be32(TPM_REQ_HEADER_SIZE + append_size + otherdata_size); + trqh->ordinal = cpu_to_be32(ordinal); + + if (append_size) + memcpy((char *)trqh + sizeof(*trqh), + append, append_size); + + rc = transmit(locty, iovec, obuffer, &obuffer_len, to_t); + if (rc) + return rc; + + *returnCode = be32_to_cpu(trsh->errcode); + + if (resbuffer) + memcpy(resbuffer, trsh, return_size); + + return 0; +} + + +static u32 +build_and_send_cmd(u32 ordinal, const u8 *append, u32 append_size, + u8 *resbuffer, u32 return_size, u32 *returnCode, + enum tpmDurationType to_t) +{ + return build_and_send_cmd_od(ordinal, append, append_size, + resbuffer, return_size, returnCode, + NULL, 0, to_t); +} + + +static u32 +determine_timeouts(void) +{ + u32 rc; + u32 returnCode; + struct tpm_res_getcap_timeouts timeouts; + struct tpm_res_getcap_durations durations; + struct tpm_driver *td = &tpm_drivers[tpm_state.tpm_driver_to_use]; + u32 i; + + rc = build_and_send_cmd(TPM_ORD_GetCapability, + GetCapability_Timeouts, + sizeof(GetCapability_Timeouts), + (u8 *)&timeouts, + sizeof(struct tpm_res_getcap_timeouts), + &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "TCGBIOS: Return code from TPM_GetCapability(Timeouts)" + " = 0x%08x\n", returnCode); + + if (rc || returnCode) + goto err_exit; + + rc = build_and_send_cmd(TPM_ORD_GetCapability, + GetCapability_Durations, + sizeof(GetCapability_Durations), + (u8 *)&durations, + sizeof(struct tpm_res_getcap_durations), + &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "TCGBIOS: Return code from TPM_GetCapability(Durations)" + " = 0x%08x\n", returnCode); + + if (rc || returnCode) + goto err_exit; + + for (i = 0; i < 3; i++) + durations.durations[i] = be32_to_cpu(durations.durations[i]); + + for (i = 0; i < 4; i++) + timeouts.timeouts[i] = be32_to_cpu(timeouts.timeouts[i]); + + dprintf(DEBUG_tcg, "TCGBIOS: timeouts: %u %u %u %u\n", + timeouts.timeouts[0], + timeouts.timeouts[1], + timeouts.timeouts[2], + timeouts.timeouts[3]); + + dprintf(DEBUG_tcg, "TCGBIOS: durations: %u %u %u\n", + durations.durations[0], + durations.durations[1], + durations.durations[2]); + + + td->set_timeouts(timeouts.timeouts, durations.durations); + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tpm_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +static u32 +tpm_startup(void) +{ + u32 rc; + u32 returnCode; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + dprintf(DEBUG_tcg, "TCGBIOS: Starting with TPM_Startup(ST_CLEAR)\n"); + rc = build_and_send_cmd(TPM_ORD_Startup, + Startup_ST_CLEAR, sizeof(Startup_ST_CLEAR), + NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "Return code from TPM_Startup = 0x%08x\n", + returnCode); + + if (CONFIG_COREBOOT) { + /* with other firmware on the system the TPM may already have been + * initialized + */ + if (returnCode == TPM_INVALID_POSTINIT) + returnCode = 0; + } + + if (rc || returnCode) + goto err_exit; + + rc = build_and_send_cmd(TPM_ORD_SelfTestFull, NULL, 0, + NULL, 10, &returnCode, TPM_DURATION_TYPE_LONG); + + dprintf(DEBUG_tcg, "Return code from TPM_SelfTestFull = 0x%08x\n", + returnCode); + + if (rc || returnCode) + goto err_exit; + + rc = build_and_send_cmd(TSC_ORD_ResetEstablishmentBit, NULL, 0, + NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "Return code from TSC_ResetEstablishmentBit = 0x%08x\n", + returnCode); + + if (rc || (returnCode != 0 && returnCode != TPM_BAD_LOCALITY)) + goto err_exit; + + rc = determine_timeouts(); + if (rc) + goto err_exit; + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tpm_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +u32 +tpm_start(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + tpm_acpi_init(); + + return tpm_startup(); +} + + +u32 +tpm_leave_bios(void) +{ + u32 rc; + u32 returnCode; + + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + rc = build_and_send_cmd(TPM_ORD_PhysicalPresence, + PhysicalPresence_CMD_ENABLE, + sizeof(PhysicalPresence_CMD_ENABLE), + NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT); + if (rc || returnCode) + goto err_exit; + + rc = build_and_send_cmd(TPM_ORD_PhysicalPresence, + PhysicalPresence_NOT_PRESENT_LOCK, + sizeof(PhysicalPresence_NOT_PRESENT_LOCK), + NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT); + if (rc || returnCode) + goto err_exit; + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tpm_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +u32 +tpm_s3_resume(void) +{ + u32 rc; + u32 returnCode; + + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + dprintf(DEBUG_tcg, "TCGBIOS: Resuming with TPM_Startup(ST_STATE)\n"); + + rc = build_and_send_cmd(TPM_ORD_Startup, + Startup_ST_STATE, sizeof(Startup_ST_STATE), + NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "TCGBIOS: ReturnCode from TPM_Startup = 0x%08x\n", + returnCode); + + if (rc || returnCode) + goto err_exit; + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tpm_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} diff --git a/src/tcgbios.h b/src/tcgbios.h new file mode 100644 index 0000000..c6abd2c --- /dev/null +++ b/src/tcgbios.h @@ -0,0 +1,386 @@ +#ifndef TCGBIOS_H +#define TCGBIOS_H + +#include "types.h" + +#define TCG_MAGIC 0x41504354L + +/* Define for section 12.3 */ +#define TCG_PC_OK 0x0 +#define TCG_PC_TPMERROR 0x1 +#define TCG_PC_LOGOVERFLOW 0x2 +#define TCG_PC_UNSUPPORTED 0x3 + +#define TPM_ALG_SHA 0x4 + +#define TCG_MAGIC 0x41504354L +#define TCG_VERSION_MAJOR 1 +#define TCG_VERSION_MINOR 2 + +#define TPM_OK 0x0 +#define TPM_RET_BASE 0x1 +#define TCG_GENERAL_ERROR (TPM_RET_BASE + 0x0) +#define TCG_TPM_IS_LOCKED (TPM_RET_BASE + 0x1) +#define TCG_NO_RESPONSE (TPM_RET_BASE + 0x2) +#define TCG_INVALID_RESPONSE (TPM_RET_BASE + 0x3) +#define TCG_INVALID_ACCESS_REQUEST (TPM_RET_BASE + 0x4) +#define TCG_FIRMWARE_ERROR (TPM_RET_BASE + 0x5) +#define TCG_INTEGRITY_CHECK_FAILED (TPM_RET_BASE + 0x6) +#define TCG_INVALID_DEVICE_ID (TPM_RET_BASE + 0x7) +#define TCG_INVALID_VENDOR_ID (TPM_RET_BASE + 0x8) +#define TCG_UNABLE_TO_OPEN (TPM_RET_BASE + 0x9) +#define TCG_UNABLE_TO_CLOSE (TPM_RET_BASE + 0xa) +#define TCG_RESPONSE_TIMEOUT (TPM_RET_BASE + 0xb) +#define TCG_INVALID_COM_REQUEST (TPM_RET_BASE + 0xc) +#define TCG_INVALID_ADR_REQUEST (TPM_RET_BASE + 0xd) +#define TCG_WRITE_BYTE_ERROR (TPM_RET_BASE + 0xe) +#define TCG_READ_BYTE_ERROR (TPM_RET_BASE + 0xf) +#define TCG_BLOCK_WRITE_TIMEOUT (TPM_RET_BASE + 0x10) +#define TCG_CHAR_WRITE_TIMEOUT (TPM_RET_BASE + 0x11) +#define TCG_CHAR_READ_TIMEOUT (TPM_RET_BASE + 0x12) +#define TCG_BLOCK_READ_TIMEOUT (TPM_RET_BASE + 0x13) +#define TCG_TRANSFER_ABORT (TPM_RET_BASE + 0x14) +#define TCG_INVALID_DRV_FUNCTION (TPM_RET_BASE + 0x15) +#define TCG_OUTPUT_BUFFER_TOO_SHORT (TPM_RET_BASE + 0x16) +#define TCG_FATAL_COM_ERROR (TPM_RET_BASE + 0x17) +#define TCG_INVALID_INPUT_PARA (TPM_RET_BASE + 0x18) +#define TCG_TCG_COMMAND_ERROR (TPM_RET_BASE + 0x19) +#define TCG_INTERFACE_SHUTDOWN (TPM_RET_BASE + 0x20) +//define TCG_PC_UNSUPPORTED (TPM_RET_BASE + 0x21) +#define TCG_PC_TPM_NOT_PRESENT (TPM_RET_BASE + 0x22) +#define TCG_PC_TPM_DEACTIVATED (TPM_RET_BASE + 0x23) + + +#define TPM_INVALID_ADR_REQUEST TCG_INVALID_ADR_REQUEST +#define TPM_IS_LOCKED TCG_TPM_IS_LOCKED +#define TPM_INVALID_DEVICE_ID TCG_INVALID_DEVICE_ID +#define TPM_INVALID_VENDOR_ID TCG_INVALID_VENDOR_ID +//define TPM_RESERVED_REG_INVALID +#define TPM_FIRMWARE_ERROR TCG_FIRMWARE_ERROR +#define TPM_UNABLE_TO_OPEN TCG_UNABLE_TO_OPEN +#define TPM_UNABLE_TO_CLOSE TCG_UNABLE_TO_CLOSE +#define TPM_INVALID_RESPONSE TCG_INVALID_RESPONSE +#define TPM_RESPONSE_TIMEOUT TCG_RESPONSE_TIMEOUT +#define TPM_INVALID_ACCESS_REQUEST TCG_INVALID_ACCESS_REQUEST +#define TPM_TRANSFER_ABORT TCG_TRANSFER_ABORT +#define TPM_GENERAL_ERROR TCG_GENERAL_ERROR + + +#define TPM_ORD_SelfTestFull 0x00000050 +#define TPM_ORD_ForceClear 0x0000005d +#define TPM_ORD_GetCapability 0x00000065 +#define TPM_ORD_PhysicalEnable 0x0000006f +#define TPM_ORD_PhysicalDisable 0x00000070 +#define TPM_ORD_SetOwnerInstall 0x00000071 +#define TPM_ORD_PhysicalSetDeactivated 0x00000072 +#define TPM_ORD_Startup 0x00000099 +#define TPM_ORD_PhysicalPresence 0x4000000a +#define TPM_ORD_Extend 0x00000014 +#define TPM_ORD_SHA1Start 0x000000a0 +#define TPM_ORD_SHA1Update 0x000000a1 +#define TPM_ORD_SHA1Complete 0x000000a2 +#define TSC_ORD_ResetEstablishmentBit 0x4000000b + + +#define TPM_ST_CLEAR 0x1 +#define TPM_ST_STATE 0x2 +#define TPM_ST_DEACTIVATED 0x3 + + +/* TPM command error codes */ +#define TPM_INVALID_POSTINIT 0x26 +#define TPM_BAD_LOCALITY 0x3d + + +/* interrupt identifiers (al register) */ +enum irq_ids { + TCG_StatusCheck = 0, + TCG_HashLogExtendEvent = 1, + TCG_PassThroughToTPM = 2, + TCG_ShutdownPreBootInterface = 3, + TCG_HashLogEvent = 4, + TCG_HashAll = 5, + TCG_TSS = 6, + TCG_CompactHashLogExtendEvent = 7, +}; + +/* event types: 10.4.1 / table 11 */ +#define EV_POST_CODE 1 +#define EV_SEPARATOR 4 +#define EV_ACTION 5 +#define EV_EVENT_TAG 6 +#define EV_COMPACT_HASH 12 +#define EV_IPL 13 +#define EV_IPL_PARTITION_DATA 14 + + +#define STATUS_FLAG_SHUTDOWN (1 << 0) + +#define SHA1_BUFSIZE 20 + + +struct iovec +{ + size_t length; + void *data; +}; + + +/* Input and Output blocks for the TCG BIOS commands */ + +struct hleei_short +{ + u16 ipblength; + u16 reserved; + const void *hashdataptr; + u32 hashdatalen; + u32 pcrindex; + const void *logdataptr; + u32 logdatalen; +} PACKED; + + +struct hleei_long +{ + u16 ipblength; + u16 reserved; + void *hashdataptr; + u32 hashdatalen; + u32 pcrindex; + u32 reserved2; + void *logdataptr; + u32 logdatalen; +} PACKED; + + +struct hleeo +{ + u16 opblength; + u16 reserved; + u32 eventnumber; + u8 digest[SHA1_BUFSIZE]; +} PACKED; + + +struct pttti +{ + u16 ipblength; + u16 reserved; + u16 opblength; + u16 reserved2; + u8 tpmopin[0]; +} PACKED; + + +struct pttto +{ + u16 opblength; + u16 reserved; + u8 tpmopout[0]; +}; + + +struct hlei +{ + u16 ipblength; + u16 reserved; + const void *hashdataptr; + u32 hashdatalen; + u32 pcrindex; + u32 logeventtype; + const void *logdataptr; + u32 logdatalen; +} PACKED; + + +struct hleo +{ + u16 opblength; + u16 reserved; + u32 eventnumber; +} PACKED; + + +struct hai +{ + u16 ipblength; + u16 reserved; + const void *hashdataptr; + u32 hashdatalen; + u32 algorithmid; +} PACKED; + + +struct ti +{ + u16 ipblength; + u16 reserved; + u16 opblength; + u16 reserved2; + u8 tssoperandin[0]; +} PACKED; + + +struct to +{ + u16 opblength; + u16 reserved; + u8 tssoperandout[0]; +} PACKED; + + +struct pcpes +{ + u32 pcrindex; + u32 eventtype; + u8 digest[SHA1_BUFSIZE]; + u32 eventdatasize; + u32 event; +} PACKED; + + +/* 10.4.2.1 */ +struct pcctes +{ + u32 eventid; + u32 eventdatasize; + u8 digest[SHA1_BUFSIZE]; +} PACKED; + +/* 10.4.2.1 w/ 10.4.2.2.1 embedded */ +struct pcctes_romex +{ + u32 eventid; + u32 eventdatasize; + u16 reserved; + u16 pfa; + u8 digest[SHA1_BUFSIZE]; +} PACKED; + + +#define TPM_REQ_HEADER \ + u16 tag; \ + u32 totlen; \ + u32 ordinal; + +#define TPM_REQ_HEADER_SIZE (sizeof(u16) + sizeof(u32) + sizeof(u32)) + +#define TPM_RSP_HEADER \ + u16 tag; \ + u32 totlen; \ + u32 errcode; + +#define TPM_RSP_HEADER_SIZE (sizeof(u16) + sizeof(u32) + sizeof(u32)) + +struct tpm_req_header { + TPM_REQ_HEADER; +} PACKED; + + +struct tpm_rsp_header { + TPM_RSP_HEADER; +} PACKED; + + +struct tpm_req_extend { + TPM_REQ_HEADER + u32 pcrindex; + u8 digest[SHA1_BUFSIZE]; +} PACKED; + + +struct tpm_rsp_extend { + TPM_RSP_HEADER + u8 digest[SHA1_BUFSIZE]; +} PACKED; + + +struct tpm_req_getcap_perm_flags { + TPM_REQ_HEADER + u32 capArea; + u32 subCapSize; + u32 subCap; +} PACKED; + + +struct tpm_permanent_flags { + u16 tag; + u8 flags[20]; +} PACKED; + + +enum permFlagsIndex { + PERM_FLAG_IDX_DISABLE = 0, + PERM_FLAG_IDX_OWNERSHIP, + PERM_FLAG_IDX_DEACTIVATED, + PERM_FLAG_IDX_READPUBEK, + PERM_FLAG_IDX_DISABLEOWNERCLEAR, + PERM_FLAG_IDX_ALLOW_MAINTENANCE, + PERM_FLAG_IDX_PHYSICAL_PRESENCE_LIFETIME_LOCK, + PERM_FLAG_IDX_PHYSICAL_PRESENCE_HW_ENABLE, +}; + + +struct tpm_res_getcap_perm_flags { + TPM_RSP_HEADER + u32 size; + struct tpm_permanent_flags perm_flags; +} PACKED; + + +struct tpm_res_getcap_ownerauth { + TPM_RSP_HEADER + u32 size; + u8 flag; +} PACKED; + + +struct tpm_res_getcap_timeouts { + TPM_RSP_HEADER + u32 size; + u32 timeouts[4]; +} PACKED; + + +struct tpm_res_getcap_durations { + TPM_RSP_HEADER + u32 size; + u32 durations[3]; +} PACKED; + + +struct tpm_res_sha1start { + TPM_RSP_HEADER + u32 max_num_bytes; +} PACKED; + + +struct tpm_res_sha1complete { + TPM_RSP_HEADER + u8 hash[20]; +} PACKED; + +struct pttti_extend { + struct pttti pttti; + struct tpm_req_extend req; +} PACKED; + + +struct pttto_extend { + struct pttto pttto; + struct tpm_rsp_extend rsp; +} PACKED; + + +enum ipltype { + IPL_BCV = 0, + IPL_EL_TORITO_1, + IPL_EL_TORITO_2 +}; + + +u32 tpm_start(void); +u32 tpm_leave_bios(void); +u32 tpm_s3_resume(void); + +#endif /* TCGBIOS_H */
This patch implements the TCG BIOS interrupt handler 1ah. It is for example used by trusted grub.
This patch adds an implementation of SHA1 (following NIST specs., IETF RFC 3147 and Wikipedia) for speeding up measurements of code. Trusted Grub for example makes use of this interface and measures (calculates SHA1) of the Linux kernel and initrd. Those files can be rather large and hunting their bytes through the TIS interface as part of the int handler commands invoked by trusted grub does take quite some time due to the many vmexits the interface is creating (one per byte).
There is also a threshold for the size of data to hash (100k) below which the TPM is used and above the internal faster SHA1 algorithm is used.
This patch for example enables trusted grub to interact with the TPM and take additional measurements.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
---
v10: - fixed address calculation when passing regs to int handler; use MAKE_FLATPTR
v6: - passing durations of commands to the transmission function
v5: - adapted code to use SHA1 threshold of driver - support in Kconfig for SHA1 threshold called CONFIG_TPM_TIS_SHA1THRESHOLD - using CONFIG_TPM_TIS_SHA1THRESHOLD in the tpm driver structure to set the threshold for having the TPM do the SHA1 or do it in software - fixing placement of set_cf in inthandler
v4: - return TCG_GENERAL_ERROR if ! has_working_tpm() - in the int handler return TCG_GENERAL_ERROR in eax if ! has_working_tpm() - in the inthanlder call set_cf() before the switch and then in the default case for functions that are not implemented
v3: - use if (CONFIG_TPM_FOR_SHA1)
v2: - use if (!CONFIG_TCGBIOS) everywhere - put sha1() into its own file - move rol() and bswap64() into util.h - move inthandler invocation from stacks.c into handle_1abb() --- Makefile | 2 +- src/clock.c | 13 ++ src/sha1.c | 145 +++++++++++++++ src/sha1.h | 8 + src/tcgbios.c | 571 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- src/tcgbios.h | 3 + src/util.h | 1 + src/x86.h | 7 + 8 files changed, 736 insertions(+), 14 deletions(-) create mode 100644 src/sha1.c create mode 100644 src/sha1.h
diff --git a/Makefile b/Makefile index 82dd716..ba2f207 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ SRCBOTH=misc.c stacks.c output.c string.c block.c cdrom.c disk.c mouse.c kbd.c \ hw/lsi-scsi.c hw/esp-scsi.c hw/megasas.c SRC16=$(SRCBOTH) SRC32FLAT=$(SRCBOTH) post.c memmap.c malloc.c romfile.c x86.c optionroms.c \ - pmm.c font.c boot.c bootsplash.c jpeg.c bmp.c tcgbios.c \ + pmm.c font.c boot.c bootsplash.c jpeg.c bmp.c tcgbios.c sha1.c \ hw/ahci.c hw/pvscsi.c hw/usb-xhci.c hw/usb-hub.c hw/sdcard.c \ fw/coreboot.c fw/lzmadecode.c fw/csm.c fw/biostables.c \ fw/paravirt.c fw/shadow.c fw/pciinit.c fw/smm.c fw/smp.c fw/mtrr.c fw/xen.c \ diff --git a/src/clock.c b/src/clock.c index 9ab0ac0..28c168c 100644 --- a/src/clock.c +++ b/src/clock.c @@ -239,6 +239,18 @@ handle_1a07(struct bregs *regs) set_success(regs); }
+static void +handle_1abb(struct bregs *regs) +{ + if (!CONFIG_TCGBIOS) + return; + + dprintf(DEBUG_tcg, "16: Calling tpm_interrupt_handler\n"); + extern void _cfunc32flat_tpm_interrupt_handler32(void); + call32(_cfunc32flat_tpm_interrupt_handler32, + (u32)MAKE_FLATPTR(GET_SEG(SS), regs), 0); +} + // Unsupported static void handle_1aXX(struct bregs *regs) @@ -260,6 +272,7 @@ handle_1a(struct bregs *regs) case 0x05: handle_1a05(regs); break; case 0x06: handle_1a06(regs); break; case 0x07: handle_1a07(regs); break; + case 0xbb: handle_1abb(regs); break; default: handle_1aXX(regs); break; } } diff --git a/src/sha1.c b/src/sha1.c new file mode 100644 index 0000000..39f43ec --- /dev/null +++ b/src/sha1.c @@ -0,0 +1,145 @@ +// Support for Calculation of SHA1 in SW +// +// Copyright (C) 2006-2011 IBM Corporation +// +// Authors: +// Stefan Berger stefanb@linux.vnet.ibm.com +// +// See: http://www.itl.nist.gov/fipspubs/fip180-1.htm +// RFC3174, Wikipedia's SHA1 alogrithm description +// + +#include "config.h" +#include "byteorder.h" // cpu_to_*, __swab64 +#include "sha1.h" // sha1 +#include "string.h" // memcpy +#include "x86.h" // rol + +typedef struct _sha1_ctx { + u32 h[5]; +} sha1_ctx; + + +static void +sha1_block(u32 *w, sha1_ctx *ctx) +{ + u32 i; + u32 a,b,c,d,e,f; + u32 tmp; + u32 idx; + + static const u32 sha_ko[4] = { + 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 }; + + /* change endianess of given data */ + for (i = 0; i < 16; i++) + w[i] = be32_to_cpu(w[i]); + + for (i = 16; i <= 79; i++) { + tmp = w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]; + w[i] = rol(tmp,1); + } + + a = ctx->h[0]; + b = ctx->h[1]; + c = ctx->h[2]; + d = ctx->h[3]; + e = ctx->h[4]; + + for (i = 0; i <= 79; i++) { + if (i <= 19) { + f = (b & c) | ((b ^ 0xffffffff) & d); + idx = 0; + } else if (i <= 39) { + f = b ^ c ^ d; + idx = 1; + } else if (i <= 59) { + f = (b & c) | (b & d) | (c & d); + idx = 2; + } else { + f = b ^ c ^ d; + idx = 3; + } + + tmp = rol(a, 5) + + f + + e + + sha_ko[idx] + + w[i]; + e = d; + d = c; + c = rol(b, 30); + b = a; + a = tmp; + } + + ctx->h[0] += a; + ctx->h[1] += b; + ctx->h[2] += c; + ctx->h[3] += d; + ctx->h[4] += e; +} + + +static void +sha1_do(sha1_ctx *ctx, const u8 *data32, u32 length) +{ + u32 offset; + u16 num; + u32 bits = 0; + u32 w[80]; + u64 tmp; + + /* treat data in 64-byte chunks */ + for (offset = 0; length - offset >= 64; offset += 64) { + memcpy(w, data32 + offset, 64); + sha1_block((u32 *)w, ctx); + bits += (64 * 8); + } + + /* last block with less than 64 bytes */ + num = length - offset; + bits += (num << 3); + + memcpy(w, data32 + offset, num); + ((u8 *)w)[num] = 0x80; + if (64 - (num + 1) > 0) + memset( &((u8 *)w)[num + 1], 0x0, 64 - (num + 1)); + + if (num >= 56) { + /* cannot append number of bits here */ + sha1_block((u32 *)w, ctx); + memset(w, 0x0, 60); + } + + /* write number of bits to end of block */ + tmp = __swab64(bits); + memcpy(&w[14], &tmp, 8); + + sha1_block(w, ctx); + + /* need to switch result's endianess */ + for (num = 0; num < 5; num++) + ctx->h[num] = cpu_to_be32(ctx->h[num]); +} + + +u32 +sha1(const u8 *data, u32 length, u8 *hash) +{ + if (!CONFIG_TCGBIOS) + return 0; + + sha1_ctx ctx = { + .h[0] = 0x67452301, + .h[1] = 0xefcdab89, + .h[2] = 0x98badcfe, + .h[3] = 0x10325476, + .h[4] = 0xc3d2e1f0, + }; + + sha1_do(&ctx, data, length); + memcpy(hash, &ctx.h[0], 20); + + return 0; +} diff --git a/src/sha1.h b/src/sha1.h new file mode 100644 index 0000000..07aabf3 --- /dev/null +++ b/src/sha1.h @@ -0,0 +1,8 @@ +#ifndef __SHA1_H +#define __SHA1_H + +#include "types.h" // u32 + +u32 sha1(const u8 *data, u32 length, u8 *hash); + +#endif // sha1.h diff --git a/src/tcgbios.c b/src/tcgbios.c index 73ff1f3..c837ab6 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -22,6 +22,7 @@ #include "output.h" // dprintf #include "std/acpi.h" // RSDP_SIGNATURE, rsdt_descriptor #include "bregs.h" // struct bregs +#include "sha1.h" // sha1
static const u8 Startup_ST_CLEAR[2] = { 0x00, TPM_ST_CLEAR }; @@ -286,7 +287,7 @@ transmit(u8 locty, const struct iovec iovec[], * If a buffer is provided, the response will be copied into it. */ static u32 -build_and_send_cmd_od(u32 ordinal, const u8 *append, u32 append_size, +build_and_send_cmd_od(u8 locty, u32 ordinal, const u8 *append, u32 append_size, u8 *resbuffer, u32 return_size, u32 *returnCode, const u8 *otherdata, u32 otherdata_size, enum tpmDurationType to_t) @@ -298,7 +299,6 @@ build_and_send_cmd_od(u32 ordinal, const u8 *append, u32 append_size, u8 obuffer[MAX_RESPONSE_SIZE]; struct tpm_req_header *trqh = (struct tpm_req_header *)ibuffer; struct tpm_rsp_header *trsh = (struct tpm_rsp_header *)obuffer; - u8 locty = 0; struct iovec iovec[3]; u32 obuffer_len = sizeof(obuffer); u32 idx = 1; @@ -325,7 +325,8 @@ build_and_send_cmd_od(u32 ordinal, const u8 *append, u32 append_size, memset(obuffer, 0x0, sizeof(obuffer));
trqh->tag = cpu_to_be16(0xc1); - trqh->totlen = cpu_to_be32(TPM_REQ_HEADER_SIZE + append_size + otherdata_size); + trqh->totlen = cpu_to_be32(TPM_REQ_HEADER_SIZE + append_size + + otherdata_size); trqh->ordinal = cpu_to_be32(ordinal);
if (append_size) @@ -346,11 +347,11 @@ build_and_send_cmd_od(u32 ordinal, const u8 *append, u32 append_size,
static u32 -build_and_send_cmd(u32 ordinal, const u8 *append, u32 append_size, +build_and_send_cmd(u8 locty, u32 ordinal, const u8 *append, u32 append_size, u8 *resbuffer, u32 return_size, u32 *returnCode, enum tpmDurationType to_t) { - return build_and_send_cmd_od(ordinal, append, append_size, + return build_and_send_cmd_od(locty, ordinal, append, append_size, resbuffer, return_size, returnCode, NULL, 0, to_t); } @@ -366,7 +367,7 @@ determine_timeouts(void) struct tpm_driver *td = &tpm_drivers[tpm_state.tpm_driver_to_use]; u32 i;
- rc = build_and_send_cmd(TPM_ORD_GetCapability, + rc = build_and_send_cmd(0, TPM_ORD_GetCapability, GetCapability_Timeouts, sizeof(GetCapability_Timeouts), (u8 *)&timeouts, @@ -379,7 +380,7 @@ determine_timeouts(void) if (rc || returnCode) goto err_exit;
- rc = build_and_send_cmd(TPM_ORD_GetCapability, + rc = build_and_send_cmd(0, TPM_ORD_GetCapability, GetCapability_Durations, sizeof(GetCapability_Durations), (u8 *)&durations, @@ -434,7 +435,7 @@ tpm_startup(void) return TCG_GENERAL_ERROR;
dprintf(DEBUG_tcg, "TCGBIOS: Starting with TPM_Startup(ST_CLEAR)\n"); - rc = build_and_send_cmd(TPM_ORD_Startup, + rc = build_and_send_cmd(0, TPM_ORD_Startup, Startup_ST_CLEAR, sizeof(Startup_ST_CLEAR), NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT);
@@ -452,7 +453,7 @@ tpm_startup(void) if (rc || returnCode) goto err_exit;
- rc = build_and_send_cmd(TPM_ORD_SelfTestFull, NULL, 0, + rc = build_and_send_cmd(0, TPM_ORD_SelfTestFull, NULL, 0, NULL, 10, &returnCode, TPM_DURATION_TYPE_LONG);
dprintf(DEBUG_tcg, "Return code from TPM_SelfTestFull = 0x%08x\n", @@ -461,7 +462,7 @@ tpm_startup(void) if (rc || returnCode) goto err_exit;
- rc = build_and_send_cmd(TSC_ORD_ResetEstablishmentBit, NULL, 0, + rc = build_and_send_cmd(3, TSC_ORD_ResetEstablishmentBit, NULL, 0, NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT);
dprintf(DEBUG_tcg, "Return code from TSC_ResetEstablishmentBit = 0x%08x\n", @@ -510,14 +511,14 @@ tpm_leave_bios(void) if (!has_working_tpm()) return TCG_GENERAL_ERROR;
- rc = build_and_send_cmd(TPM_ORD_PhysicalPresence, + rc = build_and_send_cmd(0, TPM_ORD_PhysicalPresence, PhysicalPresence_CMD_ENABLE, sizeof(PhysicalPresence_CMD_ENABLE), NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT); if (rc || returnCode) goto err_exit;
- rc = build_and_send_cmd(TPM_ORD_PhysicalPresence, + rc = build_and_send_cmd(0, TPM_ORD_PhysicalPresence, PhysicalPresence_NOT_PRESENT_LOCK, sizeof(PhysicalPresence_NOT_PRESENT_LOCK), NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT); @@ -535,6 +536,550 @@ err_exit: return TCG_TCG_COMMAND_ERROR; }
+static int +is_valid_pcpes(struct pcpes *pcpes) +{ + return (pcpes->eventtype != 0); +} + + +static u8 * +get_lasa_last_ptr(u16 *entry_count, u8 **log_area_start_address_next) +{ + struct pcpes *pcpes; + u32 log_area_minimum_length = 0; + u8 *log_area_start_address_base = + get_lasa_base_ptr(&log_area_minimum_length); + u8 *log_area_start_address_last = NULL; + u8 *end = log_area_start_address_base + log_area_minimum_length; + u32 size; + + if (entry_count) + *entry_count = 0; + + if (!log_area_start_address_base) + return NULL; + + while (log_area_start_address_base < end) { + pcpes = (struct pcpes *)log_area_start_address_base; + if (!is_valid_pcpes(pcpes)) + break; + if (entry_count) + (*entry_count)++; + size = pcpes->eventdatasize + offsetof(struct pcpes, event); + log_area_start_address_last = log_area_start_address_base; + log_area_start_address_base += size; + } + + if (log_area_start_address_next) + *log_area_start_address_next = log_area_start_address_base; + + return log_area_start_address_last; +} + + +static u32 +tpm_sha1_calc(const u8 *data, u32 length, u8 *hash) +{ + u32 rc; + u32 returnCode; + struct tpm_res_sha1start start; + struct tpm_res_sha1complete complete; + u32 blocks = length / 64; + u32 rest = length & 0x3f; + u32 numbytes, numbytes_no; + u32 offset = 0; + + rc = build_and_send_cmd(0, TPM_ORD_SHA1Start, + NULL, 0, + (u8 *)&start, + sizeof(struct tpm_res_sha1start), + &returnCode, TPM_DURATION_TYPE_SHORT); + + if (rc || returnCode) + goto err_exit; + + while (blocks > 0) { + + numbytes = be32_to_cpu(start.max_num_bytes); + if (numbytes > blocks * 64) + numbytes = blocks * 64; + + numbytes_no = cpu_to_be32(numbytes); + + rc = build_and_send_cmd_od(0, TPM_ORD_SHA1Update, + (u8 *)&numbytes_no, sizeof(numbytes_no), + NULL, 0, &returnCode, + &data[offset], numbytes, + TPM_DURATION_TYPE_SHORT); + + if (rc || returnCode) + goto err_exit; + + offset += numbytes; + blocks -= (numbytes / 64); + } + + numbytes_no = cpu_to_be32(rest); + + rc = build_and_send_cmd_od(0, TPM_ORD_SHA1Complete, + (u8 *)&numbytes_no, sizeof(numbytes_no), + (u8 *)&complete, + sizeof(struct tpm_res_sha1complete), + &returnCode, + &data[offset], rest, TPM_DURATION_TYPE_SHORT); + + if (rc || returnCode) + goto err_exit; + + memcpy(hash, complete.hash, sizeof(complete.hash)); + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM SHA1 malfunctioning.\n"); + + tpm_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +static u32 +sha1_calc(const u8 *data, u32 length, u8 *hash) +{ + if (length < tpm_drivers[tpm_state.tpm_driver_to_use].sha1threshold) + return tpm_sha1_calc(data, length, hash); + + return sha1(data, length, hash); +} + + +/* + * Extend the ACPI log with the given entry by copying the + * entry data into the log. + * Input + * Pointer to the structure to be copied into the log + * + * Output: + * lower 16 bits of return code contain entry number + * if entry number is '0', then upper 16 bits contain error code. + */ +static u32 +tpm_extend_acpi_log(void *entry_ptr, u16 *entry_count) +{ + u32 log_area_minimum_length, size; + u8 *log_area_start_address_base = + get_lasa_base_ptr(&log_area_minimum_length); + u8 *log_area_start_address_next = NULL; + struct pcpes *pcpes = (struct pcpes *)entry_ptr; + + get_lasa_last_ptr(entry_count, &log_area_start_address_next); + + dprintf(DEBUG_tcg, "TCGBIOS: LASA_BASE = %p, LASA_NEXT = %p\n", + log_area_start_address_base, log_area_start_address_next); + + if (log_area_start_address_next == NULL || log_area_minimum_length == 0) + return TCG_PC_LOGOVERFLOW; + + size = pcpes->eventdatasize + offsetof(struct pcpes, event); + + if ((log_area_start_address_next + size - log_area_start_address_base) > + log_area_minimum_length) { + dprintf(DEBUG_tcg, "TCGBIOS: LOG OVERFLOW: size = %d\n", size); + return TCG_PC_LOGOVERFLOW; + } + + memcpy(log_area_start_address_next, entry_ptr, size); + + (*entry_count)++; + + return 0; +} + + +static u32 +is_preboot_if_shutdown(void) +{ + return tpm_state.if_shutdown; +} + + +static u32 +shutdown_preboot_interface(void) +{ + u32 rc = 0; + + if (!is_preboot_if_shutdown()) { + tpm_state.if_shutdown = 1; + } else { + rc = TCG_INTERFACE_SHUTDOWN; + } + + return rc; +} + + +static void +tpm_shutdown(void) +{ + reset_acpi_log(); + shutdown_preboot_interface(); +} + + +static u32 +pass_through_to_tpm(struct pttti *pttti, struct pttto *pttto) +{ + u32 rc = 0; + u32 resbuflen = 0; + struct tpm_req_header *trh; + u8 locty = 0; + struct iovec iovec[2]; + const u32 *tmp; + + if (is_preboot_if_shutdown()) { + rc = TCG_INTERFACE_SHUTDOWN; + goto err_exit; + } + + trh = (struct tpm_req_header *)pttti->tpmopin; + + if (pttti->ipblength < sizeof(struct pttti) + TPM_REQ_HEADER_SIZE || + pttti->opblength < sizeof(struct pttto) || + be32_to_cpu(trh->totlen) + sizeof(struct pttti) > pttti->ipblength ) { + rc = TCG_INVALID_INPUT_PARA; + goto err_exit; + } + + resbuflen = pttti->opblength - offsetof(struct pttto, tpmopout); + + iovec[0].data = pttti->tpmopin; + tmp = (const u32 *)&((u8 *)iovec[0].data)[2]; + iovec[0].length = cpu_to_be32(*tmp); + + iovec[1].data = NULL; + iovec[1].length = 0; + + rc = transmit(locty, iovec, pttto->tpmopout, &resbuflen, + TPM_DURATION_TYPE_LONG /* worst case */); + if (rc) + goto err_exit; + + pttto->opblength = offsetof(struct pttto, tpmopout) + resbuflen; + pttto->reserved = 0; + +err_exit: + if (rc != 0) { + pttto->opblength = 4; + pttto->reserved = 0; + } + + return rc; +} + + +static u32 +tpm_extend(u8 *hash, u32 pcrindex) +{ + u32 rc; + struct pttto_extend pttto; + struct pttti_extend pttti = { + .pttti = { + .ipblength = sizeof(struct pttti_extend), + .opblength = sizeof(struct pttto_extend), + }, + .req = { + .tag = cpu_to_be16(0xc1), + .totlen = cpu_to_be32(sizeof(pttti.req)), + .ordinal = cpu_to_be32(TPM_ORD_Extend), + .pcrindex = cpu_to_be32(pcrindex), + }, + }; + + memcpy(pttti.req.digest, hash, sizeof(pttti.req.digest)); + + rc = pass_through_to_tpm(&pttti.pttti, &pttto.pttto); + + if (rc == 0) { + if (pttto.pttto.opblength < TPM_RSP_HEADER_SIZE || + pttto.pttto.opblength != + sizeof(struct pttto) + be32_to_cpu(pttto.rsp.totlen) || + be16_to_cpu(pttto.rsp.tag) != 0xc4) { + rc = TCG_FATAL_COM_ERROR; + } + } + + if (rc) + tpm_shutdown(); + + return rc; +} + + +static u32 +hash_all(const struct hai *hai, u8 *hash) +{ + if (is_preboot_if_shutdown() != 0) + return TCG_INTERFACE_SHUTDOWN; + + if (hai->ipblength != sizeof(struct hai) || + hai->hashdataptr == 0 || + hai->hashdatalen == 0 || + hai->algorithmid != TPM_ALG_SHA) + return TCG_INVALID_INPUT_PARA; + + return sha1_calc((const u8 *)hai->hashdataptr, hai->hashdatalen, hash); +} + + +static u32 +hash_log_event(const struct hlei *hlei, struct hleo *hleo) +{ + u32 rc = 0; + u16 size; + struct pcpes *pcpes; + u16 entry_count; + + if (is_preboot_if_shutdown() != 0) { + rc = TCG_INTERFACE_SHUTDOWN; + goto err_exit; + } + + size = hlei->ipblength; + if (size != sizeof(*hlei)) { + rc = TCG_INVALID_INPUT_PARA; + goto err_exit; + } + + pcpes = (struct pcpes *)hlei->logdataptr; + + if (pcpes->pcrindex >= 24 || + pcpes->pcrindex != hlei->pcrindex || + pcpes->eventtype != hlei->logeventtype) { + rc = TCG_INVALID_INPUT_PARA; + goto err_exit; + } + + if ((hlei->hashdataptr != 0) && (hlei->hashdatalen != 0)) { + rc = sha1_calc((const u8 *)hlei->hashdataptr, + hlei->hashdatalen, pcpes->digest); + if (rc) + return rc; + } + + rc = tpm_extend_acpi_log((void *)hlei->logdataptr, &entry_count); + if (rc) + goto err_exit; + + /* updating the log was fine */ + hleo->opblength = sizeof(struct hleo); + hleo->reserved = 0; + hleo->eventnumber = entry_count; + +err_exit: + if (rc != 0) { + hleo->opblength = 2; + hleo->reserved = 0; + } + + return rc; +} + + +static u32 +hash_log_extend_event(const struct hleei_short *hleei_s, struct hleeo *hleeo) +{ + u32 rc = 0; + struct hleo hleo; + struct hleei_long *hleei_l = (struct hleei_long *)hleei_s; + const void *logdataptr; + u32 logdatalen; + struct pcpes *pcpes; + + /* short or long version? */ + switch (hleei_s->ipblength) { + case sizeof(struct hleei_short): + /* short */ + logdataptr = hleei_s->logdataptr; + logdatalen = hleei_s->logdatalen; + break; + + case sizeof(struct hleei_long): + /* long */ + logdataptr = hleei_l->logdataptr; + logdatalen = hleei_l->logdatalen; + break; + + default: + /* bad input block */ + rc = TCG_INVALID_INPUT_PARA; + goto err_exit; + } + + pcpes = (struct pcpes *)logdataptr; + + struct hlei hlei = { + .ipblength = sizeof(hlei), + .hashdataptr = hleei_s->hashdataptr, + .hashdatalen = hleei_s->hashdatalen, + .pcrindex = hleei_s->pcrindex, + .logeventtype= pcpes->eventtype, + .logdataptr = logdataptr, + .logdatalen = logdatalen, + }; + + rc = hash_log_event(&hlei, &hleo); + if (rc) + goto err_exit; + + hleeo->opblength = sizeof(struct hleeo); + hleeo->reserved = 0; + hleeo->eventnumber = hleo.eventnumber; + + rc = tpm_extend(pcpes->digest, hleei_s->pcrindex); + +err_exit: + if (rc != 0) { + hleeo->opblength = 4; + hleeo->reserved = 0; + } + + return rc; + +} + + +static u32 +tss(struct ti *ti, struct to *to) +{ + u32 rc = 0; + + if (is_preboot_if_shutdown() == 0) { + rc = TCG_PC_UNSUPPORTED; + } else { + rc = TCG_INTERFACE_SHUTDOWN; + } + + to->opblength = sizeof(struct to); + to->reserved = 0; + + return rc; +} + + +static u32 +compact_hash_log_extend_event(u8 *buffer, + u32 info, + u32 length, + u32 pcrindex, + u32 *edx_ptr) +{ + u32 rc = 0; + struct hleeo hleeo; + struct pcpes pcpes = { + .pcrindex = pcrindex, + .eventtype = EV_COMPACT_HASH, + .eventdatasize = sizeof(info), + .event = info, + }; + struct hleei_short hleei = { + .ipblength = sizeof(hleei), + .hashdataptr = buffer, + .hashdatalen = length, + .pcrindex = pcrindex, + .logdataptr = &pcpes, + .logdatalen = sizeof(pcpes), + }; + + rc = hash_log_extend_event(&hleei, &hleeo); + if (rc == 0) + *edx_ptr = hleeo.eventnumber; + + return rc; +} + + +void VISIBLE32FLAT +tpm_interrupt_handler32(struct bregs *regs) +{ + if (!CONFIG_TCGBIOS) + return; + + set_cf(regs, 0); + + if (!has_working_tpm()) { + regs->eax = TCG_GENERAL_ERROR; + return; + } + + switch ((enum irq_ids)regs->al) { + case TCG_StatusCheck: + if (is_tpm_present() == 0) { + /* no TPM available */ + regs->eax = TCG_PC_TPM_NOT_PRESENT; + } else { + regs->eax = 0; + regs->ebx = TCG_MAGIC; + regs->ch = TCG_VERSION_MAJOR; + regs->cl = TCG_VERSION_MINOR; + regs->edx = 0x0; + regs->esi = (u32)get_lasa_base_ptr(NULL); + regs->edi = + (u32)get_lasa_last_ptr(NULL, NULL); + } + break; + + case TCG_HashLogExtendEvent: + regs->eax = + hash_log_extend_event( + (struct hleei_short *)input_buf32(regs), + (struct hleeo *)output_buf32(regs)); + break; + + case TCG_PassThroughToTPM: + regs->eax = + pass_through_to_tpm((struct pttti *)input_buf32(regs), + (struct pttto *)output_buf32(regs)); + break; + + case TCG_ShutdownPreBootInterface: + regs->eax = shutdown_preboot_interface(); + break; + + case TCG_HashLogEvent: + regs->eax = hash_log_event((struct hlei*)input_buf32(regs), + (struct hleo*)output_buf32(regs)); + break; + + case TCG_HashAll: + regs->eax = + hash_all((struct hai*)input_buf32(regs), + (u8 *)output_buf32(regs)); + break; + + case TCG_TSS: + regs->eax = tss((struct ti*)input_buf32(regs), + (struct to*)output_buf32(regs)); + break; + + case TCG_CompactHashLogExtendEvent: + regs->eax = + compact_hash_log_extend_event((u8 *)input_buf32(regs), + regs->esi, + regs->ecx, + regs->edx, + ®s->edx); + break; + + default: + set_cf(regs, 1); + } + + return; +} +
u32 tpm_s3_resume(void) @@ -550,7 +1095,7 @@ tpm_s3_resume(void)
dprintf(DEBUG_tcg, "TCGBIOS: Resuming with TPM_Startup(ST_STATE)\n");
- rc = build_and_send_cmd(TPM_ORD_Startup, + rc = build_and_send_cmd(0, TPM_ORD_Startup, Startup_ST_STATE, sizeof(Startup_ST_STATE), NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT);
diff --git a/src/tcgbios.h b/src/tcgbios.h index c6abd2c..332d785 100644 --- a/src/tcgbios.h +++ b/src/tcgbios.h @@ -379,6 +379,9 @@ enum ipltype { };
+struct bregs; +void tpm_interrupt_handler32(struct bregs *regs); + u32 tpm_start(void); u32 tpm_leave_bios(void); u32 tpm_s3_resume(void); diff --git a/src/util.h b/src/util.h index 09bb8a9..2244090 100644 --- a/src/util.h +++ b/src/util.h @@ -75,6 +75,7 @@ u32 find_resume_vector(void); void acpi_reboot(void); void find_acpi_features(void); extern struct smbios_entry_point *SMBiosAddr; +struct smbios_entry_point *get_smbios_entry_point(); void copy_smbios(void *pos); void display_uuid(void); void copy_table(void *pos); diff --git a/src/x86.h b/src/x86.h index 14ebb7d..d82d790 100644 --- a/src/x86.h +++ b/src/x86.h @@ -129,6 +129,13 @@ static inline u32 getesp(void) { return esp; }
+static inline u32 rol(u32 val, u16 rol) { + u32 res; + asm volatile("rol %%cl, %%eax" + : "=a" (res) : "a" (val), "c" (rol)); + return res; +} + static inline void outb(u8 value, u16 port) { __asm__ __volatile__("outb %b0, %w1" : : "a"(value), "Nd"(port)); }
On Mon, Mar 23, 2015 at 02:22:17PM -0400, Stefan Berger wrote:
This patch implements the TCG BIOS interrupt handler 1ah. It is for example used by trusted grub.
This patch adds an implementation of SHA1 (following NIST specs., IETF RFC 3147 and Wikipedia) for speeding up measurements of code. Trusted Grub for example makes use of this interface and measures (calculates SHA1) of the Linux kernel and initrd. Those files can be rather large and hunting their bytes through the TIS interface as part of the int handler commands invoked by trusted grub does take quite some time due to the many vmexits the interface is creating (one per byte).
There is also a threshold for the size of data to hash (100k) below which the TPM is used and above the internal faster SHA1 algorithm is used.
This patch for example enables trusted grub to interact with the TPM and take additional measurements.
[...]
--- /dev/null +++ b/src/sha1.c @@ -0,0 +1,145 @@ +// Support for Calculation of SHA1 in SW +// +// Copyright (C) 2006-2011 IBM Corporation +// +// Authors: +// Stefan Berger stefanb@linux.vnet.ibm.com +// +// See: http://www.itl.nist.gov/fipspubs/fip180-1.htm +// RFC3174, Wikipedia's SHA1 alogrithm description +//
This file needs an LGPLv3 license statement.
[...]
--- a/src/x86.h +++ b/src/x86.h @@ -129,6 +129,13 @@ static inline u32 getesp(void) { return esp; }
+static inline u32 rol(u32 val, u16 rol) {
- u32 res;
- asm volatile("rol %%cl, %%eax"
: "=a" (res) : "a" (val), "c" (rol));
- return res;
+}
Because of the mixed 16bit/32bit code in SeaBIOS, all assembler must use size suffixes - so the above should be "roll" instead of "rol".
As before - both issues are minor and can be addressed after merge (as long as there is agreement that the sha1.c file can be licensed as LGPLv3).
-Kevin
On 03/23/2015 08:13 PM, Kevin O'Connor wrote:
On Mon, Mar 23, 2015 at 02:22:17PM -0400, Stefan Berger wrote:
This patch implements the TCG BIOS interrupt handler 1ah. It is for example used by trusted grub.
This patch adds an implementation of SHA1 (following NIST specs., IETF RFC 3147 and Wikipedia) for speeding up measurements of code. Trusted Grub for example makes use of this interface and measures (calculates SHA1) of the Linux kernel and initrd. Those files can be rather large and hunting their bytes through the TIS interface as part of the int handler commands invoked by trusted grub does take quite some time due to the many vmexits the interface is creating (one per byte).
There is also a threshold for the size of data to hash (100k) below which the TPM is used and above the internal faster SHA1 algorithm is used.
This patch for example enables trusted grub to interact with the TPM and take additional measurements.
[...]
--- /dev/null +++ b/src/sha1.c @@ -0,0 +1,145 @@ +// Support for Calculation of SHA1 in SW +// +// Copyright (C) 2006-2011 IBM Corporation +// +// Authors: +// Stefan Berger stefanb@linux.vnet.ibm.com +// +// See: http://www.itl.nist.gov/fipspubs/fip180-1.htm +// RFC3174, Wikipedia's SHA1 alogrithm description +//
This file needs an LGPLv3 license statement.
[...]
--- a/src/x86.h +++ b/src/x86.h @@ -129,6 +129,13 @@ static inline u32 getesp(void) { return esp; }
+static inline u32 rol(u32 val, u16 rol) {
- u32 res;
- asm volatile("rol %%cl, %%eax"
: "=a" (res) : "a" (val), "c" (rol));
- return res;
+}
Because of the mixed 16bit/32bit code in SeaBIOS, all assembler must use size suffixes - so the above should be "roll" instead of "rol".
Ok, fixed.
As before - both issues are minor and can be addressed after merge (as long as there is agreement that the sha1.c file can be licensed as LGPLv3).
It can have that license. I can post v11 or you can modify it, either way is fine.
FYI: I have 3 more patches beyond the ones I am posting. I am using them mostly for testing purposes, so for testing the QEMU TIS for example. One of them tests the SHA1 with the first 5 input vectors from here: http://www.di-mgt.com.au/sha_testvectors.html The tests pass.
Stefan
On Tue, Mar 24, 2015 at 11:10:03AM -0400, Stefan Berger wrote:
On 03/23/2015 08:13 PM, Kevin O'Connor wrote:
Because of the mixed 16bit/32bit code in SeaBIOS, all assembler must use size suffixes - so the above should be "roll" instead of "rol".
Ok, fixed.
As before - both issues are minor and can be addressed after merge (as long as there is agreement that the sha1.c file can be licensed as LGPLv3).
It can have that license. I can post v11 or you can modify it, either way is fine.
Thanks. I pushed the first three patches into a test branch at:
https://github.com/KevinOConnor/seabios/tree/tcg-testing
I'd like to get confirmation that this works for the Xen requirements before merging.
-Kevin
On 03/25/2015 06:42 PM, Kevin O'Connor wrote:
On Tue, Mar 24, 2015 at 11:10:03AM -0400, Stefan Berger wrote:
On 03/23/2015 08:13 PM, Kevin O'Connor wrote:
Because of the mixed 16bit/32bit code in SeaBIOS, all assembler must use size suffixes - so the above should be "roll" instead of "rol".
Ok, fixed.
As before - both issues are minor and can be addressed after merge (as long as there is agreement that the sha1.c file can be licensed as LGPLv3).
It can have that license. I can post v11 or you can modify it, either way is fine.
Thanks. I pushed the first three patches into a test branch at:
https://github.com/KevinOConnor/seabios/tree/tcg-testing
I'd like to get confirmation that this works for the Xen requirements before merging.
I don't use Xen. I hope that Quan will provide feedback.
Stefan
-----Original Message----- From: Stefan Berger [mailto:stefanb@linux.vnet.ibm.com] Sent: Thursday, March 26, 2015 6:18 PM To: Kevin O'Connor; Xu, Quan Cc: seabios@seabios.org; stefano.stabellini@eu.citrix.com Subject: Re: [PATCH v10 3/6] Support for BIOS interrupt handler
On 03/25/2015 06:42 PM, Kevin O'Connor wrote:
On Tue, Mar 24, 2015 at 11:10:03AM -0400, Stefan Berger wrote:
On 03/23/2015 08:13 PM, Kevin O'Connor wrote:
Because of the mixed 16bit/32bit code in SeaBIOS, all assembler must use size suffixes - so the above should be "roll" instead of "rol".
Ok, fixed.
As before - both issues are minor and can be addressed after merge (as long as there is agreement that the sha1.c file can be licensed as LGPLv3).
It can have that license. I can post v11 or you can modify it, either way is fine.
Thanks. I pushed the first three patches into a test branch at:
https://github.com/KevinOConnor/seabios/tree/tcg-testing
I'd like to get confirmation that this works for the Xen requirements before merging.
I don't use Xen. I hope that Quan will provide feedback.
Stefan
Sure, I am glad to help you test it :):) Try to https://github.com/KevinOConnor/seabios/tree/tcg-testing ??
Quan
This patch adds invocations of functions that measure various parts of the code and data through various parts of the BIOS code. It follows TCG specifications on what needs to be measured. It also adds the implementation of the called functions.
Reference for what needs to be measured can be found in section 3.2.2++ in
http://www.trustedcomputinggroup.org/resources/pc_client_work_group_specific...
The first measurements are done once the ACPI tables have been initialized.
Once booted into Linux, the current measurements produce the following logs which can be found in /sys/kernel/security/tpm0/ascii_bios_measurements. The below log also shows measurements from trusted grub.
1 3fb240d2a04085a4e84f81e4398e070ed5a18163 06 [SMBIOS] 2 cc812353fc277c1fab99e0b721752a1392984566 06 [Option ROM] 2 9dbd87163112e5670378abe4510491259a61f411 05 [Start Option ROM Scan] 2 6f74e357331b8dee11bbad85f27bc66cb873106c 06 [Option ROM] 2 5626eb7ac05c7231e46d7461e7d3839b03ae9fad 06 [Option ROM] 4 c1e25c3f6b0dc78d57296aa2870ca6f782ccf80f 05 [Calling INT 19h] 0 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 1 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 2 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 3 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 4 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 5 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 6 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 7 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 4 8cf2fe6c87d4d0b2998a43da630292e6d85ee8b6 05 [Booting BCV device 80h (HDD)] 4 5dff94459a3e2d13a433ef94afdc306144565bf7 0d [IPL] 5 d1b33afde65ad47502332af957c60f20c84c1edc 0e [IPL Partition Data] 4 487ce764b527ccad17f1d04243d0136fa981e6c4 0d [IPL] 4 91d285e4dead566324c8938a3cc75803f462d9a1 0d [IPL] 4 8ba79ac98bb491524fef29defc724daaf6263d35 0d [IPL] 4 c591c15b82e4ff30e7383a4ff1ef3b41b38521ac 06 [] 4 8cdc27ec545eda33fbba1e8b8dae4da5c7206972 04 [Grub Event Separator] 5 8cdc27ec545eda33fbba1e8b8dae4da5c7206972 04 [Grub Event Separator] 5 e8673b9e14b02dc12d8ccfd0176bca7a3de7fc3c 0e [IPL Partition Data] 5 0163e375a0af7525c5dac1a8e74b277359e40d1d 1105 [] 8 4be30f67c3d48ab7f04d9c0fd07f06d4c68379be 1205 [] 8 54c83965978de9708d026016ecb0e70660e04388 1305 [] 5 2431ed60130faeaf3a045f21963f71cacd46a029 04 [OS Event Separator] 8 2431ed60130faeaf3a045f21963f71cacd46a029 04 [OS Event Separator] 8 f3973cae05d6e2055062119d6e6e1e077b7df876 1005 []
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
---
v5: - call code for measuring CDROM boot sector
v4: - return TCG_GENERAL_ERROR if ! has_working_tpm()
v2: - dropping call to tcpa_measure_post - converting tcpa_option_rom and tcpa_ipl functions to get pointers rather than segments passed - introduce public get_smbios_entry_point() function and use it rather than searching for the entry point - use dprintf(DEBUG_tcg, ...) --- src/boot.c | 12 ++ src/cdrom.c | 11 ++ src/optionroms.c | 4 + src/post.c | 4 + src/tcgbios.c | 369 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 9 ++ 6 files changed, 409 insertions(+)
diff --git a/src/boot.c b/src/boot.c index d6b1fb7..3044fb0 100644 --- a/src/boot.c +++ b/src/boot.c @@ -19,6 +19,7 @@ #include "std/disk.h" // struct mbr_s #include "string.h" // memset #include "util.h" // irqtimer_calc +#include "tcgbios.h" // tpm_*
/**************************************************************** @@ -628,6 +629,10 @@ boot_disk(u8 bootdrv, int checksig) } }
+ /* specs: 8.2.3 steps 4 and 5 */ + tpm_add_bootdevice_ipl(0, bootdrv, + IPL_BCV, MAKE_FLATPTR(bootseg, 0), 512); + /* Canonicalize bootseg:bootip */ u16 bootip = (bootseg & 0x0fff) << 4; bootseg &= 0xf000; @@ -651,6 +656,11 @@ boot_cdrom(struct drive_s *drive_g)
u8 bootdrv = CDEmu.emulated_drive; u16 bootseg = CDEmu.load_segment; + + /* specs: 8.2.5.6 */ + tpm_add_bootdevice_ipl(1, bootdrv, + IPL_EL_TORITO_1, MAKE_FLATPTR(bootseg, 0), 512); + /* Canonicalize bootseg:bootip */ u16 bootip = (bootseg & 0x0fff) << 4; bootseg &= 0xf000; @@ -733,6 +743,8 @@ do_boot(int seq_nr) break; }
+ tpm_returned_via_int18h(); + // Boot failed: invoke the boot recovery function struct bregs br; memset(&br, 0, sizeof(br)); diff --git a/src/cdrom.c b/src/cdrom.c index 92f34f4..0866680 100644 --- a/src/cdrom.c +++ b/src/cdrom.c @@ -15,6 +15,7 @@ #include "std/disk.h" // DISK_RET_SUCCESS #include "string.h" // memset #include "util.h" // cdrom_prepboot +#include "tcgbios.h" // tpm_*
// Locks for removable devices u8 CDRom_locks[BUILD_MAX_EXTDRIVE] VARLOW; @@ -192,6 +193,12 @@ cdrom_boot(struct drive_s *drive) if (buffer[0x20] != 0x88) return 11; // Bootable
+ /* specs: 8.2.3 step 5 and 8.2.5.6, measure El Torito boot catalog */ + /* measure 2048 bytes (one sector) */ + tpm_add_bootdevice_ipl(1, 0, + IPL_EL_TORITO_2, MAKE_FLATPTR(GET_SEG(SS), buffer), + 2048); + // Fill in el-torito cdrom emulation fields. emulated_drive_gf = drive; u8 media = buffer[0x21]; @@ -228,6 +235,10 @@ cdrom_boot(struct drive_s *drive) dop.buf_fl += count*CDROM_SECTOR_SIZE; }
+ /* specs: 8.2.3 step 4 and 8.2.5.6, measure El Torito boot image */ + /* measure 1st 512 bytes */ + tpm_ipl(IPL_EL_TORITO_1, MAKE_FLATPTR(boot_segment, 0), 512); + if (media == 0) { // No emulation requested - return success. CDEmu.emulated_drive = EXTSTART_CD + cdid; diff --git a/src/optionroms.c b/src/optionroms.c index f6047e6..b93a4b1 100644 --- a/src/optionroms.c +++ b/src/optionroms.c @@ -19,6 +19,7 @@ #include "std/pnpbios.h" // PNP_SIGNATURE #include "string.h" // memset #include "util.h" // get_pnp_offset +#include "tcgbios.h" // tpm_*
static int EnforceChecksum, S3ResumeVga, RunPCIroms;
@@ -80,6 +81,7 @@ is_valid_rom(struct rom_header *rom) if (EnforceChecksum) return 0; } + tpm_option_rom(rom, len); return 1; }
@@ -354,6 +356,8 @@ optionrom_setup(void) memset(sources, 0, sizeof(sources)); u32 post_vga = rom_get_last();
+ tpm_start_option_rom_scan(); + if (CONFIG_OPTIONROMS_DEPLOYED) { // Option roms are already deployed on the system. u32 pos = post_vga; diff --git a/src/post.c b/src/post.c index 0c7b36b..e273c18 100644 --- a/src/post.c +++ b/src/post.c @@ -197,6 +197,9 @@ prepareboot(void) void VISIBLE32FLAT startBoot(void) { + tpm_calling_int19h(); + tpm_add_event_separators(); + // Clear low-memory allocations (required by PMM spec). memset((void*)BUILD_STACK_ADDR, 0, BUILD_EBDA_MINIMUM - BUILD_STACK_ADDR);
@@ -223,6 +226,7 @@ maininit(void)
// Initialize TPM tpm_start(); + tpm_smbios_measure();
// Run vga option rom vgarom_setup(); diff --git a/src/tcgbios.c b/src/tcgbios.c index c837ab6..3f31231 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -23,6 +23,7 @@ #include "std/acpi.h" // RSDP_SIGNATURE, rsdt_descriptor #include "bregs.h" // struct bregs #include "sha1.h" // sha1 +#include "std/smbios.h"
static const u8 Startup_ST_CLEAR[2] = { 0x00, TPM_ST_CLEAR }; @@ -56,6 +57,8 @@ static const u8 GetCapability_Durations[] = { 0x00, 0x00, 0x01, 0x20 };
+static u8 evt_separator[] = {0xff,0xff,0xff,0xff}; +
#define RSDP_CAST(ptr) ((struct rsdp_descriptor *)ptr)
@@ -1080,6 +1083,372 @@ tpm_interrupt_handler32(struct bregs *regs) return; }
+/* + * Add a measurement to the log; the data at data_seg:data/length are + * appended to the TCG_PCClientPCREventStruct + * + * Input parameters: + * pcrIndex : which PCR to extend + * event_type : type of event; specs 10.4.1 + * data : pointer to the data (i.e., string) to be added to the log + * length : length of the data + */ +static u32 +tpm_add_measurement_to_log(u32 pcrIndex, + u32 event_type, + const char *data, u32 length) +{ + u32 rc = 0; + struct hleeo hleeo; + u8 _pcpes[offsetof(struct pcpes, event) + 400]; + struct pcpes *pcpes = (struct pcpes *)_pcpes; + + if (length < sizeof(_pcpes) - offsetof(struct pcpes, event)) { + + pcpes->pcrindex = pcrIndex; + pcpes->eventtype = event_type; + memset(&pcpes->digest, 0x0, sizeof(pcpes->digest)); + pcpes->eventdatasize = length; + memcpy(&pcpes->event, data, length); + + struct hleei_short hleei = { + .ipblength = sizeof(hleei), + .hashdataptr = &pcpes->event, + .hashdatalen = length, + .pcrindex = pcrIndex, + .logdataptr = _pcpes, + .logdatalen = length + offsetof(struct pcpes, event), + }; + + rc = hash_log_extend_event(&hleei, &hleeo); + } else { + rc = TCG_GENERAL_ERROR; + } + + return rc; +} + + +/* + * Add a measurement to the log; further description of the data + * that are to be hashed are NOT appended to the TCG_PCClientPCREventStruc. + * Input parameters: + * pcrIndex : PCR to extend + * event_type : type of event; specs 10.4.1 + * ptr : 32 bit pointer to the data to be hashed + * length : length of the data to be hashed + * + * Returns lower 16 bit of return code of TCG_HashLogExtendEvent. '0' means + * success, otherwise an error is indicated. + */ +static u32 +tpm_add_measurement_to_log_simple(u32 pcrIndex, + u16 event_type, + const u8 *ptr, u32 length) +{ + struct hleeo hleeo; + struct pcpes pcpes = { + .pcrindex = pcrIndex, + .eventtype = event_type, + /* specs: 10.4.1, EV_IPL eventfield should not contain the code.*/ + .eventdatasize = 0, + .event = 0, + }; + struct hleei_short hleei = { + .ipblength = sizeof(hleei), + .hashdataptr = ptr, + .hashdatalen = length, + .pcrindex = pcrIndex, + .logdataptr = &pcpes, + .logdatalen = offsetof(struct pcpes, event), + }; + + return hash_log_extend_event(&hleei, &hleeo); +} + + +/* + * Add a measurement to the list of measurements + * pcrIndex : PCR to be extended + * event_type : type of event; specs 10.4.1 + * data : additional parameter; used as parameter for 10.4.3 + * 'action index' + */ +static u32 +tpm_add_measurement(u32 pcrIndex, + u16 event_type, + const char *string) +{ + u32 rc; + + switch (event_type) { + case EV_SEPARATOR: + rc = tpm_add_measurement_to_log_simple(pcrIndex, + event_type, + (u8 *)evt_separator, + 4); + break; + + case EV_ACTION: + rc = tpm_add_measurement_to_log(pcrIndex, + event_type, + string, + strlen(string)); + break; + + default: + rc = TCG_INVALID_INPUT_PARA; + } + + return rc; +} + + +u32 +tpm_calling_int19h(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + return tpm_add_measurement(4, EV_ACTION, + "Calling INT 19h"); +} + + +u32 +tpm_returned_via_int18h(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + return tpm_add_measurement(4, EV_ACTION, + "Return via INT 18h"); +} + + +/* + * Add event separators for PCRs 0 to 7; specs 8.2.3 + */ +u32 +tpm_add_event_separators(void) +{ + u32 rc; + u32 pcrIndex = 0; + + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + while (pcrIndex <= 7) { + rc = tpm_add_measurement(pcrIndex, EV_SEPARATOR, NULL); + if (rc) + break; + pcrIndex ++; + } + + return rc; +} + + +/* + * Add a measurement regarding the boot device (CDRom, Floppy, HDD) to + * the list of measurements. + */ +static u32 +tpm_add_bootdevice(u32 bootcd, u32 bootdrv) +{ + const char *string; + + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + switch (bootcd) { + case 0: + switch (bootdrv) { + case 0: + string = "Booting BCV device 00h (Floppy)"; + break; + + case 0x80: + string = "Booting BCV device 80h (HDD)"; + break; + + default: + string = "Booting unknown device"; + break; + } + + break; + + default: + string = "Booting from CD ROM device"; + } + + return tpm_add_measurement_to_log(4, EV_ACTION, + string, strlen(string)); +} + + +/* + * Add measurement to the log about option rom scan + * 10.4.3 : action 14 + */ +u32 +tpm_start_option_rom_scan(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + return tpm_add_measurement(2, EV_ACTION, + "Start Option ROM Scan"); +} + + +/* + * Add measurement to the log about an option rom + */ +u32 +tpm_option_rom(const void *addr, u32 len) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + u32 rc; + struct pcctes_romex pcctes = { + .eventid = 7, /* 10.4.2.3.7 */ + .eventdatasize = sizeof(u16) + sizeof(u16) + SHA1_BUFSIZE, + }; + + rc = sha1((const u8 *)addr, len, pcctes.digest); + if (rc) + return rc; + + return tpm_add_measurement_to_log(2, + EV_EVENT_TAG, + (const char *)&pcctes, + sizeof(pcctes)); +} + + +u32 +tpm_smbios_measure(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + u32 rc; + struct pcctes pcctes = { + .eventid = 1, /* 10.4.2.3.1 */ + .eventdatasize = SHA1_BUFSIZE, + }; + struct smbios_entry_point *sep = SMBiosAddr; + + dprintf(DEBUG_tcg, "TCGBIOS: SMBIOS at %p\n", sep); + + if (!sep) + return 0; + + rc = sha1((const u8 *)sep->structure_table_address, + sep->structure_table_length, pcctes.digest); + if (rc) + return rc; + + return tpm_add_measurement_to_log(1, + EV_EVENT_TAG, + (const char *)&pcctes, + sizeof(pcctes)); +} + + +/* + * Add a measurement to the log in support of 8.2.5.3 + * Creates two log entries + * + * Input parameter: + * bootcd : 0: MBR of hdd, 1: boot image, 2: boot catalog of El Torito + * addr : address where the IP data are located + * length : IP data length in bytes + */ +u32 +tpm_ipl(enum ipltype bootcd, const u8 *addr, u32 length) +{ + u32 rc; + + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + switch (bootcd) { + case IPL_EL_TORITO_1: + /* specs: 8.2.5.6 El Torito */ + rc = tpm_add_measurement_to_log_simple(4, + EV_IPL, + addr, + length); + break; + + case IPL_EL_TORITO_2: + /* specs: 8.2.5.6 El Torito */ + rc = tpm_add_measurement_to_log_simple(5, + EV_IPL_PARTITION_DATA, + addr, + length); + break; + + default: + /* specs: 8.2.5.3 */ + /* equivalent to: dd if=/dev/hda ibs=1 count=440 | sha1sum */ + rc = tpm_add_measurement_to_log_simple(4, + EV_IPL, + addr, + 0x1b8); + + if (rc) + break; + + /* equivalent to: dd if=/dev/hda ibs=1 count=72 skip=440 | sha1sum */ + rc = tpm_add_measurement_to_log_simple(5, + EV_IPL_PARTITION_DATA, + addr + 0x1b8, + 0x48); + } + + return rc; +} + + +u32 +tpm_add_bootdevice_ipl(u32 bootcd, u32 bootdrv, + enum ipltype ipltype, const u8 *addr, u32 length) +{ + u32 rc = tpm_add_bootdevice(bootcd, bootdrv); + if (rc) + return rc; + + return tpm_ipl(ipltype, addr, length); +}
u32 tpm_s3_resume(void) diff --git a/src/tcgbios.h b/src/tcgbios.h index 332d785..7cf4364 100644 --- a/src/tcgbios.h +++ b/src/tcgbios.h @@ -385,5 +385,14 @@ void tpm_interrupt_handler32(struct bregs *regs); u32 tpm_start(void); u32 tpm_leave_bios(void); u32 tpm_s3_resume(void); +u32 tpm_calling_int19h(void); +u32 tpm_returned_via_int18h(void); +u32 tpm_add_bootdevice_ipl(u32 bootcd, u32 bootdrv, + enum ipltype ipltype, const u8 *addr, u32 length); +u32 tpm_add_event_separators(void); +u32 tpm_ipl(enum ipltype bootcd, const u8 *addr, u32 count); +u32 tpm_start_option_rom_scan(void); +u32 tpm_option_rom(const void *addr, u32 len); +u32 tpm_smbios_measure(void);
#endif /* TCGBIOS_H */
This patch implements the specification found here:
http://www.trustedcomputinggroup.org/resources/tcg_physical_presence_interfa...
It adds the necessary BIOS code so that for example an administrator can send messages from the OS to the BIOS for the BIOS to change the state of the TPM upon reboot. With the help of this interface, an administrator does not have to manually interact with the BIOS.
As an example, on Linux the root use can send an opcode to the BIOS through the TPM's sysfs entries following the opcodes listed in table 2 of the above specs. To for example disable and deactivate the TPM, the root user would send opcode '7' to the BIOS:
#> cd /sys/devices/pnp0/00:0?/ppi
#> echo 7 > request
#> reboot
This patch allocates an 'anchor' in the f-segment identifiable by the signature 'TCG_MAGIC'. It also allocates a chunk of memory in the high memory region and points to it from the anchor. The memory in the high memory region may have been initialized with the signature 'TCG_MAGIC' on a previous boot and if this signature is found, the opcode (sent from the OS) is looked at and acted upon by the BIOS.
The implementation requires an ACPI _DSM method to be implemented for the TPM's SSDT. The code in the _DSM will look for the anchor in the f-segment and write the administrator's opcode into the high memory area. The _DSM method is invoked when the root user interacts with the entries shown in the above ppi sysfs directory. The patch implementing the _DSM will be posted independently.
This patch supports opcodes 1-11, 14, 21, and 22.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com --- src/post.c | 3 + src/tcgbios.c | 584 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 54 ++++++ 3 files changed, 641 insertions(+)
diff --git a/src/post.c b/src/post.c index e273c18..4c944c8 100644 --- a/src/post.c +++ b/src/post.c @@ -219,6 +219,7 @@ maininit(void)
// Setup platform devices. platform_hardware_setup(); + tpm_ppi_init();
// Start hardware initialization (if threads allowed during optionroms) if (threads_during_optionroms()) @@ -240,6 +241,8 @@ maininit(void) // Run option roms optionrom_setup();
+ // Process user-requested TPM state change + tpm_ppi_process(); // Allow user to modify overall boot order. interactive_bootmenu(); wait_threads(); diff --git a/src/tcgbios.c b/src/tcgbios.c index 3f31231..1d93d77 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -24,6 +24,7 @@ #include "bregs.h" // struct bregs #include "sha1.h" // sha1 #include "std/smbios.h" +#include "malloc.h" // malloc_*
static const u8 Startup_ST_CLEAR[2] = { 0x00, TPM_ST_CLEAR }; @@ -42,6 +43,11 @@ static const u8 GetCapability_Permanent_Flags[12] = { 0x00, 0x00, 0x01, 0x08 };
+static const u8 GetCapability_STClear_Flags[12] = { + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x09 +}; + static const u8 GetCapability_OwnerAuth[12] = { 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x11 @@ -89,6 +95,10 @@ static tpm_state_t tpm_state = { .tpm_driver_to_use = TPM_INVALID_DRIVER, };
+typedef struct { + u8 op; +} tpm_bios_cfg_t; +
/******************************************************** Extensions for TCG-enabled BIOS @@ -1438,6 +1448,521 @@ tpm_ipl(enum ipltype bootcd, const u8 *addr, u32 length) return rc; }
+static u32 +read_stclear_flags(char *buf, int buf_len) +{ + u32 rc; + u32 returnCode; + struct tpm_res_getcap_stclear_flags stcf; + + memset(buf, 0x0, buf_len); + + rc = build_and_send_cmd(0, TPM_ORD_GetCapability, + GetCapability_STClear_Flags, + sizeof(GetCapability_STClear_Flags), + (u8 *)&stcf, + sizeof(struct tpm_res_getcap_stclear_flags), + &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "TCGBIOS: Return code from TPM_GetCapability() " + "= 0x%08x\n", returnCode); + + if (rc || returnCode) + goto err_exit; + + memcpy(buf, &stcf.stclear_flags, buf_len); + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tpm_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +static u32 +assert_physical_presence(int verbose) +{ + u32 rc = 0; + u32 returnCode; + struct tpm_stclear_flags stcf; + + rc = read_stclear_flags((char *)&stcf, sizeof(stcf)); + if (rc) { + dprintf(DEBUG_tcg, + "Error reading STClear flags: 0x%08x\n", rc); + return rc; + } + + if (stcf.flags[STCLEAR_FLAG_IDX_PHYSICAL_PRESENCE]) + /* physical presence already asserted */ + return 0; + + rc = build_and_send_cmd(0, TPM_ORD_PhysicalPresence, + PhysicalPresence_CMD_ENABLE, + sizeof(PhysicalPresence_CMD_ENABLE), + NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, + "Return code from TSC_PhysicalPresence(CMD_ENABLE) = 0x%08x\n", + returnCode); + + if (rc || returnCode) { + if (verbose) + printf("Error: Could not enable physical presence.\n\n"); + goto err_exit; + } + + rc = build_and_send_cmd(0, TPM_ORD_PhysicalPresence, + PhysicalPresence_PRESENT, + sizeof(PhysicalPresence_PRESENT), + NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, + "Return code from TSC_PhysicalPresence(PRESENT) = 0x%08x\n", + returnCode); + + if (rc || returnCode) { + if (verbose) + printf("Error: Could not set presence flag.\n\n"); + goto err_exit; + } + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tpm_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +static u32 +read_permanent_flags(char *buf, int buf_len) +{ + u32 rc; + u32 returnCode; + struct tpm_res_getcap_perm_flags pf; + + memset(buf, 0x0, buf_len); + + rc = build_and_send_cmd(0, TPM_ORD_GetCapability, + GetCapability_Permanent_Flags, + sizeof(GetCapability_Permanent_Flags), + (u8 *)&pf, + sizeof(struct tpm_res_getcap_perm_flags), + &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "TCGBIOS: Return code from TPM_GetCapability() " + "= 0x%08x\n", returnCode); + + if (rc || returnCode) + goto err_exit; + + memcpy(buf, &pf.perm_flags, buf_len); + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tpm_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +static u32 +read_has_owner(u8 *has_owner) +{ + u32 rc; + u32 returnCode; + struct tpm_res_getcap_ownerauth oauth; + + rc = build_and_send_cmd(0, TPM_ORD_GetCapability, + GetCapability_OwnerAuth, + sizeof(GetCapability_OwnerAuth), + (u8 *)&oauth, + sizeof(struct tpm_res_getcap_ownerauth), + &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "TCGBIOS: Return code from TPM_GetCapability() " + "= 0x%08x\n", returnCode); + + if (rc || returnCode) + goto err_exit; + + *has_owner = oauth.flag; + + return 0; + +err_exit: + dprintf(DEBUG_tcg,"TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tpm_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +static u32 +disable_tpm(int disable, u32 *returnCode, int verbose) +{ + u32 rc; + struct tpm_permanent_flags pf; + + rc = read_permanent_flags((char *)&pf, sizeof(pf)); + if (rc) + return rc; + + if (!!pf.flags[PERM_FLAG_IDX_DISABLE] == !!disable) { + if (verbose) + printf("TPM is already %s.\n,", + disable ? "disabled" : "enabled"); + return 0; + } + + rc = assert_physical_presence(verbose); + if (rc) { + dprintf(DEBUG_tcg, "TCGBIOS: Asserting physical presence failed.\n"); + return rc; + } + + rc = build_and_send_cmd(0, disable ? TPM_ORD_PhysicalDisable + : TPM_ORD_PhysicalEnable, + NULL, 0, NULL, 10, returnCode, + TPM_DURATION_TYPE_SHORT); + dprintf(DEBUG_tcg, "Return code from TPM_Physical%sable = 0x%08x\n", + disable ? "Dis" : "En", *returnCode); + + if (rc || *returnCode) + goto err_exit; + + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: %sabling the TPM failed.\n", + disable ? "Dis" : "En"); + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tpm_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +static u32 +deactivate_tpm(int deactivate, int allow_reset, u32 *returnCode, int verbose) +{ + u32 rc; + struct tpm_permanent_flags pf; + + rc = read_permanent_flags((char *)&pf, sizeof(pf)); + if (rc) + return rc; + + if (!!pf.flags[PERM_FLAG_IDX_DEACTIVATED] == !!deactivate) { + if (verbose) + printf("TPM is already %s.\n", + deactivate ? "deactivated" : "activated"); + return 0; + } + + if (pf.flags[PERM_FLAG_IDX_DISABLE]) { + if (verbose) + printf("TPM must first be enabled.\n"); + return 0; + } + + rc = assert_physical_presence(verbose); + if (rc) { + dprintf(DEBUG_tcg, "TCGBIOS: Asserting physical presence failed.\n"); + return rc; + } + + rc = build_and_send_cmd(0, TPM_ORD_PhysicalSetDeactivated, + deactivate ? CommandFlag_TRUE + : CommandFlag_FALSE, + deactivate ? sizeof(CommandFlag_TRUE) + : sizeof(CommandFlag_FALSE), + NULL, 10, returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, + "Return code from PhysicalSetDeactivated(%d) = 0x%08x\n", + deactivate ? 1 : 0, *returnCode); + + if (rc || *returnCode) + goto err_exit; + + if (!deactivate && allow_reset) { + if (verbose) { + printf("Requiring a reboot to activate the TPM.\n"); + + msleep(2000); + } + extern void reset_vector(void) __noreturn; + reset_vector(); + } + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tpm_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +static u32 +enable_activate(int allow_reset, u32 *returnCode, int verbose) +{ + u32 rc; + + rc = disable_tpm(0, returnCode, verbose); + if (rc) + return rc; + + rc = deactivate_tpm(0, allow_reset, returnCode, verbose); + + return rc; +} + + +static u32 +force_clear(int enable_activate_before, int enable_activate_after, + u32 *returnCode, int verbose) +{ + u32 rc; + u8 has_owner; + + rc = read_has_owner(&has_owner); + if (rc) + return rc; + if (!has_owner) { + if (verbose) + printf("TPM does not have an owner.\n"); + return 0; + } + + if (enable_activate_before) { + rc = enable_activate(0, returnCode, verbose); + if (rc) { + dprintf(DEBUG_tcg, + "TCGBIOS: Enabling/activating the TPM failed.\n"); + return rc; + } + } + + rc = assert_physical_presence(verbose); + if (rc) { + dprintf(DEBUG_tcg, "TCGBIOS: Asserting physical presence failed.\n"); + return rc; + } + + rc = build_and_send_cmd(0, TPM_ORD_ForceClear, + NULL, 0, NULL, 10, returnCode, + TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "Return code from TPM_ForceClear() = 0x%08x\n", + *returnCode); + + if (rc || *returnCode) + goto err_exit; + + if (!enable_activate_after) { + if (verbose) + printf("Owner successfully cleared.\n" + "You will need to enable/activate the TPM again.\n\n"); + return 0; + } + + enable_activate(1, returnCode, verbose); + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tpm_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +static u32 +set_owner_install(int allow, u32 *returnCode, int verbose) +{ + u32 rc; + u8 has_owner; + struct tpm_permanent_flags pf; + + rc = read_has_owner(&has_owner); + if (rc) + return rc; + if (has_owner) { + if (verbose) + printf("Must first remove owner.\n"); + return 0; + } + + rc = read_permanent_flags((char *)&pf, sizeof(pf)); + if (rc) + return rc; + + if (pf.flags[PERM_FLAG_IDX_DISABLE]) { + if (verbose) + printf("TPM must first be enable.\n"); + return 0; + } + + rc = assert_physical_presence(verbose); + if (rc) { + dprintf(DEBUG_tcg, "TCGBIOS: Asserting physical presence failed.\n"); + return rc; + } + + rc = build_and_send_cmd(0, TPM_ORD_SetOwnerInstall, + (allow) ? CommandFlag_TRUE : + CommandFlag_FALSE, + sizeof(CommandFlag_TRUE), + NULL, 10, returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "Return code from TPM_SetOwnerInstall() = 0x%08x\n", + *returnCode); + + if (rc || *returnCode) + goto err_exit; + + if (verbose) + printf("Installation of owner %s.\n", allow ? "enabled" : "disabled"); + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + tpm_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +static u32 +tpm_process_cfg(const tpm_bios_cfg_t *cfg, int verbose, + u32 *returnCode, u8 *next_step) +{ + u32 rc = 0; + + switch (cfg->op) { + case 0: /* no-op */ + break; + + case 1: + rc = disable_tpm(0, returnCode, verbose); + break; + + case 2: + rc = disable_tpm(1, returnCode, verbose); + break; + + case 3: + rc = deactivate_tpm(0, 1, returnCode, verbose); + break; + + case 4: + rc = deactivate_tpm(1, 1, returnCode, verbose); + break; + + case 5: + rc = force_clear(1, 0, returnCode, verbose); + break; + + case 6: + rc = enable_activate(1, returnCode, verbose); + break; + + case 7: + rc = deactivate_tpm(1, 1, returnCode, verbose); + if (!rc) + rc = disable_tpm(1, returnCode, verbose); + break; + + case 8: + rc = set_owner_install(1, returnCode, verbose); + break; + + case 9: + rc = set_owner_install(0, returnCode, verbose); + break; + + case 10: + *next_step = 8; + rc = enable_activate(1, returnCode, verbose); + /* no reboot happened */ + if (!rc) + rc = set_owner_install(1, returnCode, verbose); + break; + + case 11: + rc = set_owner_install(0, returnCode, verbose); + if (!rc) + rc = deactivate_tpm(1, 0, returnCode, verbose); + if (!rc) + rc = disable_tpm(1, returnCode, verbose); + break; + + case 14: + rc = force_clear(0, 0, returnCode, verbose); + if (!rc) + rc = enable_activate(1, returnCode, verbose); + break; + + case 21: + *next_step = 5; + rc = enable_activate(1, returnCode, verbose); + /* no reboot happened */ + if (!rc) + rc = force_clear(0, 0, returnCode, verbose); + break; + + case 22: + *next_step = 14; + rc = enable_activate(1, returnCode, verbose); + /* no reboot happened */ + if (!rc) + rc = force_clear(0, 0, returnCode, verbose); + *next_step = 0; + if (!rc) + rc = enable_activate(1, returnCode, verbose); + break; + + default: + break; + } + + if (rc) + printf("Op %d: An error occurred: 0x%x\n", cfg->op, rc); + + /* no reboot, no next step */ + *next_step = 0; + + return rc; +}
u32 tpm_add_bootdevice_ipl(u32 bootcd, u32 bootdrv, @@ -1484,3 +2009,62 @@ err_exit: return rc; return TCG_TCG_COMMAND_ERROR; } + +static int tpm_ppi_found_high = 0; +static struct tpm_ppi *tp; + +void +tpm_ppi_init(void) +{ + struct tpm_ppi_anchor *tpa = malloc_fseg(sizeof(*tpa)); + tp = malloc_high(sizeof(*tp)); + + dprintf(DEBUG_tcg, "Allocated TPM PPI structure at 0x%x\n", (int)tp); + + tpa->sign1 = TCG_MAGIC; + tpa->ptr = tp; + tpa->sign2 = TCG_MAGIC; + + if (tp->sign1 != TCG_MAGIC || tp->sign2 != TCG_MAGIC) { + tp->sign1 = TCG_MAGIC; + tp->sign2 = TCG_MAGIC; + /* set number of bytes that ACPI can read/write */ + tp->size = sizeof(tp->opcode) + sizeof(tp->failure) + + sizeof(tp->recent_opcode) + sizeof(tp->response); + tp->opcode = 0; + tp->recent_opcode = 0; + tp->failure = 0; + tpm_ppi_found_high = 0; + } else { + tpm_ppi_found_high = 1; + } +} + +void +tpm_ppi_process(void) +{ + tpm_bios_cfg_t cfg; + + if (tp) { + cfg.op = tp->opcode; + if (!cfg.op) { + /* intermediate step after a reboot? */ + cfg.op = tp->next_step; + } else { + /* last full opcode */ + tp->recent_opcode = cfg.op; + } + if (cfg.op) { + printf("Processing TPM PPI opcode %d\n", cfg.op); + tp->failure = (tpm_process_cfg(&cfg, 0, &tp->response, + &tp->next_step) != 0); + } + } +} + +void +tpm_ppi_dump(void) +{ + printf("tpm_ppi_found_high = %d\n", tpm_ppi_found_high); + printf("tp = %lx\n", (long)tp); +} diff --git a/src/tcgbios.h b/src/tcgbios.h index 7cf4364..bf5b19b 100644 --- a/src/tcgbios.h +++ b/src/tcgbios.h @@ -328,6 +328,36 @@ struct tpm_res_getcap_perm_flags { } PACKED;
+struct tpm_req_getcap_stclear_flags { + TPM_REQ_HEADER + u32 capArea; + u32 subCapSize; + u32 subCap; +} PACKED; + + +struct tpm_stclear_flags { + u16 tag; + u8 flags[5]; +} PACKED; + + +enum stclearFlagsIndex { + STCLEAR_FLAG_IDX_DEACTIVATED = 0, + STCLEAR_FLAG_IDX_DISABLE_FORCE_CLEAR, + STCLEAR_FLAG_IDX_PHYSICAL_PRESENCE, + STCLEAR_FLAG_IDX_PHYSICAL_PRESENCE_LOCK, + STCLEAR_FLAG_IDX_GLOBAL_LOCK, +}; + + +struct tpm_res_getcap_stclear_flags { + TPM_RSP_HEADER + u32 size; + struct tpm_stclear_flags stclear_flags; +} PACKED; + + struct tpm_res_getcap_ownerauth { TPM_RSP_HEADER u32 size; @@ -378,6 +408,30 @@ enum ipltype { IPL_EL_TORITO_2 };
+/* + * physical presence interface + */ + +struct tpm_ppi { + u32 sign1; + u16 size; // number of subsequent bytes for ACPI to access + u8 opcode; // set by ACPI + u8 failure; // set by BIOS (0 = success) + u8 recent_opcode; // set by BIOS + u32 response; // set by BIOS + u8 next_step; // BIOS only + u32 sign2; +} PACKED; + +struct tpm_ppi_anchor { + u32 sign1; + struct tpm_ppi *ptr; + u32 sign2; +}; + +void tpm_ppi_init(void); +void tpm_ppi_dump(void); +void tpm_ppi_process(void);
struct bregs; void tpm_interrupt_handler32(struct bregs *regs);
This patch provides an addtional menu entry that enables the user to control certain aspects of the TPM's state.
If a working TPM has been detected, the top level BIOS menu will look like this:
Press F12 for boot menu. Press F11 to TPM menu.
Upon pressing F11 the TPM menu will be shown:
1. Enable TPM 2. Disable TPM 3. Activate TPM 4. Deactivate TPM 5. Clear ownership 6. Allow installation of owner 7. Prevent installation of owner Escape for previous menu. TPM is enabled, active, does not have an owner but one can be installed.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
---
v9: - to enter TPM menu, use Tab key by default
v6: - passing durations of commands to the transmission function
v5: - fixed an indentation
v2: - use if (!CONFIG_TCGBIOS) ... in all non-static functions - use dprintf(DEBUG_tcg, ...) for printing of debugging info - removing some code used for a future patch --- src/boot.c | 15 ++++++-- src/tcgbios.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 2 + src/util.h | 1 + 4 files changed, 131 insertions(+), 3 deletions(-)
diff --git a/src/boot.c b/src/boot.c index 3044fb0..3be0395 100644 --- a/src/boot.c +++ b/src/boot.c @@ -427,7 +427,7 @@ get_raw_keystroke(void) }
// Read a keystroke - waiting up to 'msec' milliseconds. -static int +int get_keystroke(int msec) { u32 end = irqtimer_calc(msec); @@ -461,13 +461,22 @@ interactive_bootmenu(void)
char *bootmsg = romfile_loadfile("etc/boot-menu-message", NULL); int menukey = romfile_loadint("etc/boot-menu-key", 1); - printf("%s", bootmsg ?: "\nPress ESC for boot menu.\n\n"); - free(bootmsg); + int menukey_tpm = romfile_loadint("etc/boot-menu-key-tpm", 0xf); +again: + printf("%s", bootmsg ?: "\nPress ESC for boot menu.\n"); + if (tpm_is_detected()) + printf("Press Tab for TPM menu.\n"); + printf("\n");
u32 menutime = romfile_loadint("etc/boot-menu-wait", DEFAULT_BOOTMENU_WAIT); enable_bootsplash(); int scan_code = get_keystroke(menutime); disable_bootsplash(); + if (tpm_is_detected() && scan_code == menukey_tpm) { + tpm_menu(); + goto again; + } + free(bootmsg); if (scan_code != menukey) return;
diff --git a/src/tcgbios.c b/src/tcgbios.c index 1d93d77..9dcd316 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -25,6 +25,7 @@ #include "sha1.h" // sha1 #include "std/smbios.h" #include "malloc.h" // malloc_* +#include "stacks.h" // wait_threads
static const u8 Startup_ST_CLEAR[2] = { 0x00, TPM_ST_CLEAR }; @@ -134,6 +135,17 @@ probe_tpm(void) } }
+int +tpm_is_detected(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + probe_tpm(); + + return tpm_state.tpm_found; +} + static int has_working_tpm(void) { @@ -2068,3 +2080,107 @@ tpm_ppi_dump(void) printf("tpm_ppi_found_high = %d\n", tpm_ppi_found_high); printf("tp = %lx\n", (long)tp); } + + +static void +show_tpm_state(void) +{ + struct tpm_permanent_flags pf; + u8 has_owner; + + if (read_permanent_flags((char *)&pf, sizeof(pf)) || + read_has_owner(&has_owner)) + return; + + printf("TPM is "); + + if (pf.flags[PERM_FLAG_IDX_DISABLE]) + printf("disabled"); + else + printf("enabled"); + + if (pf.flags[PERM_FLAG_IDX_DEACTIVATED]) + printf(", deactivated"); + else + printf(", active"); + + if (has_owner) + printf(" and has an owner.\n"); + else { + printf(", does not have an owner "); + if (pf.flags[PERM_FLAG_IDX_OWNERSHIP]) + printf("but one can be installed.\n"); + else + printf("and an owner cannot be installed.\n"); + } + +} + + +void +tpm_menu(void) +{ + if (!CONFIG_TCGBIOS) + return; + + int scan_code; + u32 rc, returnCode; + u8 next_step; + tpm_bios_cfg_t cfg = { + .op = 0, + }; + + //tpm_ppi_dump(); + + while (get_keystroke(0) >= 0) + ; + wait_threads(); + + for (;;) { + if (has_working_tpm()) { + printf("1. Enable TPM\n" + "2. Disable TPM\n" + "3. Activate TPM\n" + "4. Deactivate TPM\n" + "5. Clear ownership\n" + "6. Allow installation of owner\n" + "7. Prevent installation of owner\n"); + } else { + printf("TPM is not working correctly.\n"); + } + + printf("Escape for previous menu.\n"); + + if (has_working_tpm()) { + show_tpm_state(); + } + + cfg.op = 0; + + while ((scan_code = get_keystroke(1000)) == ~0) + ; + + switch (scan_code) { + case 1: + // ESC + return; + case 2 ... 6: + cfg.op = scan_code - 1; + break; + case 7 ... 8: + cfg.op = scan_code + 1; + break; + default: + continue; + } + + if (has_working_tpm()) { + rc = tpm_process_cfg(&cfg, 1, &returnCode, &next_step); + + if (rc) + printf("An error occurred: 0x%x\n", rc); + } + } +} + + diff --git a/src/tcgbios.h b/src/tcgbios.h index bf5b19b..38b673f 100644 --- a/src/tcgbios.h +++ b/src/tcgbios.h @@ -448,5 +448,7 @@ u32 tpm_ipl(enum ipltype bootcd, const u8 *addr, u32 count); u32 tpm_start_option_rom_scan(void); u32 tpm_option_rom(const void *addr, u32 len); u32 tpm_smbios_measure(void); +int tpm_is_detected(void); +void tpm_menu(void);
#endif /* TCGBIOS_H */ diff --git a/src/util.h b/src/util.h index 2244090..bb24744 100644 --- a/src/util.h +++ b/src/util.h @@ -36,6 +36,7 @@ int bootprio_find_pci_rom(struct pci_device *pci, int instance); int bootprio_find_named_rom(const char *name, int instance); struct usbdevice_s; int bootprio_find_usb(struct usbdevice_s *usbdev, int lun); +int get_keystroke(int msec);
// bootsplash.c void enable_vga_console(void);