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 (not yet in Qemu git) - ACPI support for the TPM device (SSDT table) - ACPI support for measurement logging (TCPA table) - Support for initialzation of the TPM - 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) - TPM-specific menu for controlling aspects of the TPM - [An optional test suite for the TIS interface]
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) - User menu for controlling aspects of the state of the TPM
v4: - if ! has_working_tpm() now returns error code everywhere - tis_test.c now also under LGPLv3 - in inthandler, pulled set_cf() out of switch and then only call it in the default case where we need to indicate that a function is not implemented
v3: - some nits here and there - calling timer_setup now after S3 resume
v2: - following Kevin's comment - refactoring code so that every patch compiles
Regards, Stefan
This patch adds an implementation of a TPM TIS driver for the TPM TIS emulation supported by Qemu (patches posted, not in git yet). Usage of the driver is broken up into several functions. 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.
v2: - adapted tpm_drivers.c to be under LGPLv3
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- src/tpm_drivers.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tpm_drivers.h | 55 +++++++++++++++ 2 files changed, 253 insertions(+)
Index: seabios/src/tpm_drivers.c =================================================================== --- /dev/null +++ seabios/src/tpm_drivers.c @@ -0,0 +1,198 @@ +// Implementation of a TPM driver for the TPM TIS interface +// +// Copyright (C) 2006-2011 IBM Corporation +// Copyright (C) 2006-2011 Stefan Berger stefanb@us.ibm.com +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" +#include "util.h" +#include "tpm_drivers.h" +#include "tcgbios.h" + +/* 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); + + return 1; +} + +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; + + 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, 1000, + 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(); + + writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY); + rc = tis_wait_sts(locty, 1000, + 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(); + + do { + while (burst == 0 && ctr < 2000) { + 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(); + + if (tis_wait_sts(locty, 1000, TIS_STS_VALID, TIS_STS_VALID) != 0) + rc = TCG_NO_RESPONSE; + + return rc; +} + +static u32 tis_waitrespready(u32 timeout) +{ + u32 rc = 0; + u8 locty = tis_find_active_locality(); + + 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] = { + { + .probe = tis_probe, + .init = tis_init, + .activate = tis_activate, + .ready = tis_ready, + .senddata = tis_senddata, + .readresp = tis_readresp, + .waitdatavalid = tis_waitdatavalid, + .waitrespready = tis_waitrespready, + }, +}; Index: seabios/src/tpm_drivers.h =================================================================== --- /dev/null +++ seabios/src/tpm_drivers.h @@ -0,0 +1,55 @@ +#ifndef TPM_DRIVERS_H + +#include "types.h" // u32 + +/* low level driver implementation */ +struct tpm_driver { + 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)(u32 timeout); +}; + +#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 */ + + +#endif /* TPM_DRIVERS_H */
This patch provides ACPI support for the TPM device. It probes for the TPM device and only if a TPM device is found then the TPM's SSDT and TCPA table are created. This patch also connects them to the RSDT.
Since the logging area in the TCPA table requires 64kb, the memory reserved for ACPI tables (config.h) is increased to 96kb.
This patch requires the subsequent patch for it to compile and work.
The IRQ description in the TPM's SSDT is commented since it will be 'safer' to run the TPM in polling mode - the Linux TPM TIS driver for example has too many issues when run in interrupt mode.
The description of the TCPA (client) table can be found here:
http://www.trustedcomputinggroup.org/resources/server_work_group_acpi_genera...
The compiled SSDT description is also part of this patch.
v2: - Increasing the CONFIG_MAX_HIGHTABLE to 96kb - Adding cut-down tcgbios.c|h to keep SeaBIOS compiling - Build tpm_drivers.c and tcgbios.c -> TPM's SSDT and TCPA tables are now visible in Linux
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- Makefile | 11 ++++++- src/acpi-tpm-ssdt.dsl | 22 +++++++++++++++ src/acpi-tpm-ssdt.hex | 26 ++++++++++++++++++ src/acpi.c | 41 ++++++++++++++++++++++++++++ src/acpi.h | 20 +++++++++++++ src/config.h | 2 - src/tcgbios.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 57 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 248 insertions(+), 3 deletions(-)
Index: seabios/src/acpi-tpm-ssdt.dsl =================================================================== --- /dev/null +++ seabios/src/acpi-tpm-ssdt.dsl @@ -0,0 +1,22 @@ +DefinitionBlock ( + "acpi-tpm-ssdt.aml",// Output Filename + "SSDT", // Signature + 0x01, // SSDT Compliance Revision + "BXPC", // OEMID + "BXSSDT", // TABLE ID + 0x1 // OEM Revision + ) +{ + /* TPM with emulated TPM TIS interface */ + Device (TPM) { + Name (_HID, EisaID ("PNP0C31")) + Name (_CRS, ResourceTemplate () + { + Memory32Fixed (ReadWrite, 0xFED40000, 0x00005000) + //IRQNoFlags () {11} + }) + Method (_STA, 0, NotSerialized) { + Return (0x0F) + } + } +} Index: seabios/src/acpi-tpm-ssdt.hex =================================================================== --- /dev/null +++ seabios/src/acpi-tpm-ssdt.hex @@ -0,0 +1,26 @@ +/* + * + * Intel ACPI Component Architecture + * ASL Optimizing Compiler version 20101013-64 [Nov 21 2010] + * Copyright (c) 2000 - 2010 Intel Corporation + * + * Compilation of "out/.dsl.i" - Mon Apr 4 15:44:33 2011 + * + * C source code output + * AML code block contains 0x56 bytes + * + */ +unsigned char AmlCode_TPM[] = +{ + 0x53,0x53,0x44,0x54,0x56,0x00,0x00,0x00, /* 00000000 "SSDTV..." */ + 0x01,0x13,0x42,0x58,0x50,0x43,0x00,0x00, /* 00000008 "..BXPC.." */ + 0x42,0x58,0x53,0x53,0x44,0x54,0x00,0x00, /* 00000010 "BXSSDT.." */ + 0x01,0x00,0x00,0x00,0x49,0x4E,0x54,0x4C, /* 00000018 "....INTL" */ + 0x13,0x10,0x10,0x20,0x5B,0x82,0x30,0x54, /* 00000020 "... [.0T" */ + 0x50,0x4D,0x5F,0x08,0x5F,0x48,0x49,0x44, /* 00000028 "PM_._HID" */ + 0x0C,0x41,0xD0,0x0C,0x31,0x08,0x5F,0x43, /* 00000030 ".A..1._C" */ + 0x52,0x53,0x11,0x11,0x0A,0x0E,0x86,0x09, /* 00000038 "RS......" */ + 0x00,0x01,0x00,0x00,0xD4,0xFE,0x00,0x50, /* 00000040 ".......P" */ + 0x00,0x00,0x79,0x00,0x14,0x09,0x5F,0x53, /* 00000048 "..y..._S" */ + 0x54,0x41,0x00,0xA4,0x0A,0x0F /* 00000050 "TA...." */ +}; Index: seabios/Makefile =================================================================== --- seabios.orig/Makefile +++ seabios/Makefile @@ -20,7 +20,7 @@ SRC16=$(SRCBOTH) system.c disk.c font.c SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \ acpi.c smm.c mptable.c smbios.c pciinit.c optionroms.c mtrr.c \ lzmadecode.c bootsplash.c jpeg.c usb-hub.c paravirt.c dev-i440fx.c \ - pci_region.c + pci_region.c tcgbios.c tpm_drivers.c SRC32SEG=util.c output.c pci.c pcibios.c apm.c stacks.c
cc-option = $(shell if test -z "`$(1) $(2) -S -o /dev/null -xc \ @@ -192,13 +192,20 @@ $(OUT)vgabios.bin: $(OUT)vgabios.bin.raw $(Q)./tools/buildrom.py $< $@
####### dsdt build rules +src/acpi-tpm-ssdt.hex: src/acpi-tpm-ssdt.dsl + @echo "Compiling TPM SSDT" + $(Q)cpp -P $< > $(OUT)$*.dsl.i + $(Q)iasl -tc -p $(OUT)$* $(OUT)$*.dsl.i + $(Q)cp $(OUT)$*.hex $@ + $(Q)sed -i 's/AmlCode/AmlCode_TPM/' $@ + src/%.hex: src/%.dsl @echo "Compiling DSDT" $(Q)cpp -P $< > $(OUT)$*.dsl.i $(Q)iasl -tc -p $(OUT)$* $(OUT)$*.dsl.i $(Q)cp $(OUT)$*.hex $@
-$(OUT)ccode32flat.o: src/acpi-dsdt.hex +$(OUT)ccode32flat.o: src/acpi-dsdt.hex src/acpi-tpm-ssdt.hex
####### Kconfig rules export HOSTCC := $(CC) Index: seabios/src/acpi.c =================================================================== --- seabios.orig/src/acpi.c +++ seabios/src/acpi.c @@ -13,6 +13,8 @@ #include "pci_regs.h" // PCI_INTERRUPT_LINE #include "paravirt.h" #include "dev-i440fx.h" // piix4_fadt_init +#include "acpi-tpm-ssdt.hex" +#include "tcgbios.h" // has_working_tpm
/****************************************************/ /* ACPI tables init */ @@ -586,6 +588,39 @@ static const struct pci_device_id acpi_f PCI_DEVICE_END, };
+ +static u32 add_tpm_device(void **tpm_addr, void **tcpa_addr) +{ + struct tcpa_descriptor_rev2 *tcpa; + + *tpm_addr = NULL; + *tcpa_addr = NULL; + + if (has_working_tpm()) { + u32 laml = 64 * 1024; + *tpm_addr = malloc_high(sizeof(AmlCode_TPM)); + + tcpa = malloc_high(sizeof(*tcpa) + laml); + if (!tcpa || !*tpm_addr) { + warn_noalloc(); + return 1; + } + + if (*tpm_addr) + memcpy(*tpm_addr, AmlCode_TPM, sizeof(AmlCode_TPM)); + + memset(tcpa, 0x0, sizeof(*tcpa) + laml); + u64 lasa = (u32)tcpa + sizeof(*tcpa); + + tcpa->laml = laml; + tcpa->lasa = lasa; + build_header((void*)tcpa, TCPA_SIGNATURE, sizeof(*tcpa), 2); + + *tcpa_addr = tcpa; + } + return 0; +} + struct rsdp_descriptor *RsdpAddr;
#define MAX_ACPI_TABLES 20 @@ -642,6 +677,12 @@ acpi_bios_init(void) } }
+ void *tcpa, *tpm; + if (add_tpm_device(&tpm, &tcpa)) + return; + ACPI_INIT_TABLE(tpm); + ACPI_INIT_TABLE(tcpa); + struct rsdt_descriptor_rev1 *rsdt; size_t rsdt_len = sizeof(*rsdt) + sizeof(u32) * tbl_idx; rsdt = malloc_high(rsdt_len); Index: seabios/src/acpi.h =================================================================== --- seabios.orig/src/acpi.h +++ seabios/src/acpi.h @@ -98,4 +98,24 @@ struct fadt_descriptor_rev1 #endif } 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 laml; + u64 lasa; +} PACKED; + +/* TCPA ACPI definitions */ +#define TCPA_ACPI_CLASS_CLIENT 0 +#define TCPA_ACPI_CLASS_SERVER 1 + + #endif // acpi.h Index: seabios/src/config.h =================================================================== --- seabios.orig/src/config.h +++ seabios/src/config.h @@ -26,7 +26,7 @@ // Space to reserve in f-segment for dynamic allocations #define CONFIG_MAX_BIOSTABLE 2048 // Space to reserve in high-memory for tables -#define CONFIG_MAX_HIGHTABLE (64*1024) +#define CONFIG_MAX_HIGHTABLE (96*1024) // Largest supported externaly facing drive id #define CONFIG_MAX_EXTDRIVE 16
Index: seabios/src/tcgbios.c =================================================================== --- /dev/null +++ seabios/src/tcgbios.c @@ -0,0 +1,72 @@ +// Implementation of the TCG BIOS extension according to the specification +// described in +// https://www.trustedcomputinggroup.org/specs/PCClient/TCG_PCClientImplementat... +// +// Copyright (C) 2006-2011 IBM Corporation +// Copyright (C) 2006-2011 Stefan Berger stefanb@us.ibm.com +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + + +#include "config.h" + +#include "types.h" +#include "tpm_drivers.h" // tpm_drivers[] + + +typedef struct { + u8 tpm_probed:1; + u8 tpm_found:1; + u8 tpm_working:1; + u8 if_shutdown:1; + u8 tpm_driver_to_use:4; +} tcpa_state_t; + + +static tcpa_state_t tcpa_state = { + .tpm_driver_to_use = TPM_INVALID_DRIVER, +}; + +extern struct tpm_driver tpm_drivers[]; + + +/******************************************************** + 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(); + tcpa_state.tpm_driver_to_use = i; + rc = 1; + break; + } + } + + return rc; +} + + +int +has_working_tpm(void) +{ + if (!tcpa_state.tpm_probed) { + tcpa_state.tpm_probed = 1; + tcpa_state.tpm_found = (is_tpm_present() != 0); + tcpa_state.tpm_working = 1; + } + if (!tcpa_state.tpm_working) + return 0; + + return tcpa_state.tpm_found; +} + + Index: seabios/src/tcgbios.h =================================================================== --- /dev/null +++ seabios/src/tcgbios.h @@ -0,0 +1,57 @@ +#ifndef TCGBIOS_H +#define TCGBIOS_H + + +#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 + + +int has_working_tpm(void); + + +#endif /* TCGBIOS_H */
On 04/12/2011 09:32 AM, Stefan Berger wrote:
+static u32 add_tpm_device(void **tpm_addr, void **tcpa_addr) +{
- struct tcpa_descriptor_rev2 *tcpa;
- *tpm_addr = NULL;
- *tcpa_addr = NULL;
- if (has_working_tpm()) {
u32 laml = 64 * 1024;
Kevin,
the above line prepares 64 kb to be allocated for an ACPI table. It works fine if booting a VM from an image. However, when passing the kernel, initrd and command line parameters to Qemu directly (-kernel, -append, ..), the 64kb above seem to be too much -- I see errors in the Linux kernel's dmesg when Linux tries to access the ACPI tables. Lowering the above to 48kb (happens to) makes it work. I am wondering whether something is copying into the ACPI area or what else may be the reason - do you know?
*tpm_addr = malloc_high(sizeof(AmlCode_TPM));
tcpa = malloc_high(sizeof(*tcpa) + laml);
if (!tcpa || !*tpm_addr) {
warn_noalloc();
return 1;
}
Regards, Stefan
On Wed, May 18, 2011 at 03:49:25PM -0400, Stefan Berger wrote:
On 04/12/2011 09:32 AM, Stefan Berger wrote:
+static u32 add_tpm_device(void **tpm_addr, void **tcpa_addr) +{
- struct tcpa_descriptor_rev2 *tcpa;
- *tpm_addr = NULL;
- *tcpa_addr = NULL;
- if (has_working_tpm()) {
u32 laml = 64 * 1024;
Kevin,
the above line prepares 64 kb to be allocated for an ACPI table. It works fine if booting a VM from an image. However, when passing the kernel, initrd and command line parameters to Qemu directly (-kernel, -append, ..), the 64kb above seem to be too much -- I see errors in the Linux kernel's dmesg when Linux tries to access the ACPI tables. Lowering the above to 48kb (happens to) makes it work. I am wondering whether something is copying into the ACPI area or what else may be the reason - do you know?
I don't know of any reason why that would fail. You can print out the address and compare it to the e820 map that SeaBIOS outputs and verify it is properly reserved.
Does increasing the amount of guest system ram help?
BTW, is this just a buffer or could the size be dynamically calculated?
-Kevin
On 05/18/2011 07:44 PM, Kevin O'Connor wrote:
On Wed, May 18, 2011 at 03:49:25PM -0400, Stefan Berger wrote:
On 04/12/2011 09:32 AM, Stefan Berger wrote:
+static u32 add_tpm_device(void **tpm_addr, void **tcpa_addr) +{
- struct tcpa_descriptor_rev2 *tcpa;
- *tpm_addr = NULL;
- *tcpa_addr = NULL;
- if (has_working_tpm()) {
u32 laml = 64 * 1024;
Kevin,
the above line prepares 64 kb to be allocated for an ACPI table. It works fine if booting a VM from an image. However, when passing the kernel, initrd and command line parameters to Qemu directly (-kernel, -append, ..), the 64kb above seem to be too much -- I see errors in the Linux kernel's dmesg when Linux tries to access the ACPI tables. Lowering the above to 48kb (happens to) makes it work. I am wondering whether something is copying into the ACPI area or what else may be the reason - do you know?
I don't know of any reason why that would fail. You can print out the address and compare it to the e820 map that SeaBIOS outputs and verify it is properly reserved.
Does increasing the amount of guest system ram help?
It helped. At some point the initrd and ACPI tables weren't overlapping anymore... I forgot to increase the size reserved for ACPI tables inside the Qemu code...
BTW, is this just a buffer or could the size be dynamically calculated?
Following specs it's supposed to be 64kb. The BIOS writes logs into this area.
Stefan
-Kevin
This patch adds a global get_rsdp() function call and refactors find_resume_vector() to call it.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- src/acpi.c | 12 ++++++++++-- src/acpi.h | 1 + 2 files changed, 11 insertions(+), 2 deletions(-)
Index: seabios/src/acpi.c =================================================================== --- seabios.orig/src/acpi.c +++ seabios/src/acpi.c @@ -706,11 +706,19 @@ acpi_bios_init(void) dprintf(1, "ACPI tables: RSDP=%p RSDT=%p\n", rsdp, rsdt); }
-u32 -find_resume_vector(void) +struct rsdp_descriptor * +get_rsdp(void) { dprintf(4, "rsdp=%p\n", RsdpAddr); if (!RsdpAddr || RsdpAddr->signature != RSDP_SIGNATURE) + return 0; + return RsdpAddr; +} + +u32 +find_resume_vector(void) +{ + if (!get_rsdp()) return 0; struct rsdt_descriptor_rev1 *rsdt = (void*)RsdpAddr->rsdt_physical_address; dprintf(4, "rsdt=%p\n", rsdt); Index: seabios/src/acpi.h =================================================================== --- seabios.orig/src/acpi.h +++ seabios/src/acpi.h @@ -4,6 +4,7 @@ #include "types.h" // u32
void acpi_bios_init(void); +struct rsdp_descriptor *get_rsdp(); u32 find_resume_vector(void);
#define RSDP_SIGNATURE 0x2052545020445352LL // "RSD PTR "
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 once 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 depending on COREBOOT not being selected 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.
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
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- src/Kconfig | 8 + src/boot.c | 2 src/config.h | 1 src/post.c | 5 src/resume.c | 2 src/tcgbios.c | 397 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 312 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 727 insertions(+)
Index: seabios/src/post.c =================================================================== --- seabios.orig/src/post.c +++ seabios/src/post.c @@ -25,6 +25,7 @@ #include "paravirt.h" // qemu_cfg_port_probe #include "ps2port.h" // ps2port_setup #include "virtio-blk.h" // virtio_blk_setup +#include "tcgbios.h" // tcpa_*
/**************************************************************** @@ -238,6 +239,10 @@ maininit(void) mouse_setup(); init_bios_tables();
+ // Initialize tpm (after acpi tables were written) + tcpa_acpi_init(); + tcpa_startup(); + // Run vga option rom vga_setup();
Index: seabios/src/boot.c =================================================================== --- seabios.orig/src/boot.c +++ seabios/src/boot.c @@ -14,6 +14,7 @@ #include "cmos.h" // inb_cmos #include "paravirt.h" // romfile_loadfile #include "pci.h" //pci_bdf_to_* +#include "tcgbios.h" // tcpa_*
/**************************************************************** @@ -449,6 +450,7 @@ boot_prep(void) // Allow user to modify BCV/IPL order. interactive_bootmenu(); wait_threads(); + tcpa_leave_bios();
// Map drives and populate BEV list struct bootentry_s *pos = BootList; Index: seabios/src/resume.c =================================================================== --- seabios.orig/src/resume.c +++ seabios/src/resume.c @@ -10,6 +10,7 @@ #include "biosvar.h" // struct bios_data_area_s #include "bregs.h" // struct bregs #include "acpi.h" // find_resume_vector +#include "tcgbios.h" // tcpa_s3_resume
// Reset DMA controller void @@ -116,6 +117,7 @@ s3_resume(void) if (s3_resume_vector) { dprintf(1, "Jump to resume vector (%x)\n", s3_resume_vector); br.code = FLATPTR_TO_SEGOFF((void*)s3_resume_vector); + tcpa_s3_resume(); } else { dprintf(1, "No resume vector set!\n"); // Jump to the post vector to restart with a normal boot. Index: seabios/src/config.h =================================================================== --- seabios.orig/src/config.h +++ seabios/src/config.h @@ -119,5 +119,6 @@ #define DEBUG_unimplemented 2 #define DEBUG_invalid 3 #define DEBUG_thread 2 +#define DEBUG_tcg 20
#endif // config.h Index: seabios/src/Kconfig =================================================================== --- seabios.orig/src/Kconfig +++ seabios/src/Kconfig @@ -314,6 +314,14 @@ menu "BIOS interfaces" default n help Disable A20 on 16bit boot. + + config TCGBIOS + depends on !COREBOOT + bool "TPM support and TCG BIOS extensions" + default y + help + Provide TPM support along with TCG BIOS extensions + endmenu
menu "BIOS Tables" Index: seabios/src/tcgbios.h =================================================================== --- seabios.orig/src/tcgbios.h +++ seabios/src/tcgbios.h @@ -1,6 +1,22 @@ #ifndef TCGBIOS_H #define TCGBIOS_H
+#include "types.h" +#include "bregs.h" /* struct bregs */ + +#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 @@ -51,7 +67,303 @@ #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 TPM_ST_CLEAR 0x1 +#define TPM_ST_STATE 0x2 +#define TPM_ST_DEACTIVATED 0x3 + + +/* 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_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 +}; + +void tcpa_acpi_init(void); int has_working_tpm(void); +u32 tcpa_startup(void); +u32 tcpa_leave_bios(void); +u32 tcpa_s3_resume(void);
#endif /* TCGBIOS_H */ Index: seabios/src/tcgbios.c =================================================================== --- seabios.orig/src/tcgbios.c +++ seabios/src/tcgbios.c @@ -12,6 +12,48 @@
#include "types.h" #include "tpm_drivers.h" // tpm_drivers[] +#include "util.h" // printf, get_keystroke +#include "tcgbios.h"// tcpa_*, prototypes +#include "acpi.h" // RSDP_SIGNATURE, rsdt_descriptor + + +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[2] = { 0x00, 0x10 }; +static const u8 PhysicalPresence_LOCK[2] = { 0x00, 0x04 }; + +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 +}; + + +#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 { @@ -58,6 +100,9 @@ is_tpm_present(void) int has_working_tpm(void) { + if (!CONFIG_TCGBIOS) + return 0; + if (!tcpa_state.tpm_probed) { tcpa_state.tpm_probed = 1; tcpa_state.tpm_found = (is_tpm_present() != 0); @@ -70,3 +115,355 @@ has_working_tpm(void) }
+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; + + rsdp = get_rsdp(); + + if (rsdp) + tcpa = find_tcpa_by_rsdp(rsdp); + else + tcpa_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 *laml) +{ + u8 *lasa = 0; + struct tcpa_descriptor_rev2 *tcpa = find_tcpa_table(); + + if (tcpa) { + lasa = (u8 *)(long)tcpa->lasa; + if (laml) + *laml = tcpa->laml; + } + + return lasa; +} + + +/* clear the ACPI log */ +static void +reset_acpi_log(void) +{ + u32 laml; + u8 *lasa = get_lasa_base_ptr(&laml); + + if (lasa) + memset(lasa, 0x0, laml); +} + + +/* + initialize the TCPA ACPI subsystem; find the ACPI tables and determine + where the TCPA table is. + */ +void +tcpa_acpi_init(void) +{ + if (!CONFIG_TCGBIOS) + return; + + tcpa_state.if_shutdown = 0; + tcpa_state.tpm_probed = 0; + tcpa_state.tpm_found = 0; + tcpa_state.tpm_working = 0; + + if (!has_working_tpm()) { + tcpa_state.if_shutdown = 1; + return; + } + + reset_acpi_log(); +} + + +static u32 +transmit(u8 locty, const struct iovec iovec[], + u8 *respbuffer, u32 *respbufferlen) +{ + u32 rc = 0; + u32 irc; + struct tpm_driver *td; + unsigned int i; + + if (tcpa_state.tpm_driver_to_use == TPM_INVALID_DRIVER) + return TCG_FATAL_COM_ERROR; + + td = &tpm_drivers[tcpa_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(10000); + 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) +{ +#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 = htons(0xc1); + trqh->totlen = htonl(TPM_REQ_HEADER_SIZE + append_size + otherdata_size); + trqh->ordinal = htonl(ordinal); + + if (append_size) + memcpy((char *)trqh + sizeof(*trqh), + append, append_size); + + rc = transmit(locty, iovec, obuffer, &obuffer_len); + if (rc) + return rc; + + *returnCode = ntohl(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) +{ + return build_and_send_cmd_od(ordinal, append, append_size, + resbuffer, return_size, returnCode, + NULL, 0); +} + + +u32 +tcpa_startup(void) +{ + u32 rc; + u32 returnCode; + + if (!CONFIG_TCGBIOS) + return 0; + + 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); + + dprintf(DEBUG_tcg, "Return code from TPM_Startup = 0x%08x\n", + returnCode); + + if (rc && returnCode) + goto err_exit; + + rc = build_and_send_cmd(TPM_ORD_SelfTestFull, NULL, 0, + NULL, 10, &returnCode); + + dprintf(DEBUG_tcg, "Return code from TPM_SelfTestFull = 0x%08x\n", + returnCode); + + if (rc || returnCode) + goto err_exit; + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tcpa_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +u32 +tcpa_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); + if (rc || returnCode) + goto err_exit; + + rc = build_and_send_cmd(TPM_ORD_PhysicalPresence, + PhysicalPresence_NOT_PRESENT, + sizeof(PhysicalPresence_NOT_PRESENT), + NULL, 10, &returnCode); + if (rc || returnCode) + goto err_exit; + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tcpa_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +u32 +tcpa_s3_resume(void) +{ + u32 rc; + u32 returnCode; + + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + timer_setup(); + + 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); + + 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__); + + tcpa_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +
Stefan Berger wrote:
- enable configuration of SeaBIOS to be built with TCGBIOS extensions depending on COREBOOT not being selected
Why is this neccessary?
//Peter
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.
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()
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- Makefile | 2 src/Kconfig | 8 src/clock.c | 12 + src/sha1.c | 139 ++++++++++++++ src/sha1.h | 8 src/tcgbios.c | 542 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 4 src/util.h | 30 +++ 8 files changed, 744 insertions(+), 1 deletion(-)
Index: seabios/src/tcgbios.c =================================================================== --- seabios.orig/src/tcgbios.c +++ seabios/src/tcgbios.c @@ -15,6 +15,7 @@ #include "util.h" // printf, get_keystroke #include "tcgbios.h"// tcpa_*, prototypes #include "acpi.h" // RSDP_SIGNATURE, rsdt_descriptor +#include "sha1.h" // sha1
static const u8 Startup_ST_CLEAR[2] = { 0x00, TPM_ST_CLEAR }; @@ -466,4 +467,545 @@ err_exit: }
+static int +isValidPcpes(struct pcpes *pcpes) +{ + return (pcpes->eventtype != 0); +} + + +static u8 * +get_lasa_last_ptr(u16 *entry_count, u8 **lasa_next) +{ + struct pcpes *pcpes; + u32 laml; + u8 *lasa_base = get_lasa_base_ptr(&laml); + u8 *lasa_last = NULL; + u8 *end = lasa_base + laml; + u32 size; + + if (entry_count) + *entry_count = 0; + + if (!lasa_base) + return NULL; + + while (lasa_base < end) { + pcpes = (struct pcpes *)lasa_base; + if (!isValidPcpes(pcpes)) + break; + if (entry_count) + (*entry_count)++; + size = pcpes->eventdatasize + offsetof(struct pcpes, event); + lasa_last = lasa_base; + lasa_base += size; + } + + if (lasa_next) + *lasa_next = lasa_base; + + return lasa_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; + + if (length > 100 * 1024) + return sha1(data, length, hash); + + rc = build_and_send_cmd(TPM_ORD_SHA1Start, + NULL, 0, + (u8 *)&start, + sizeof(struct tpm_res_sha1start), + &returnCode); + + if (rc || returnCode) + goto err_exit; + + while (blocks > 0) { + + numbytes = ntohl(start.max_num_bytes); + if (numbytes > blocks * 64) + numbytes = blocks * 64; + + numbytes_no = htonl(numbytes); + + rc = build_and_send_cmd_od(TPM_ORD_SHA1Update, + (u8 *)&numbytes_no, sizeof(numbytes_no), + NULL, 0, &returnCode, + &data[offset], numbytes); + + if (rc || returnCode) + goto err_exit; + + offset += numbytes; + blocks -= (numbytes / 64); + } + + numbytes_no = htonl(rest); + + rc = build_and_send_cmd_od(TPM_ORD_SHA1Complete, + (u8 *)&numbytes_no, sizeof(numbytes_no), + (u8 *)&complete, + sizeof(struct tpm_res_sha1complete), + &returnCode, + &data[offset], rest); + + 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"); + + tcpa_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 (CONFIG_TPM_FOR_SHA1) + 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 +tcpa_extend_acpi_log(void *entry_ptr, u16 *entry_count) +{ + u32 laml, size; + u8 *lasa_base = get_lasa_base_ptr(&laml), *lasa_next; + struct pcpes *pcpes = (struct pcpes *)entry_ptr; + + get_lasa_last_ptr(entry_count, &lasa_next); + + dprintf(DEBUG_tcg, "TCGBIOS: LASA_BASE = %p, LASA_NEXT = %p\n",lasa_base, lasa_next); + + if (lasa_next == NULL || laml == 0) + return TCG_PC_LOGOVERFLOW; + + size = pcpes->eventdatasize + offsetof(struct pcpes, event); + + if ((lasa_next + size - lasa_base) > laml) { + dprintf(DEBUG_tcg, "TCGBIOS: LOG OVERFLOW: size = %d\n", size); + return TCG_PC_LOGOVERFLOW; + } + + memcpy(lasa_next, entry_ptr, size); + + (*entry_count)++; + + return 0; +} + + +static u32 +is_preboot_if_shutdown(void) +{ + return tcpa_state.if_shutdown; +} + + +static u32 +shutdown_preboot_interface(void) +{ + u32 rc = 0; + + if (!is_preboot_if_shutdown()) { + tcpa_state.if_shutdown = 1; + } else { + rc = TCG_INTERFACE_SHUTDOWN; + } + + return rc; +} + + +static void +tcpa_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) || + ntohl(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 = htonl(*tmp); + + iovec[1].data = NULL; + iovec[1].length = 0; + + rc = transmit(locty, iovec, pttto->tpmopout, &resbuflen); + 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 = htons(0xc1), + .totlen = htonl(sizeof(pttti.req)), + .ordinal = htonl(TPM_ORD_Extend), + .pcrindex = htonl(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) + ntohl(pttto.rsp.totlen) || + ntohs(pttto.rsp.tag) != 0xc4) { + rc = TCG_FATAL_COM_ERROR; + } + } + + if (rc) + tcpa_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 = tcpa_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 +tcpa_interrupt_handler32(struct bregs *regs) +{ + if (!CONFIG_TCGBIOS) + return; + + if (!has_working_tpm()) { + regs->eax = TCG_GENERAL_ERROR; + return; + } + + set_cf(regs, 0); + + 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; +} + +
Index: seabios/src/clock.c =================================================================== --- seabios.orig/src/clock.c +++ seabios/src/clock.c @@ -13,6 +13,7 @@ #include "bregs.h" // struct bregs #include "biosvar.h" // GET_GLOBAL #include "usb-hid.h" // usb_check_event +#include "tcgbios.h" // _cfunc32flat_tcpa_interrupt_handler32
// RTC register flags #define RTC_A_UIP 0x80 @@ -434,6 +435,16 @@ handle_1a07(struct bregs *regs) set_success(regs); }
+static void +handle_1abb(struct bregs *regs) +{ + if (!CONFIG_TCGBIOS) + return; + + dprintf(DEBUG_tcg, "16: Calling tcpa_interrupt_handler\n"); + call32(_cfunc32flat_tcpa_interrupt_handler32, (u32)regs, 0); +} + // Unsupported static void handle_1aXX(struct bregs *regs) @@ -456,6 +467,7 @@ handle_1a(struct bregs *regs) case 0x06: handle_1a06(regs); break; case 0x07: handle_1a07(regs); break; case 0xb1: handle_1ab1(regs); break; + case 0xbb: handle_1abb(regs); break; default: handle_1aXX(regs); break; } } Index: seabios/src/Kconfig =================================================================== --- seabios.orig/src/Kconfig +++ seabios/src/Kconfig @@ -322,6 +322,14 @@ menu "BIOS interfaces" help Provide TPM support along with TCG BIOS extensions
+ config TPM_FOR_SHA1 + depends on TCGBIOS + bool "Use the TPM for SHA1 calculations" + default y + help + Either you may use the TPM for SHA1 calculations or + use the internal sha1 algorithm to do it (faster). + endmenu
menu "BIOS Tables" Index: seabios/Makefile =================================================================== --- seabios.orig/Makefile +++ seabios/Makefile @@ -20,7 +20,7 @@ SRC16=$(SRCBOTH) system.c disk.c font.c SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \ acpi.c smm.c mptable.c smbios.c pciinit.c optionroms.c mtrr.c \ lzmadecode.c bootsplash.c jpeg.c usb-hub.c paravirt.c dev-i440fx.c \ - pci_region.c tcgbios.c tpm_drivers.c + pci_region.c tcgbios.c tpm_drivers.c sha1.c SRC32SEG=util.c output.c pci.c pcibios.c apm.c stacks.c
cc-option = $(shell if test -z "`$(1) $(2) -S -o /dev/null -xc \ Index: seabios/src/sha1.c =================================================================== --- /dev/null +++ seabios/src/sha1.c @@ -0,0 +1,139 @@ +// Support for Calculation of SHA1 in SW +// +// Copyright (C) 2006-2011 Stefan Berger stefanb@linux.vnet.ibm.com +// +// See: http://www.itl.nist.gov/fipspubs/fip180-1.htm +// RFC3174, Wikipedia's SHA1 alogrithm description +// + +#include "sha1.h" // sha1 +#include "util.h" // memcpy, rol, bswap_64 + +typedef struct _sha1_ctx { + u32 h[5]; +} sha1_ctx; + + +static const u32 sha_ko[4] = { 0x5a827999, + 0x6ed9eba1, + 0x8f1bbcdc, + 0xca62c1d6 }; + + +static void +sha1_block(u32 *w, sha1_ctx *ctx) +{ + u32 i; + u32 a,b,c,d,e,f; + u32 tmp; + u32 idx; + + /* change endianess of given data */ + for (i = 0; i < 16; i++) + w[i] = htonl(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 = bswap_64(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] = htonl(ctx->h[num]); +} + + +u32 +sha1(const u8 *data, u32 length, u8 *hash) +{ + 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; +} Index: seabios/src/sha1.h =================================================================== --- /dev/null +++ seabios/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 Index: seabios/src/util.h =================================================================== --- seabios.orig/src/util.h +++ seabios/src/util.h @@ -497,4 +497,34 @@ extern u8 BiosChecksum; // version (auto generated file out/version.c) extern const char VERSION[];
+ +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 u64 bswap_64(u64 val) +{ + u32 hi = (u32)(val >> 32); + u32 lo = (u32)val; + + __asm__ __volatile__ ("bswap %%eax" + : "=a" (lo) + : "a" (lo)); + + __asm__ __volatile__ ("bswap %%eax" + : "=a" (hi) + : "a" (hi)); + + return ((u64)lo << 32 | hi); +} + + #endif // util.h Index: seabios/src/tcgbios.h =================================================================== --- seabios.orig/src/tcgbios.h +++ seabios/src/tcgbios.h @@ -359,6 +359,10 @@ enum ipltype { IPL_EL_TORITO_2 };
+ +void tcpa_interrupt_handler32(struct bregs *regs); +void _cfunc32flat_tcpa_interrupt_handler32(struct bregs *regs); + void tcpa_acpi_init(void); int has_working_tpm(void); u32 tcpa_startup(void);
This patch adds invocactions 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. The whole 0xe and 0xf segment are measure for measuring the 'POST'.
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.
0 0f9a2ad992c04a24e081b2a49b984616c4358386 01 [POST CODE] 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 []
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, ...)
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- src/boot.c | 6 src/cdrom.c | 10 + src/optionroms.c | 4 src/post.c | 4 src/smbios.c | 12 + src/smbios.h | 1 src/tcgbios.c | 359 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 8 + 8 files changed, 404 insertions(+)
Index: seabios/src/post.c =================================================================== --- seabios.orig/src/post.c +++ seabios/src/post.c @@ -190,6 +190,9 @@ init_hw(void) void VISIBLE32FLAT startBoot(void) { + tcpa_calling_int19h(); + tcpa_add_event_separators(); + // Clear low-memory allocations (required by PMM spec). memset((void*)BUILD_STACK_ADDR, 0, BUILD_EBDA_MINIMUM - BUILD_STACK_ADDR);
@@ -242,6 +245,7 @@ maininit(void) // Initialize tpm (after acpi tables were written) tcpa_acpi_init(); tcpa_startup(); + tcpa_smbios_measure();
// Run vga option rom vga_setup(); Index: seabios/src/optionroms.c =================================================================== --- seabios.orig/src/optionroms.c +++ seabios/src/optionroms.c @@ -14,6 +14,7 @@ #include "pci_ids.h" // PCI_CLASS_DISPLAY_VGA #include "boot.h" // IPL #include "paravirt.h" // qemu_cfg_* +#include "tcgbios.h" // tcpa_*
/**************************************************************** @@ -134,6 +135,7 @@ is_valid_rom(struct rom_header *rom) if (CONFIG_OPTIONROMS_CHECKSUM) return 0; } + tcpa_option_rom(rom, len); return 1; }
@@ -385,6 +387,8 @@ optionrom_setup(void) memset(sources, 0, sizeof(sources)); u32 post_vga = RomEnd;
+ tcpa_start_option_rom_scan(); + if (CONFIG_OPTIONROMS_DEPLOYED) { // Option roms are already deployed on the system. u32 pos = RomEnd; Index: seabios/src/boot.c =================================================================== --- seabios.orig/src/boot.c +++ seabios/src/boot.c @@ -533,6 +533,10 @@ boot_disk(u8 bootdrv, int checksig) } }
+ tcpa_add_bootdevice(0, bootdrv); + /* specs: 8.2.3 steps 4 and 5 */ + tcpa_ipl(IPL_BCV, MAKE_FLATPTR(bootseg, 0), 512); + /* Canonicalize bootseg:bootip */ u16 bootip = (bootseg & 0x0fff) << 4; bootseg &= 0xf000; @@ -620,6 +624,8 @@ do_boot(u16 seq_nr) break; }
+ tcpa_returned_via_int18h(); + // Boot failed: invoke the boot recovery function struct bregs br; memset(&br, 0, sizeof(br)); Index: seabios/src/cdrom.c =================================================================== --- seabios.orig/src/cdrom.c +++ seabios/src/cdrom.c @@ -11,6 +11,7 @@ #include "biosvar.h" // GET_EBDA #include "ata.h" // ATA_CMD_REQUEST_SENSE #include "blockcmd.h" // CDB_CMD_REQUEST_SENSE +#include "tcgbios.h" // tcpa_*
/**************************************************************** @@ -293,6 +294,11 @@ cdrom_boot(struct drive_s *drive_g) 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) */ + tcpa_add_bootdevice(1, 0); + tcpa_ipl(IPL_EL_TORITO_2, MAKE_FLATPTR(GET_SEG(SS), buffer), 2048); + u16 ebda_seg = get_ebda_seg(); u8 media = buffer[0x21]; SET_EBDA2(ebda_seg, cdemu.media, media); @@ -319,6 +325,10 @@ cdrom_boot(struct drive_s *drive_g) if (ret) return 12;
+ /* specs: 8.2.3 step 4 and 8.2.5.6, measure El Torito boot image */ + /* measure 1st 512 bytes */ + tcpa_ipl(IPL_EL_TORITO_1, MAKE_FLATPTR(boot_segment, 0), 512); + if (media == 0) { // No emulation requested - return success. SET_EBDA2(ebda_seg, cdemu.emulated_extdrive, EXTSTART_CD + cdid); Index: seabios/src/tcgbios.c =================================================================== --- seabios.orig/src/tcgbios.c +++ seabios/src/tcgbios.c @@ -16,6 +16,7 @@ #include "tcgbios.h"// tcpa_*, prototypes #include "acpi.h" // RSDP_SIGNATURE, rsdt_descriptor #include "sha1.h" // sha1 +#include "smbios.h" // get_smbios_entry_point
static const u8 Startup_ST_CLEAR[2] = { 0x00, TPM_ST_CLEAR }; @@ -40,6 +41,8 @@ static const u8 GetCapability_OwnerAuth[ 0x00, 0x00, 0x01, 0x11 };
+static u8 evt_separator[] = {0xff,0xff,0xff,0xff}; +
#define RSDP_CAST(ptr) ((struct rsdp_descriptor *)ptr)
@@ -1008,4 +1011,360 @@ tcpa_interrupt_handler32(struct bregs *r }
+/* + * 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 +tcpa_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 +tcpa_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 +tcpa_add_measurement(u32 pcrIndex, + u16 event_type, + const char *string) +{ + u32 rc; + + switch (event_type) { + case EV_SEPARATOR: + rc = tcpa_add_measurement_to_log_simple(pcrIndex, + event_type, + (u8 *)evt_separator, + 4); + break; + + case EV_ACTION: + rc = tcpa_add_measurement_to_log(pcrIndex, + event_type, + string, + strlen(string)); + break; + + default: + rc = TCG_INVALID_INPUT_PARA; + } + + return rc; +} + + +u32 +tcpa_calling_int19h(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + return tcpa_add_measurement(4, EV_ACTION, + "Calling INT 19h"); +} + + +u32 +tcpa_returned_via_int18h(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + return tcpa_add_measurement(4, EV_ACTION, + "Return via INT 18h"); +} + + +/* + * Add event separators for PCRs 0 to 7; specs 8.2.3 + */ +u32 +tcpa_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 = tcpa_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. + */ +u32 +tcpa_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 tcpa_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 +tcpa_start_option_rom_scan(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + return tcpa_add_measurement(2, EV_ACTION, + "Start Option ROM Scan"); +} + + +/* + * Add measurement to the log about an option rom + */ +u32 +tcpa_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 tcpa_add_measurement_to_log(2, + EV_EVENT_TAG, + (const char *)&pcctes, + sizeof(pcctes)); +} + + +u32 +tcpa_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 = get_smbios_entry_point(); + + 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 tcpa_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 +tcpa_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 = tcpa_add_measurement_to_log_simple(4, + EV_IPL, + addr, + length); + break; + + case IPL_EL_TORITO_2: + /* specs: 8.2.5.6 El Torito */ + rc = tcpa_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 = tcpa_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 = tcpa_add_measurement_to_log_simple(5, + EV_IPL_PARTITION_DATA, + addr + 0x1b8, + 0x48); + } + + return rc; +} + +
Index: seabios/src/smbios.c =================================================================== --- seabios.orig/src/smbios.c +++ seabios/src/smbios.c @@ -10,6 +10,8 @@ #include "paravirt.h" // qemu_cfg_smbios_load_field #include "smbios.h" // struct smbios_entry_point
+static struct smbios_entry_point *smbios_ep; + static void smbios_entry_point_init(u16 max_structure_size, u16 structure_table_length, @@ -24,6 +26,7 @@ smbios_entry_point_init(u16 max_structur free(finaltable); return; } + smbios_ep = ep; memcpy(finaltable, structure_table_address, structure_table_length);
memcpy(ep->anchor_string, "_SM_", 4); @@ -512,3 +515,12 @@ smbios_init(void) smbios_entry_point_init(max_struct_size, p - start, start, nr_structs); free(start); } + +struct smbios_entry_point * +get_smbios_entry_point(void) +{ + if (!smbios_ep || memcmp(smbios_ep->anchor_string, "_SM_", 4) != 0) + return 0; + + return smbios_ep; +} Index: seabios/src/smbios.h =================================================================== --- seabios.orig/src/smbios.h +++ seabios/src/smbios.h @@ -3,6 +3,7 @@
// smbios.c void smbios_init(void); +struct smbios_entry_point *get_smbios_entry_point();
/* SMBIOS entry point -- must be written to a 16-bit aligned address between 0xf0000 and 0xfffff. Index: seabios/src/tcgbios.h =================================================================== --- seabios.orig/src/tcgbios.h +++ seabios/src/tcgbios.h @@ -368,6 +368,14 @@ int has_working_tpm(void); u32 tcpa_startup(void); u32 tcpa_leave_bios(void); u32 tcpa_s3_resume(void); +u32 tcpa_calling_int19h(void); +u32 tcpa_returned_via_int18h(void); +u32 tcpa_add_bootdevice(u32 bootcd, u32 bootdrv); +u32 tcpa_add_event_separators(void); +u32 tcpa_ipl(enum ipltype bootcd, const u8 *addr, u32 count); +u32 tcpa_start_option_rom_scan(void); +u32 tcpa_option_rom(const void *addr, u32 len); +u32 tcpa_smbios_measure(void);
#endif /* TCGBIOS_H */
This patch provides an addtional menu entry that enables the user to control certain aspects of the TPM.
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.
The code for the above 7 menu items is part of this patch.
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
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- src/boot.c | 10 + src/tcgbios.c | 502 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 1 3 files changed, 512 insertions(+), 1 deletion(-)
Index: seabios/src/tcgbios.c =================================================================== --- seabios.orig/src/tcgbios.c +++ seabios/src/tcgbios.c @@ -75,6 +75,10 @@ static tcpa_state_t tcpa_state = {
extern struct tpm_driver tpm_drivers[];
+typedef struct { + u8 op; +} tpm_bios_cfg_t; +
/******************************************************** Extensions for TCG-enabled BIOS @@ -1368,3 +1372,501 @@ tcpa_ipl(enum ipltype bootcd, const u8 *
+static u32 +assert_physical_presence(void) +{ + u32 rc = 0; + u32 returnCode; + + rc = build_and_send_cmd(TPM_ORD_PhysicalPresence, + PhysicalPresence_CMD_ENABLE, + sizeof(PhysicalPresence_CMD_ENABLE), + NULL, 10, &returnCode); + + dprintf(DEBUG_tcg, + "Return code from TSC_PhysicalPresence(CMD_ENABLE) = 0x%08x\n", + returnCode); + + if (rc || returnCode) + goto err_exit; + + rc = build_and_send_cmd(TPM_ORD_PhysicalPresence, + PhysicalPresence_PRESENT, + sizeof(PhysicalPresence_PRESENT), + NULL, 10, &returnCode); + + dprintf(DEBUG_tcg, + "Return code from TSC_PhysicalPresence(PRESENT) = 0x%08x\n", + returnCode); + + if (rc || returnCode) + goto err_exit; + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tcpa_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(TPM_ORD_GetCapability, + GetCapability_Permanent_Flags, + sizeof(GetCapability_Permanent_Flags), + (u8 *)&pf, + sizeof(struct tpm_res_getcap_perm_flags), + &returnCode); + + 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__); + + tcpa_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(TPM_ORD_GetCapability, + GetCapability_OwnerAuth, + sizeof(GetCapability_OwnerAuth), + (u8 *)&oauth, + sizeof(struct tpm_res_getcap_ownerauth), + &returnCode); + + 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__); + + tcpa_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +static u32 +disable_tpm(int disable, int verbose) +{ + u32 rc; + u32 returnCode; + 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(); + if (rc) { + dprintf(DEBUG_tcg, "TCGBIOS: Asserting physical presence failed.\n"); + return rc; + } + + rc = build_and_send_cmd(disable ? TPM_ORD_PhysicalDisable + : TPM_ORD_PhysicalEnable, + NULL, 0, NULL, 10, &returnCode); + 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__); + + tcpa_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +static u32 +deactivate_tpm(int deactivate, int allow_reset, int verbose) +{ + u32 rc; + u32 returnCode; + 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(); + if (rc) { + dprintf(DEBUG_tcg, "TCGBIOS: Asserting physical presence failed.\n"); + return rc; + } + + rc = build_and_send_cmd(TPM_ORD_PhysicalSetDeactivated, + deactivate ? CommandFlag_TRUE + : CommandFlag_FALSE, + deactivate ? sizeof(CommandFlag_TRUE) + : sizeof(CommandFlag_FALSE), + NULL, 10, &returnCode); + + 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); + } + reset_vector(); + } + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tcpa_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +static u32 +enable_activate(int allow_reset, int verbose) +{ + u32 rc; + + rc = disable_tpm(0, verbose); + if (rc) + return rc; + + rc = deactivate_tpm(0, allow_reset, verbose); + + return rc; +} + + +static u32 +force_clear(int enable_activate_before, int enable_activate_after, int verbose) +{ + u32 rc; + u32 returnCode; + 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, verbose); + if (rc) { + dprintf(DEBUG_tcg, + "TCGBIOS: Enabling/activating the TPM failed.\n"); + return rc; + } + } + + rc = assert_physical_presence(); + if (rc) { + dprintf(DEBUG_tcg, "TCGBIOS: Asserting physical presence failed.\n"); + return rc; + } + + rc = build_and_send_cmd(TPM_ORD_ForceClear, + NULL, 0, NULL, 10, &returnCode); + + 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, verbose); + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tcpa_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +static u32 +set_owner_install(int allow, int verbose) +{ + u32 rc, returnCode; + 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(); + if (rc) { + dprintf(DEBUG_tcg, "TCGBIOS: Asserting physical presence failed.\n"); + return rc; + } + + rc = build_and_send_cmd(TPM_ORD_SetOwnerInstall, + (allow) ? CommandFlag_TRUE : + CommandFlag_FALSE, + sizeof(CommandFlag_TRUE), + NULL, 10, &returnCode); + + 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__); + tcpa_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +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"); + } + +} + + +static u32 +tcpa_process_cfg(const tpm_bios_cfg_t *cfg, int verbose) +{ + u32 rc = 0; + + switch (cfg->op) { + case 0: /* no-op */ + break; + + case 1: + rc = disable_tpm(0, verbose); + break; + + case 2: + rc = disable_tpm(1, verbose); + break; + + case 3: + rc = deactivate_tpm(0, 1, verbose); + break; + + case 4: + rc = deactivate_tpm(1, 1, verbose); + break; + + case 5: + rc = force_clear(1, 0, verbose); + break; + + case 6: + rc = set_owner_install(1, verbose); + break; + + case 7: + rc = set_owner_install(0, verbose); + break; + + default: + break; + } + + if (rc) + printf("Op %d: An error occurred: 0x%x\n", cfg->op, rc); + + return rc; +} + + +void +tcpa_menu(void) +{ + if (!CONFIG_TCGBIOS) + return; + + int show_menu = 1; + int scan_code; + u32 rc; + tpm_bios_cfg_t cfg = { + .op = 0, + }; + + while (get_keystroke(0) >= 0) + ; + wait_threads(); + + for (;;) { + if (show_menu) { + 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" + "Escape for previous menu.\n"); + show_menu = 0; + show_tpm_state(); + } + + cfg.op = 0; + + scan_code = get_keystroke(1000); + + switch (scan_code) { + case 1: + // ESC + return; + case 2 ... 8: + cfg.op = scan_code - 1; + break; + default: + continue; + } + + rc = tcpa_process_cfg(&cfg, 1); + + if (rc) + printf("An error occurred: 0x%x\n", rc); + + show_menu = 1; + } +} + + Index: seabios/src/boot.c =================================================================== --- seabios.orig/src/boot.c +++ seabios/src/boot.c @@ -364,11 +364,19 @@ interactive_bootmenu(void) while (get_keystroke(0) >= 0) ;
- printf("Press F12 for boot menu.\n\n"); +again: + printf("Press F12 for boot menu.\n"); + if (has_working_tpm()) + printf("Press F11 to TPM menu.\n"); + printf("\n");
enable_bootsplash(); int scan_code = get_keystroke(CONFIG_BOOTMENU_WAIT); disable_bootsplash(); + if (has_working_tpm() && scan_code == 0x85) { + tcpa_menu(); + goto again; + } if (scan_code != 0x86) /* not F12 */ return; Index: seabios/src/tcgbios.h =================================================================== --- seabios.orig/src/tcgbios.h +++ seabios/src/tcgbios.h @@ -376,6 +376,7 @@ u32 tcpa_ipl(enum ipltype bootcd, const u32 tcpa_start_option_rom_scan(void); u32 tcpa_option_rom(const void *addr, u32 len); u32 tcpa_smbios_measure(void); +void tcpa_menu(void);
#endif /* TCGBIOS_H */
This patch adds an optional test suite (CONFIG_TIS_TEST) for the TIS interface to SeaBIOS. If compiled into the BIOS, it can be invoked through the TPM-specific menu item 8.
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 8. TIS test
I would like to see this code become part of the SeaBIOS code base but I understand that a test suite in a BIOS is not the right place... Nevertheless, for testing the TIS emulation in Qemu, I am posting it here. The test suite fills up the available BIOS space from 92.6% at the previous patch to 98.4%.
v4: - adapted tis_test.c to be under LGPLv3
v3: - use if (CONFIG_TIS_TEST) ... where possible, otherwise use #if CONFIG_...
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- Makefile | 2 src/Kconfig | 7 src/tcgbios.c | 33 +- src/tis_test.c | 834 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tis_test.h | 52 +++ 5 files changed, 919 insertions(+), 9 deletions(-)
Index: seabios/Makefile =================================================================== --- seabios.orig/Makefile +++ seabios/Makefile @@ -20,7 +20,7 @@ SRC16=$(SRCBOTH) system.c disk.c font.c SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \ acpi.c smm.c mptable.c smbios.c pciinit.c optionroms.c mtrr.c \ lzmadecode.c bootsplash.c jpeg.c usb-hub.c paravirt.c dev-i440fx.c \ - pci_region.c tcgbios.c tpm_drivers.c sha1.c + pci_region.c tcgbios.c tpm_drivers.c sha1.c tis_test.c SRC32SEG=util.c output.c pci.c pcibios.c apm.c stacks.c
cc-option = $(shell if test -z "`$(1) $(2) -S -o /dev/null -xc \ Index: seabios/src/tcgbios.c =================================================================== --- seabios.orig/src/tcgbios.c +++ seabios/src/tcgbios.c @@ -18,6 +18,9 @@ #include "sha1.h" // sha1 #include "smbios.h" // get_smbios_entry_point
+#if CONFIG_TIS_TEST +#include "tis_test.h" +#endif
static const u8 Startup_ST_CLEAR[2] = { 0x00, TPM_ST_CLEAR }; static const u8 Startup_ST_STATE[2] = { 0x00, TPM_ST_STATE }; @@ -697,6 +700,9 @@ pass_through_to_tpm(struct pttti *pttti, iovec[1].data = NULL; iovec[1].length = 0;
+ if (CONFIG_TIS_TEST) + locty = pttti->reserved; + rc = transmit(locty, iovec, pttto->tpmopout, &resbuflen); if (rc) goto err_exit; @@ -1731,26 +1737,29 @@ err_exit: }
-static void +static int show_tpm_state(void) { + int state = 0; struct tpm_permanent_flags pf; u8 has_owner;
if (read_permanent_flags((char *)&pf, sizeof(pf)) || read_has_owner(&has_owner)) - return; + return ~0;
printf("TPM is ");
- if (pf.flags[PERM_FLAG_IDX_DISABLE]) + if (pf.flags[PERM_FLAG_IDX_DISABLE]) { printf("disabled"); - else + state |= 1 << PERM_FLAG_IDX_DISABLE; + } else printf("enabled");
- if (pf.flags[PERM_FLAG_IDX_DEACTIVATED]) + if (pf.flags[PERM_FLAG_IDX_DEACTIVATED]) { printf(", deactivated"); - else + state |= 1 << PERM_FLAG_IDX_DEACTIVATED; + } else printf(", active");
if (has_owner) @@ -1763,6 +1772,7 @@ show_tpm_state(void) printf("and an owner cannot be installed.\n"); }
+ return state; }
@@ -1821,7 +1831,7 @@ tcpa_menu(void) return;
int show_menu = 1; - int scan_code; + int scan_code, state; u32 rc; tpm_bios_cfg_t cfg = { .op = 0, @@ -1840,9 +1850,12 @@ tcpa_menu(void) "5. Clear ownership\n" "6. Allow installation of owner\n" "7. Prevent installation of owner\n" +#if CONFIG_TIS_TEST + "8. TIS test\n" +#endif "Escape for previous menu.\n"); show_menu = 0; - show_tpm_state(); + state = show_tpm_state(); }
cfg.op = 0; @@ -1856,6 +1869,10 @@ tcpa_menu(void) case 2 ... 8: cfg.op = scan_code - 1; break; +#if CONFIG_TIS_TEST + case 9: + tis_test(state); +#endif default: continue; } Index: seabios/src/tis_test.c =================================================================== --- /dev/null +++ seabios/src/tis_test.c @@ -0,0 +1,834 @@ +// +// TIS interface tests +// +// Copyright (C) 2006-2011 IBM Corporation +// Copyright (C) 2006-2011 Stefan Berger stefanb@us.ibm.com +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + + +#include "config.h" + +#include "types.h" +#include "util.h" /* read{b,l}, write{b,l} */ +#include "tcgbios.h" +#include "tpm_drivers.h" +#include "tis_test.h" + + +static int +tis_check_reg(u32 reg, u8 locty, u32 mask, u32 exp, u32 timeout) +{ + u32 val; + u32 now = 0; + + while (now <= timeout) { + val = readl(TIS_REG(locty, reg)); + + if ((val & mask) == exp) + return 0; + + msleep(10); + + now += 10; + } + + return 1; +} + +static int +tis_check_status(u8 locty, u32 mask, u32 exp, u32 timeout) +{ + return tis_check_reg(TIS_REG_STS, locty, mask, exp, timeout); +} + +static int +tis_check_access(u8 locty, u32 mask, u32 exp, u32 timeout) +{ + return tis_check_reg(TIS_REG_ACCESS, locty, mask, exp, timeout); +} + +static void +wait_for_keystroke(void) +{ + printf("Press escape to continue.\n"); + while (1) { + switch (get_keystroke(1000)) { + case 1: + return; + } + } +} + +static void +check_access(u8 locty, u8 mask, u8 exp, + const char *succ_msg, + const char *fail_msg) +{ + u8 acc = readb(TIS_REG(locty, TIS_REG_ACCESS)); + u8 res = acc & mask; + + if (res == exp) + printf("%s -- access reg : %02x\n", succ_msg, acc); + else { + printf("ERROR: %s\n" + "value = %02x, mask = %02x, exp = %02x, actual = %02x, " + "bad = %02x\n", + fail_msg, acc, mask, exp, res, res ^ exp ); + wait_for_keystroke(); + } +} + +static void +tis_activate_locality(u8 new_locty) +{ + int locty; + + /* release locality in use top-downwards */ + for (locty = 4; locty >= 0; locty--) + writeb(TIS_REG(locty, TIS_REG_ACCESS), + TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_BEEN_SEIZED); + + printf("Requesting access to locality %d\n", new_locty); + + writeb(TIS_REG(new_locty, TIS_REG_ACCESS), + TIS_ACCESS_REQUEST_USE); + + check_access(new_locty, + TIS_ACCESS_ACTIVE_LOCALITY, + TIS_ACCESS_ACTIVE_LOCALITY, + "Switch to the locality", + "Could not get the locality"); +} + +/* + * Test getting access to localities using the + * TIS_ACCESS_ACTIVATE_LOCALITY flag + * The test ends with locality 0 being active. + */ +static void +test_active_locality_flag(void) +{ + printf("Requesting access to locality 0\n"); + + writeb(TIS_REG(0, TIS_REG_ACCESS), + TIS_ACCESS_REQUEST_USE|TIS_ACCESS_BEEN_SEIZED); + + check_access(0, + TIS_ACCESS_TPM_ESTABLISHMENT, + TIS_ACCESS_TPM_ESTABLISHMENT, + "Valid TPM Establishment flag in locality 0", + "Invalid TPM Establishment flag in locality 0"); + + check_access(0, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY | TIS_ACCESS_BEEN_SEIZED, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY, + "Got locality 0", + "Could not get locality 0"); + + printf("Giving up locality 0\n"); + writeb(TIS_REG(0, TIS_REG_ACCESS), TIS_ACCESS_ACTIVE_LOCALITY); + + check_access(0, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY, + 0x80, + "Successfully released locality 0", + "Still have locality 0"); + + printf("Requesting access to locality 1\n"); + + writeb(TIS_REG(1, TIS_REG_ACCESS), TIS_ACCESS_REQUEST_USE); + + check_access(1, + TIS_ACCESS_ACTIVE_LOCALITY, + TIS_ACCESS_ACTIVE_LOCALITY, + "Got locality 1", + "Could not get locality 1"); + + printf("Requesting access to locality 2; seizing locality.\n"); + + writeb(TIS_REG(2, TIS_REG_ACCESS), TIS_ACCESS_SEIZE); + + check_access(1, + 0x80 | TIS_ACCESS_BEEN_SEIZED | TIS_ACCESS_ACTIVE_LOCALITY, + 0x80 | TIS_ACCESS_BEEN_SEIZED, + "Access to locality 1 has been seized", + "Access to locality 1 has not been properly seized"); + + check_access(2, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY, + "Got locality 2", + "Could not get locality 2"); + + printf("Setting request to use by locality 1\n"); + + writeb(TIS_REG(1, TIS_REG_ACCESS), TIS_ACCESS_REQUEST_USE); + + check_access(2, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY| TIS_ACCESS_PENDING_REQUEST, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY| TIS_ACCESS_PENDING_REQUEST, + "Locality 2 sees pending request by locality 1", + "Locality 2 does not see pending request by locality 1"); + + check_access(1, + 0x80 | TIS_ACCESS_REQUEST_USE | TIS_ACCESS_ACTIVE_LOCALITY, + 0x80 | TIS_ACCESS_REQUEST_USE, + "Locality 1 has request pending", + "Locality 1 does not have request pending"); + + printf("Setting request to use by locality 0\n"); + + writeb(TIS_REG(0, TIS_REG_ACCESS), TIS_ACCESS_REQUEST_USE); + + check_access(0, + 0x80 | TIS_ACCESS_REQUEST_USE | TIS_ACCESS_ACTIVE_LOCALITY, + 0x80 | TIS_ACCESS_REQUEST_USE, + "Locality 0 has request pending", + "Locality 0 does not have request pending"); + + check_access(2, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + "Locality 2 sees pending request by locality 1", + "Locality 2 does not see pending request by locality 1"); + + printf("Setting request to use by locality 3\n"); + + writeb(TIS_REG(3, TIS_REG_ACCESS), TIS_ACCESS_REQUEST_USE); + + check_access(3, + 0x80 | TIS_ACCESS_REQUEST_USE | TIS_ACCESS_ACTIVE_LOCALITY, + 0x80 | TIS_ACCESS_REQUEST_USE, + "Locality 3 has request pending", + "Locality 3 does not have request pending"); + + check_access(2, + 0x80|TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + 0x80|TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + "Locality 2 sees pending request by localities 0, 1 & 3", + "Locality 2 does not see pending request by localities " + "0, 1 & 3"); + + printf("Releasing use by locality 2\n"); + + writeb(TIS_REG(2, TIS_REG_ACCESS), TIS_ACCESS_ACTIVE_LOCALITY); + + check_access(2, + 0x80 | TIS_ACCESS_PENDING_REQUEST|TIS_ACCESS_ACTIVE_LOCALITY, + 0x80 | TIS_ACCESS_PENDING_REQUEST, + "Locality 2 successfully relased usage", + "Locality 2 did not completely release usage"); + + check_access(3, + 0x80|TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + 0x80|TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + "Locality 3 is active and sees pending request by localities " + "0 & 1", + "Locality 3 does not see proper flags"); + + check_access(0, + 0x80|TIS_ACCESS_REQUEST_USE|TIS_ACCESS_PENDING_REQUEST| + TIS_ACCESS_ACTIVE_LOCALITY, + 0x80|TIS_ACCESS_REQUEST_USE|TIS_ACCESS_PENDING_REQUEST, + "Locality 0 has request pending and sees other pending request", + "Locality 0 does not have request pending"); + + printf("Locality 1 drops request\n"); + + writeb(TIS_REG(1, TIS_REG_ACCESS), TIS_ACCESS_ACTIVE_LOCALITY); + + printf("Releasing use by locality 3\n"); + + writeb(TIS_REG(3, TIS_REG_ACCESS), TIS_ACCESS_ACTIVE_LOCALITY); + + check_access(3, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + 0x80, + "Locality 3 successfully relased usage", + "Locality 3 did not completely release usage"); + + check_access(1, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + 0x80, + "Locality 1 successfully relased usage", + "Locality 1 did not completely release usage"); + + check_access(0, + 0x80|TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + 0x80|TIS_ACCESS_ACTIVE_LOCALITY, + "Locality 0 is active", + "Locality 0 is not avtive"); +} + +/* + * Test the seize flag. It is assumed that locality 0 is active. + * The test ends with locality 2 being active + */ +static void +test_seize_locality_flag(void) +{ + printf("Requesting use by locality 1 using the SEIZE bit\n"); + + writeb(TIS_REG(1, TIS_REG_ACCESS), TIS_ACCESS_SEIZE); + + check_access(0, + 0x80 | TIS_ACCESS_BEEN_SEIZED|TIS_ACCESS_ACTIVE_LOCALITY, + 0x80 | TIS_ACCESS_BEEN_SEIZED, + "Access to locality 0 has been seized", + "Access to locality 0 has not been properly seized"); + + check_access(1, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY, + "Locality 1 successfully seized usage", + "Locality 1 did not seize usage"); + + printf("Requesting use by locality 2 using the SEIZE bit\n"); + + writeb(TIS_REG(2, TIS_REG_ACCESS), TIS_ACCESS_SEIZE); + + check_access(1, + 0x80 | TIS_ACCESS_BEEN_SEIZED | TIS_ACCESS_ACTIVE_LOCALITY, + 0x80 | TIS_ACCESS_BEEN_SEIZED, + "Access to locality 1 has been seized", + "Access to locality 1 has not been properly seized"); + + check_access(2, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + 0x80 | TIS_ACCESS_ACTIVE_LOCALITY, + "Locality 2 successfully seized usage", + "Locality 1 did not seize usage"); +} + +#define ENABLED(L4,L3,L2,L1,L0) \ + ((L4) << 4 | (L3) << 3 | (L2) << 2 | (L1) << 1 | (L0)) + +static const u8 pcr_resets[] = { + ENABLED(1,1,1,1,1), /* PCR 16 */ + ENABLED(1,0,0,0,0), + ENABLED(1,0,0,0,0), + ENABLED(1,0,0,0,0), + ENABLED(1,0,1,0,0), + ENABLED(0,0,1,0,0), + ENABLED(0,0,1,0,0), + ENABLED(1,1,1,1,1), +}; + +static const u8 pcr_extends[] = { + ENABLED(1,1,1,1,1), /* PCR 16 */ + ENABLED(1,1,1,0,0), + ENABLED(1,1,1,0,0), + ENABLED(0,1,1,0,0), + ENABLED(0,1,1,1,0), + ENABLED(0,0,1,0,0), + ENABLED(0,0,1,0,0), + ENABLED(1,1,1,1,1), +}; + + +static u32 +do_pcr_read(u32 pcrindex, u8 *result, u8 locty) +{ + u32 rc; + + struct pttto_pcrread pttto; + struct pttti_pcrread pttti = { + .pttti = { + .reserved = locty, + .ipblength = sizeof(struct pttti_pcrread), + .opblength = sizeof(struct pttto_pcrread), + }, + .req = { + .tag = htons(0xc1), + .totlen = htonl(sizeof(pttti.req)), + .ordinal = htonl(TPM_ORD_PcrRead), + .pcrindex = htonl(pcrindex), + }, + }; + struct bregs regs; + regs.es = FLATPTR_TO_SEG (&pttti); + regs.edi = FLATPTR_TO_OFFSET(&pttti); + regs.ds = FLATPTR_TO_SEG (&pttto); + regs.esi = FLATPTR_TO_OFFSET(&pttto); + regs.eax = TCG_PassThroughToTPM; + + tcpa_interrupt_handler32(®s); + + rc = regs.eax; + + if (rc == 0) { + if ((pttto.pttto.opblength < TPM_RSP_HEADER_SIZE) || + ntohs(pttto.rsp.tag) != 0xc4) { + rc = TCG_FATAL_COM_ERROR; + } + } + + if (rc == 0) + memcpy(result, pttto.rsp.digest, sizeof(pttto.rsp.digest)); + + return rc; +} + + +static u32 +do_pcr_extend(u8 *hash, u32 pcrindex, u8 locty) +{ + u32 rc; + + struct pttto_extend pttto; + struct pttti_extend pttti = { + .pttti = { + .reserved = locty, + .ipblength = sizeof(struct pttti_extend), + .opblength = sizeof(struct pttto_extend), + }, + .req = { + .tag = htons(0xc1), + .totlen = htonl(sizeof(pttti.req)), + .ordinal = htonl(TPM_ORD_Extend), + .pcrindex = htonl(pcrindex), + }, + }; + struct bregs regs; + regs.es = FLATPTR_TO_SEG(&pttti); + regs.edi = FLATPTR_TO_OFFSET(&pttti); + regs.ds = FLATPTR_TO_SEG(&pttto); + regs.esi = FLATPTR_TO_OFFSET(&pttto); + regs.eax = TCG_PassThroughToTPM; + + memcpy(pttti.req.digest, hash, sizeof(pttti.req.digest)); + + tcpa_interrupt_handler32(®s); + + rc = regs.eax; + + if (rc == 0) { + if ((pttto.pttto.opblength < TPM_RSP_HEADER_SIZE) || + ntohs(pttto.rsp.tag) != 0xc4) { + rc = TCG_FATAL_COM_ERROR; + } + } + + return rc; +} + + +static u32 +do_pcr_reset(u32 pcrindex, u8 locty) +{ + u32 rc; + + struct pttto_pcrreset pttto; + struct pttti_pcrreset pttti = { + .pttti = { + .reserved = locty, + .ipblength = sizeof(struct pttti_pcrreset), + .opblength = sizeof(struct pttto_pcrreset), + }, + .req = { + .tag = htons(0xc1), + .totlen = htonl(sizeof(pttti.req)), + .ordinal = htonl(TPM_ORD_PCR_Reset), + .sizeOfSelect = htons(3), + }, + }; + struct bregs regs; + regs.es = FLATPTR_TO_SEG(&pttti); + regs.edi = FLATPTR_TO_OFFSET(&pttti); + regs.ds = FLATPTR_TO_SEG(&pttto); + regs.esi = FLATPTR_TO_OFFSET(&pttto); + regs.eax = TCG_PassThroughToTPM; + + pttti.req.pcrSelect[pcrindex >> 3] = 1 << (pcrindex & 0x7); + + tcpa_interrupt_handler32(®s); + + rc = regs.eax; + + if (rc == 0) { + if ((pttto.pttto.opblength < TPM_RSP_HEADER_SIZE) || + ntohs(pttto.rsp.tag) != 0xc4) { + rc = TCG_FATAL_COM_ERROR; + } + } + + return rc; +} + + +static void +test_pcr_test(void) +{ + u32 pcr_num, res; + u8 locty; + u8 value_a[20], value_b[20], value_c[20]; + u8 reset_pcr_0s[20] = { /* all PCRs; 17-22 if TOSPresent = true */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + u8 reset_pcr_1s[20] = { /* 17-22 if TOSPresent = false */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + }; + u8 value_ext[20] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; + int extended; + int tos_present = !readb(TIS_REG(0, TIS_REG_ACCESS)) & + TIS_ACCESS_TPM_ESTABLISHMENT; + + printf("Testing PCR Extend and Reset\n"); + + for (pcr_num = 16; pcr_num <= 23; pcr_num++) { + + printf("PCR %d: ",pcr_num); + + for (locty = 0; locty <= 4; locty++) { + if ((res = do_pcr_read(pcr_num, value_a, locty))) { + printf("!F0[%d] = 0x%08x", locty, res); + continue; + } + if ((res = do_pcr_extend(value_ext, pcr_num, locty))) { + printf("!F1[%d] = 0x%08x", locty, res); + continue; + } + if ((res = do_pcr_read(pcr_num, value_b, locty))) { + printf("!F2[%d] = 0x%08x", locty, res); + continue; + } + /* did the PCR change? */ + if (memcmp(value_a, value_b, sizeof(value_a)) != 0) { + /* extend worked */ + extended = 1; + if ((pcr_extends[pcr_num-16] & (1 << locty)) == 0) + printf("!F3[%d] ", locty); + else + printf(" P1[%d] ", locty); + } else { + extended = 0; + /* extend did not work */ + if (pcr_extends[pcr_num-16] & (1 << locty)) + printf("!F4[%d] ", locty); + else + printf(" P2[%d] ", locty); + } + + if (!extended) { + /* Need to extend using locality 2; this always works */ + if ((res = do_pcr_extend(value_ext, pcr_num, 2))) { + printf("!F5[%d] = 0x%08x", locty, res); + continue; + } + } + + if ((res = do_pcr_reset(pcr_num, locty))) { + printf("!F6[%d] = 0x%08x", locty, res); + continue; + } + if (do_pcr_read(pcr_num, value_c, locty)) { + printf("!F7[%d] = 0x%08x", locty, res); + continue; + } + /* did reset work? */ + if ((( pcr_num <= 16 || + pcr_num == 23 || + (pcr_num >= 17 && pcr_num <= 22 && tos_present) + ) && + memcmp(value_c, reset_pcr_0s, sizeof(value_c)) == 0 + ) || + ( pcr_num >= 17 && pcr_num <= 22 && !tos_present && + memcmp(value_c, reset_pcr_1s, sizeof(value_c)) == 0 + ) + ) { + /* reset worked */ + if ((pcr_resets[pcr_num-16] & (1 << locty)) == 0) + printf("!F8[%d] ", locty); + else + printf(" P3[%d] ", locty); + } else { + /* reset did not work */ + if ((pcr_resets[pcr_num-16] & (1 << locty))) + printf("!F9[%d] ", locty); + else + printf(" P4[%d] ", locty); + } + } + printf("\n"); + } +} + + +/* send a command to the TPM without waiting for the response; + the caller must have activate the given locality */ +static int +send_command(u8 locty, const unsigned char *command, u32 cmd_length) +{ + u32 c; + u32 burst; + + writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY); + + if (tis_check_status(locty, + TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY, 1000)) { + printf("Error: Could not bring the TPM " + "into ready state in locality %d\n", locty); + wait_for_keystroke(); + return 1; + } + + burst = readl(TIS_REG(locty, TIS_REG_STS)) >> 8; + + for (c = 0; c < cmd_length && burst > 0; c++) { + writeb(TIS_REG(locty, TIS_REG_DATA_FIFO), command[c]); + burst--; + } + + if (c != cmd_length) { + printf("Error: Could not write data into locality %d FIFO\n", locty); + wait_for_keystroke(); + return 1; + } + + writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_TPM_GO); + + return 0; +} + + +unsigned char pcr_read_cmd[] = { + 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x15, 0x00, 0x00, 0x00, 0x0a +}; + +static void +test_cmd_aborts(void) +{ + printf("Testing command aborts\n"); + + /* abort using COMMAND_READY flag */ + + tis_activate_locality(0); + + if (send_command(0, pcr_read_cmd, sizeof(pcr_read_cmd))) { + printf("Error: Sending command did not work.\n"); + wait_for_keystroke(); + } else { + writeb(TIS_REG(0, TIS_REG_STS), TIS_STS_COMMAND_READY); + + if (tis_check_status(0, + TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY, + 1000)) { + printf("After abort, locality %d did not become ready in time.\n", + 0); + } + } + + /* abort using the Seize flag by a higher locality */ + + writeb(TIS_REG(0, TIS_REG_ACCESS), TIS_ACCESS_BEEN_SEIZED); + + if (send_command(0, pcr_read_cmd, sizeof(pcr_read_cmd))) { + printf("Error: Sending command did not work.\n"); + wait_for_keystroke(); + } else { + writeb(TIS_REG(1, TIS_REG_ACCESS), TIS_ACCESS_SEIZE); + + if (tis_check_access(1, + TIS_ACCESS_ACTIVE_LOCALITY, + TIS_ACCESS_ACTIVE_LOCALITY, + 1000)) { + printf("Error: Aborting of locality %d command by locality %d " + "using the SEIZE flag did not work.\n", + 0, 1); + wait_for_keystroke(); + } else { + check_access(0, + TIS_ACCESS_BEEN_SEIZED|TIS_ACCESS_ACTIVE_LOCALITY, + TIS_ACCESS_BEEN_SEIZED, + "Access to locality 0 has been seized", + "Access to locality 0 has not been properly seized"); + + check_access(1, + TIS_ACCESS_ACTIVE_LOCALITY, + TIS_ACCESS_ACTIVE_LOCALITY, + "Got locality 1", + "Could not get locality 1"); + } + } + + /* abort using ACTIVE_LOCALITY flag */ + tis_activate_locality(1); + + /* put in request by locality 0 */ + writeb(TIS_REG(0, TIS_REG_ACCESS), TIS_ACCESS_REQUEST_USE); + + check_access(1, + TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + TIS_ACCESS_ACTIVE_LOCALITY|TIS_ACCESS_PENDING_REQUEST, + "Locality 1 sees pending request", + "Locality 1 does not see pending request"); + + if (send_command(1, pcr_read_cmd, sizeof(pcr_read_cmd))) { + printf("Error: Sending command did not work.\n"); + wait_for_keystroke(); + } else { + writeb(TIS_REG(1, TIS_REG_ACCESS), TIS_ACCESS_ACTIVE_LOCALITY); + + if (tis_check_access(0, + TIS_ACCESS_ACTIVE_LOCALITY, + TIS_ACCESS_ACTIVE_LOCALITY, + 1000)) { + printf("Error: Aborting of locality %d command " + "using the ACTIVE_LOCALITY flag did not work.\n", + 1); + printf("access[0] : %02x\n", readb(TIS_REG(0, TIS_REG_ACCESS))); + printf("access[1] : %02x\n", readb(TIS_REG(1, TIS_REG_ACCESS))); + wait_for_keystroke(); + } else { + check_access(1, + TIS_ACCESS_ACTIVE_LOCALITY, + 0, + "Access to locality 1 relinquished", + "Access to locality 1 not relinquished"); + + check_access(0, + TIS_ACCESS_ACTIVE_LOCALITY, + TIS_ACCESS_ACTIVE_LOCALITY, + "Got locality 0", + "Could not get locality 0"); + } + } +} + + +static int +test_various(u8 locty) +{ + int i; + + tis_activate_locality(locty); + + printf("Putting locality %d in ready state.\n", locty); + + writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY); + + if (tis_check_status(locty, + TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY, 1000)) { + printf("Error: Could not bring the TPM " + "into ready state in locality %d\n",locty); + wait_for_keystroke(); + return 1; + } + + printf("Reading data from FIFO. Expecting 0xff.\n"); + + if (readb(TIS_REG(locty, TIS_REG_DATA_FIFO)) != 0xff) { + printf("Error: Expected to 0xff from FIFO\n"); + wait_for_keystroke(); + } + + printf("Writing a byte into the FIFO.\n"); + + writeb(TIS_REG(locty, TIS_REG_DATA_FIFO), pcr_read_cmd[0]); + + if (tis_check_status(locty, + TIS_STS_EXPECT, TIS_STS_EXPECT, 0)) { + printf("Error: TIS is not expecting data in locality %d\n",locty); + wait_for_keystroke(); + return 1; + } + + printf("Sending single byte to be processed.\n"); + + /* tpm will process the single byte */ + writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_TPM_GO); + + if (tis_check_status(locty, + TIS_STS_EXPECT, 0, 0)) { + printf("Error: TIS is still expecting data in locality %d [2nd]\n", + locty); + wait_for_keystroke(); + return 1; + } + + printf("Checking for result.\n"); + + if (tis_check_status(locty, + TIS_STS_DATA_AVAILABLE, TIS_STS_DATA_AVAILABLE, + 1000)) { + printf("Error: No data available in locality %d [2nd]\n", + locty); + wait_for_keystroke(); + return 1; + } + + printf("Putting locality %d in ready state.\n", locty); + + writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY); + + printf("Writing TPM_PcrRead command into FIFO.\n"); + + for (i = 0; i < sizeof(pcr_read_cmd); i++) { + if (i > 0 && tis_check_status(0, TIS_STS_EXPECT, TIS_STS_EXPECT, 0)) { + printf("Error: TIS is not expecting data anymore [%d/%d].\n", + i,sizeof(pcr_read_cmd)-1); + readb(TIS_REG(locty, 0xf90)); + wait_for_keystroke(); + } + writeb(TIS_REG(locty, TIS_REG_DATA_FIFO), pcr_read_cmd[i]); + } + + printf("Checking whether the TPM is still expecting data. " + "It should not.\n"); + + if (tis_check_status(locty, + TIS_STS_EXPECT, 0, 0)) { + printf("Error: TIS is still expecting data in locality %d\n", + locty); + wait_for_keystroke(); + return 1; + } + + printf("Sending command to put locality %d into ready state.\n", locty); + + writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY); + + printf("Checking that locality %d is in ready state.\n", locty); + + if (tis_check_status(locty, + TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY, 1000)) { + printf("Error: Could not bring the TPM " + "into ready state in locality %d [2nd]\n", locty); + wait_for_keystroke(); + return 1; + } + + return 0; +} + + +void +tis_test(int tpm_state) +{ + if (!CONFIG_TIS_TEST) + return; + + test_active_locality_flag(); + + /* locality 0 is active */ + + test_seize_locality_flag(); + + /* locality 2 is active */ + + if (tpm_state == 0) + test_pcr_test(); + + test_cmd_aborts(); + + test_various(0); + + tis_activate_locality(0); + + printf("END OF TEST.\n"); + wait_for_keystroke(); +} Index: seabios/src/tis_test.h =================================================================== --- /dev/null +++ seabios/src/tis_test.h @@ -0,0 +1,52 @@ +#ifndef TIS_TEST +#define TIS_TEST + +#include "tcgbios.h" + +#define TPM_ORD_PCR_Reset 0x000000c8 +#define TPM_ORD_PcrRead 0x00000015 + +void tis_test(int tpm_state); + +struct tpm_req_pcrread { + TPM_REQ_HEADER + u32 pcrindex; +} PACKED; + +struct tpm_rsp_pcrread { + TPM_RSP_HEADER + u8 digest[SHA1_BUFSIZE]; +} PACKED; + +struct pttti_pcrread { + struct pttti pttti; + struct tpm_req_pcrread req; +} PACKED; + +struct pttto_pcrread { + struct pttto pttto; + struct tpm_rsp_pcrread rsp; +} PACKED; + + +struct tpm_req_pcrreset { + TPM_REQ_HEADER + u16 sizeOfSelect; + u8 pcrSelect[3]; +} PACKED; + +struct tpm_rsp_pcrreset { + TPM_RSP_HEADER +} PACKED; + +struct pttti_pcrreset { + struct pttti pttti; + struct tpm_req_pcrreset req; +} PACKED; + +struct pttto_pcrreset { + struct pttto pttto; + struct tpm_rsp_pcrreset rsp; +} PACKED; + +#endif /* TIS_TEST */ Index: seabios/src/Kconfig =================================================================== --- seabios.orig/src/Kconfig +++ seabios/src/Kconfig @@ -330,6 +330,13 @@ menu "BIOS interfaces" Either you may use the TPM for SHA1 calculations or use the internal sha1 algorithm to do it (faster).
+ config TIS_TEST + depends on TCGBIOS + bool "TPM TIS test" + default n + help + Test cases for the TPM TIS interface + endmenu
menu "BIOS Tables"