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
v5: - adapted patches to checkout of 76b5e71 (June 21) - bugfixes (see individual patches) - added patch to support the transfer of Qemu-provided measurements via firmware interface
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.
v5: - introducing a configurable threashold as part of the driver interface structure below which the TPM is used for calculating the sha1
v2: - adapted tpm_drivers.c to be under LGPLv3
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- src/tpm_drivers.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tpm_drivers.h | 58 +++++++++++++++ 2 files changed, 257 insertions(+)
Index: seabios/src/tpm_drivers.c =================================================================== --- /dev/null +++ seabios/src/tpm_drivers.c @@ -0,0 +1,199 @@ +// 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, + .sha1threshold = 100 * 1024, + }, +}; Index: seabios/src/tpm_drivers.h =================================================================== --- /dev/null +++ seabios/src/tpm_drivers.h @@ -0,0 +1,58 @@ +#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); + /* the TPM will be used for buffers of sizes below the sha1threshold + for calculating the hash */ + u32 sha1threshold; +}; + +#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 \ - pci_region.c biostables.c xen.c + pci_region.c biostables.c xen.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 @@ -12,6 +12,8 @@ #include "pci_ids.h" // PCI_VENDOR_ID_INTEL #include "pci_regs.h" // PCI_INTERRUPT_LINE #include "paravirt.h" +#include "acpi-tpm-ssdt.hex" +#include "tcgbios.h" // has_working_tpm
/****************************************************/ /* ACPI tables init */ @@ -599,6 +601,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 @@ -655,6 +690,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 @@ -19,7 +19,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 */
Hi,
On 07/06/2011 04:32 PM, Stefan Berger wrote:
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)
}
- }
+}
For some reason the Linux tpm_tis driver does not recognize the device (without using force=1) for me on Linux-2.6.33. However, the driver modules are being loaded automatically, but without registering a usable tpm device.
I'm no expert regarding ACPI but I noticed that the default SSDT provided by SeaBIOS uses the Scope(_SB) header (in build_ssdt()).
Using this Scope(_SB) { ... } around the device entry in your SSDT, I've been able to successfully use the TPM device detection.
Could you please include this header in your SSDT?
Regards, Andreas
On 08/04/2011 12:14 PM, Andreas Niederl wrote:
Hi,
On 07/06/2011 04:32 PM, Stefan Berger wrote:
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)
}
- }
+}
For some reason the Linux tpm_tis driver does not recognize the device (without using force=1) for me on Linux-2.6.33. However, the driver modules are being loaded automatically, but without registering a usable tpm device.
Hm, I don't need it but I am running later versions of Linux, i.e. 2.6.35.13-92.fc14
I'm no expert regarding ACPI but I noticed that the default SSDT provided by SeaBIOS uses the Scope(_SB) header (in build_ssdt()).
Using this Scope(_SB) { ... } around the device entry in your SSDT, I've been able to successfully use the TPM device detection.
Could you please include this header in your SSDT?
I'll add it.
Thanks for testing.
Stefan
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 @@ -719,11 +719,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.
v5: - adding the lock flag to the 'not present' Physcial_presence_NOT_PRESENT structure
v4: - return TCG_GENERAL_ERROR if ! has_working_tpm()
v3: - upon S3 resume call timer_setup()
v2: - replace mssleep() with calls to msleep() - Moving Kconfig patch to this file - converting code to call dprintf(DEBUG_tcg, ...) - use the get_rsdp call to get hold of the RSDP - use util.c:checksum() - Adapting tcgbios.c to be under LGPLv3 - using if (!CONFIG_TCGBIOS) everywhere
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 | 396 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 312 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 726 insertions(+)
Index: seabios/src/post.c =================================================================== --- seabios.orig/src/post.c +++ seabios/src/post.c @@ -26,6 +26,7 @@ #include "xen.h" // xen_probe_hvm_info #include "ps2port.h" // ps2port_setup #include "virtio-blk.h" // virtio_blk_setup +#include "tcgbios.h" // tcpa_*
/**************************************************************** @@ -244,6 +245,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_*
/**************************************************************** @@ -473,6 +474,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 @@ -112,5 +112,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 @@ -341,6 +341,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,47 @@
#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_LOCK[2] = { 0x00, 0x14 }; + +static const u8 CommandFlag_FALSE[1] = { 0x00 }; +static const u8 CommandFlag_TRUE[1] = { 0x01 }; + +static const u8 GetCapability_Permanent_Flags[12] = { + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x08 +}; + +static const u8 GetCapability_OwnerAuth[12] = { + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x11 +}; + + +#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 +99,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 +114,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_LOCK, + sizeof(PhysicalPresence_NOT_PRESENT_LOCK), + 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; +} + + +
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.
v5: - adapted code to use SHA1 threshold of driver - support in Kconfig for SHA1 threshold called CONFIG_TPM_TIS_SHA1THRESHOLD - using CONFIG_TPM_TIS_SHA1THRESHOLD in the tpm driver structure to set the threshold for having the TPM do the SHA1 or do it in software - fixing placement of set_cf in inthandler
v4: - return TCG_GENERAL_ERROR if ! has_working_tpm() - in the int handler return TCG_GENERAL_ERROR in eax if ! has_working_tpm() - in the inthanlder call set_cf() before the switch and then in the default case for functions that are not implemented
v3: - use if (CONFIG_TPM_FOR_SHA1)
v2: - use if (!CONFIG_TCGBIOS) everywhere - put sha1() into its own file - move rol() and bswap64() into util.h - move inthandler invocation from stacks.c into handle_1abb()
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- Makefile | 2 src/Kconfig | 9 src/clock.c | 12 + src/sha1.c | 139 +++++++++++++ src/sha1.h | 8 src/tcgbios.c | 539 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 4 src/tpm_drivers.c | 2 src/util.h | 30 +++ 9 files changed, 743 insertions(+), 2 deletions(-)
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 }; @@ -465,4 +466,542 @@ 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; + + 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 (length < tpm_drivers[tcpa_state.tpm_driver_to_use].sha1threshold) + return tpm_sha1_calc(data, length, hash); + + return sha1(data, length, hash); +} + + +/* + * Extend the ACPI log with the given entry by copying the + * entry data into the log. + * Input + * Pointer to the structure to be copied into the log + * + * Output: + * lower 16 bits of return code contain entry number + * if entry number is '0', then upper 16 bits contain error code. + */ +static u32 +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; + + set_cf(regs, 0); + + if (!has_working_tpm()) { + regs->eax = TCG_GENERAL_ERROR; + return; + } + + switch ((enum irq_ids)regs->al) { + case TCG_StatusCheck: + if (is_tpm_present() == 0) { + /* no TPM available */ + regs->eax = TCG_PC_TPM_NOT_PRESENT; + } else { + regs->eax = 0; + regs->ebx = TCG_MAGIC; + regs->ch = TCG_VERSION_MAJOR; + regs->cl = TCG_VERSION_MINOR; + regs->edx = 0x0; + regs->esi = (u32)get_lasa_base_ptr(NULL); + regs->edi = + (u32)get_lasa_last_ptr(NULL, NULL); + } + break; + + case TCG_HashLogExtendEvent: + regs->eax = + hash_log_extend_event( + (struct hleei_short *)input_buf32(regs), + (struct hleeo *)output_buf32(regs)); + break; + + case TCG_PassThroughToTPM: + regs->eax = + pass_through_to_tpm((struct pttti *)input_buf32(regs), + (struct pttto *)output_buf32(regs)); + break; + + case TCG_ShutdownPreBootInterface: + regs->eax = shutdown_preboot_interface(); + break; + + case TCG_HashLogEvent: + regs->eax = hash_log_event((struct hlei*)input_buf32(regs), + (struct hleo*)output_buf32(regs)); + break; + + case TCG_HashAll: + regs->eax = + hash_all((struct hai*)input_buf32(regs), + (u8 *)output_buf32(regs)); + break; + + case TCG_TSS: + regs->eax = tss((struct ti*)input_buf32(regs), + (struct to*)output_buf32(regs)); + break; + + case TCG_CompactHashLogExtendEvent: + regs->eax = + compact_hash_log_extend_event((u8 *)input_buf32(regs), + regs->esi, + regs->ecx, + regs->edx, + ®s->edx); + break; + + default: + set_cf(regs, 1); + } + + return; +} + +
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 @@ -349,6 +349,15 @@ menu "BIOS interfaces" help Provide TPM support along with TCG BIOS extensions
+ config TPM_TIS_SHA1THRESHOLD + depends on TCGBIOS + int "TPM TIS used for hashing of buffers smaller than size (in kb)" + default 100 + help + Use the TPM via the TIS interface for hashing of buffers + below the given size (in kb), otherwise use the internal + SHA1 algorithm for calculating the SHA1. + endmenu
menu "BIOS Tables" 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 @@ -496,4 +496,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); 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 \ - pci_region.c biostables.c xen.c tcgbios.c tpm_drivers.c + pci_region.c biostables.c xen.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/tpm_drivers.c =================================================================== --- seabios.orig/src/tpm_drivers.c +++ seabios/src/tpm_drivers.c @@ -194,6 +194,6 @@ struct tpm_driver tpm_drivers[TPM_NUM_DR .readresp = tis_readresp, .waitdatavalid = tis_waitdatavalid, .waitrespready = tis_waitrespready, - .sha1threshold = 100 * 1024, + .sha1threshold = CONFIG_TPM_TIS_SHA1THRESHOLD * 1024, }, };
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.
Once booted into Linux, the current measurements produce the following logs which can be found in /sys/kernel/security/tpm0/ascii_bios_measurements. The below log also shows measurements from trusted grub.
1 3fb240d2a04085a4e84f81e4398e070ed5a18163 06 [SMBIOS] 2 cc812353fc277c1fab99e0b721752a1392984566 06 [Option ROM] 2 9dbd87163112e5670378abe4510491259a61f411 05 [Start Option ROM Scan] 2 6f74e357331b8dee11bbad85f27bc66cb873106c 06 [Option ROM] 2 5626eb7ac05c7231e46d7461e7d3839b03ae9fad 06 [Option ROM] 4 c1e25c3f6b0dc78d57296aa2870ca6f782ccf80f 05 [Calling INT 19h] 0 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 1 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 2 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 3 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 4 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 5 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 6 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 7 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 4 8cf2fe6c87d4d0b2998a43da630292e6d85ee8b6 05 [Booting BCV device 80h (HDD)] 4 5dff94459a3e2d13a433ef94afdc306144565bf7 0d [IPL] 5 d1b33afde65ad47502332af957c60f20c84c1edc 0e [IPL Partition Data] 4 487ce764b527ccad17f1d04243d0136fa981e6c4 0d [IPL] 4 91d285e4dead566324c8938a3cc75803f462d9a1 0d [IPL] 4 8ba79ac98bb491524fef29defc724daaf6263d35 0d [IPL] 4 c591c15b82e4ff30e7383a4ff1ef3b41b38521ac 06 [] 4 8cdc27ec545eda33fbba1e8b8dae4da5c7206972 04 [Grub Event Separator] 5 8cdc27ec545eda33fbba1e8b8dae4da5c7206972 04 [Grub Event Separator] 5 e8673b9e14b02dc12d8ccfd0176bca7a3de7fc3c 0e [IPL Partition Data] 5 0163e375a0af7525c5dac1a8e74b277359e40d1d 1105 [] 8 4be30f67c3d48ab7f04d9c0fd07f06d4c68379be 1205 [] 8 54c83965978de9708d026016ecb0e70660e04388 1305 [] 5 2431ed60130faeaf3a045f21963f71cacd46a029 04 [OS Event Separator] 8 2431ed60130faeaf3a045f21963f71cacd46a029 04 [OS Event Separator] 8 f3973cae05d6e2055062119d6e6e1e077b7df876 1005 []
v5: - call code for measuring CDROM boot sector
v4: - return TCG_GENERAL_ERROR if ! has_working_tpm()
v2: - dropping call to tcpa_measure_post - converting tcpa_option_rom and tcpa_ipl functions to get pointers rather than segments passed - introduce public get_smbios_entry_point() function and use it rather than searching for the entry point - use dprintf(DEBUG_tcg, ...)
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- src/boot.c | 11 + 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, 409 insertions(+)
Index: seabios/src/post.c =================================================================== --- seabios.orig/src/post.c +++ seabios/src/post.c @@ -197,6 +197,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);
@@ -248,6 +251,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; }
@@ -396,6 +398,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 @@ -557,6 +557,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; @@ -581,6 +585,11 @@ boot_cdrom(struct drive_s *drive_g) u16 ebda_seg = get_ebda_seg(); u8 bootdrv = GET_EBDA2(ebda_seg, cdemu.emulated_extdrive); u16 bootseg = GET_EBDA2(ebda_seg, cdemu.load_segment); + + tcpa_add_bootdevice(1, bootdrv); + /* specs: 8.2.5.6 */ + tcpa_ipl(IPL_EL_TORITO_1, MAKE_FLATPTR(bootseg, 0), 512); + /* Canonicalize bootseg:bootip */ u16 bootip = (bootseg & 0x0fff) << 4; bootseg &= 0xf000; @@ -644,6 +653,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 }; @@ -39,6 +40,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)
@@ -1004,4 +1007,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.
v5: - fixed an indentation
v2: - use if (!CONFIG_TCGBIOS) ... in all non-static functions - use dprintf(DEBUG_tcg, ...) for printing of debugging info - removing some code used for a future patch
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 @@ -74,6 +74,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 @@ -1364,3 +1368,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 @@ -388,11 +388,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 support for measurements provided by Qemu via the firmware interface. In the case where Qemu was started with the -kernel, -initrd and -append command lines, Qemu hashes the kernel and initrd files as well as the command line parameters and provides the measurements to SeaBIOS via the firmware interface. SeaBIOS then processes the individual measurements and extends the TPM's PCRs as well as writes logs about those measurements.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- src/boot.c | 1 src/paravirt.c | 14 ++++++++++ src/paravirt.h | 4 +++ src/tcgbios.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 16 ++++++++++++ 5 files changed, 109 insertions(+)
Index: seabios/src/tcgbios.c =================================================================== --- seabios.orig/src/tcgbios.c +++ seabios/src/tcgbios.c @@ -17,6 +17,7 @@ #include "acpi.h" // RSDP_SIGNATURE, rsdt_descriptor #include "sha1.h" // sha1 #include "smbios.h" // get_smbios_entry_point +#include "paravirt.h" // QEMU_CFG_KERNEL_*, QEMU_CFG_INITRD_*
static const u8 Startup_ST_CLEAR[2] = { 0x00, TPM_ST_CLEAR }; @@ -1308,6 +1309,79 @@ tcpa_smbios_measure(void) }
+u32 tcpa_process_firmware_cfg(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + void *ptr = (void *)0x100000; + u16 ctr = 0; + + u32 len = qemu_cfg_get_u32(QEMU_CFG_TPM_MEASURE_SIZE); + + if (len) { + qemu_cfg_copy(QEMU_CFG_TPM_MEASURE_DATA, ptr, len); + + TPMMsrHdr *hdr = ptr; + u32 idx = sizeof(*hdr); + + if (hdr->rev < 1) + return TCG_GENERAL_ERROR; + + /* since the first revision (1) we have numTPMMsrEntries */ + while (idx < hdr->totlen && ctr < hdr->numTPMMsrEntries) { + TPMMsrEntry *entry = (void *)hdr + idx; + + idx += entry->len; + + struct hleo hleo; + + u8 _pcpes[offsetof(struct pcpes, event) + 400]; + struct pcpes *pcpes = (struct pcpes *)_pcpes; + + pcpes->pcrindex = entry->pcrindex; + pcpes->eventtype = entry->type; + if (entry->eventdatasize < 400) { + pcpes->eventdatasize = entry->eventdatasize; + memcpy(&pcpes->event, &entry->event, entry->eventdatasize); + } else { + pcpes->eventdatasize = 0; + } + memcpy(pcpes->digest, entry->digest, sizeof(pcpes->digest)); + + struct hlei hlei = { + .ipblength = sizeof(hlei), + .hashdataptr = NULL, + .hashdatalen = 0, + .pcrindex = entry->pcrindex, + .logeventtype= entry->type, + .logdataptr = pcpes, + .logdatalen = pcpes->eventdatasize + + offsetof(struct pcpes, event), + }; + + u32 rc = hash_log_event(&hlei, &hleo); + if (rc) + goto err_exit; + + rc = tpm_extend(entry->digest, entry->pcrindex); + if (rc) + goto err_exit; + + ctr++; + } + } + + return 0; + +err_exit: + return 1; +} + + /* * Add a measurement to the log in support of 8.2.5.3 * Creates two log entries Index: seabios/src/boot.c =================================================================== --- seabios.orig/src/boot.c +++ seabios/src/boot.c @@ -620,6 +620,7 @@ static void boot_rom(u32 vector) { printf("Booting from ROM...\n"); + tcpa_process_firmware_cfg(); struct segoff_s so; so.segoff = vector; call_boot_entry(so, 0); Index: seabios/src/paravirt.c =================================================================== --- seabios.orig/src/paravirt.c +++ seabios/src/paravirt.c @@ -61,6 +61,20 @@ void qemu_cfg_port_probe(void) dprintf(4, "qemu_cfg_present=%d\n", qemu_cfg_present); }
+u32 qemu_cfg_get_u32(int e) +{ + u32 i; + + qemu_cfg_read_entry(&i, e, sizeof(i)); + + return i; +} + +void qemu_cfg_copy(int e, void *buf, int len) +{ + qemu_cfg_read_entry(buf, e, len); +} + void qemu_cfg_get_uuid(u8 *uuid) { if (!qemu_cfg_present) Index: seabios/src/paravirt.h =================================================================== --- seabios.orig/src/paravirt.h +++ seabios/src/paravirt.h @@ -32,6 +32,8 @@ static inline int kvm_para_available(voi #define QEMU_CFG_BOOT_MENU 0x0e #define QEMU_CFG_MAX_CPUS 0x0f #define QEMU_CFG_FILE_DIR 0x19 +#define QEMU_CFG_TPM_MEASURE_SIZE 0x1a +#define QEMU_CFG_TPM_MEASURE_DATA 0x1b #define QEMU_CFG_ARCH_LOCAL 0x8000 #define QEMU_CFG_ACPI_TABLES (QEMU_CFG_ARCH_LOCAL + 0) #define QEMU_CFG_SMBIOS_ENTRIES (QEMU_CFG_ARCH_LOCAL + 1) @@ -41,6 +43,8 @@ static inline int kvm_para_available(voi extern int qemu_cfg_present;
void qemu_cfg_port_probe(void); +u32 qemu_cfg_get_u32(int e); +void qemu_cfg_copy(int e, void *buf, int len); int qemu_cfg_show_boot_menu(void); void qemu_cfg_get_uuid(u8 *uuid); int qemu_cfg_irq0_override(void); Index: seabios/src/tcgbios.h =================================================================== --- seabios.orig/src/tcgbios.h +++ seabios/src/tcgbios.h @@ -376,7 +376,23 @@ 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); +u32 tcpa_process_firmware_cfg(void); void tcpa_menu(void);
+typedef struct TPMMsrHdr { + u16 rev; + u32 totlen; + u16 numTPMMsrEntries; +} __attribute__((packed)) TPMMsrHdr; + +typedef struct TPMMsrEntry { + u32 len; + u32 pcrindex; + u32 type; + u8 digest[SHA1_BUFSIZE]; + u32 eventdatasize; + u32 event; +} __attribute__((packed)) TPMMsrEntry; + #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 | 3 src/Kconfig | 7 src/tcgbios.c | 33 +- src/tis_test.c | 834 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tis_test.h | 52 +++ 5 files changed, 920 insertions(+), 9 deletions(-)
Index: seabios/src/tcgbios.c =================================================================== --- seabios.orig/src/tcgbios.c +++ seabios/src/tcgbios.c @@ -19,6 +19,9 @@ #include "smbios.h" // get_smbios_entry_point #include "paravirt.h" // QEMU_CFG_KERNEL_*, QEMU_CFG_INITRD_*
+#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 }; @@ -694,6 +697,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; @@ -1801,26 +1807,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) @@ -1833,6 +1842,7 @@ show_tpm_state(void) printf("and an owner cannot be installed.\n"); }
+ return state; }
@@ -1891,7 +1901,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, @@ -1910,9 +1920,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; @@ -1926,6 +1939,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 @@ -358,6 +358,13 @@ menu "BIOS interfaces" below the given size (in kb), otherwise use the internal SHA1 algorithm for calculating the SHA1.
+ config TIS_TEST + depends on TCGBIOS + bool "TPM TIS test" + default n + help + Test cases for the TPM TIS interface + endmenu
menu "BIOS Tables" Index: seabios/Makefile =================================================================== --- seabios.orig/Makefile +++ seabios/Makefile @@ -20,7 +20,8 @@ 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 \ - pci_region.c biostables.c xen.c tcgbios.c tpm_drivers.c sha1.c + pci_region.c biostables.c xen.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 \
On Wed, Jul 06, 2011 at 12:31:58PM -0400, Stefan Berger wrote:
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.
...
Thanks Stefan.
Where does this stand with respect to QEmu integration?
BTW, I don't think patch 7 or 9 really make sense to integrate in the official version of SeaBIOS. Also, in patch 8, I'd prefer to see all new fw_cfg entries use the "romfile" mechanism.
-Kevin
On 07/06/2011 06:58 PM, Kevin O'Connor wrote:
On Wed, Jul 06, 2011 at 12:31:58PM -0400, Stefan Berger wrote:
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.
...
Thanks Stefan.
Where does this stand with respect to QEmu integration?
Qemu integration is at least 'working' for me - it's just lacking review/attention on the Qemu mailing list.
BTW, I don't think patch 7 or 9 really make sense to integrate in the official version of SeaBIOS. Also, in patch 8, I'd prefer to see all new fw_cfg entries use the "romfile" mechanism.
Patch 7 is the menu. This patch is needed in 'some form' since in some cases, like after giving up ownership of the TPM, the TPM becomes disabled and deactivated and one has to interact with the BIOS to activate and enable it again. Other scenarios include someone who has forgotten the owner password for the TPM and now has to go through the BIOS to give up ownership of it -- that's the only way one can do this then.
I'll have a look at the 'romfile' mechanism for patch 8.
I only post patch 9 for someone who is interested to be able to run the tests. Since the 128kb are slowly filling up, it's not going to be compilable with it for much longer and I don't expect it to go into the repo.
Thanks for the feedback.
Stefan
-Kevin
On Thu, Jul 07, 2011 at 07:48:29AM -0400, Stefan Berger wrote:
On 07/06/2011 06:58 PM, Kevin O'Connor wrote:
BTW, I don't think patch 7 or 9 really make sense to integrate in the official version of SeaBIOS. Also, in patch 8, I'd prefer to see all new fw_cfg entries use the "romfile" mechanism.
Patch 7 is the menu. This patch is needed in 'some form' since in some cases, like after giving up ownership of the TPM, the TPM becomes disabled and deactivated and one has to interact with the BIOS to activate and enable it again. Other scenarios include someone who has forgotten the owner password for the TPM and now has to go through the BIOS to give up ownership of it -- that's the only way one can do this then.
Hrmm. I don't recall seeing this menu on the factory BIOS of real machines. How do normal users interact with it?
Can the info be passed in from QEmu?
I'll have a look at the 'romfile' mechanism for patch 8.
I only post patch 9 for someone who is interested to be able to run the tests. Since the 128kb are slowly filling up, it's not going to be compilable with it for much longer and I don't expect it to go into the repo.
There is no limit at 128K - if it's exceeded the build will start using a 256K rom.
More important than the total size is the "fixed" size reported at the end of the build - that's how much space is used under 1 Meg after the "post" phase completes. Ideally it would stay under 64K though that's not a hard limit either.
-Kevin
On 07/07/2011 08:43 AM, Kevin O'Connor wrote:
On Thu, Jul 07, 2011 at 07:48:29AM -0400, Stefan Berger wrote:
On 07/06/2011 06:58 PM, Kevin O'Connor wrote:
BTW, I don't think patch 7 or 9 really make sense to integrate in the official version of SeaBIOS. Also, in patch 8, I'd prefer to see all new fw_cfg entries use the "romfile" mechanism.
Patch 7 is the menu. This patch is needed in 'some form' since in some cases, like after giving up ownership of the TPM, the TPM becomes disabled and deactivated and one has to interact with the BIOS to activate and enable it again. Other scenarios include someone who has forgotten the owner password for the TPM and now has to go through the BIOS to give up ownership of it -- that's the only way one can do this then.
Hrmm. I don't recall seeing this menu on the factory BIOS of real machines. How do normal users interact with it?
Everyone has to go through the BIOS menu to maybe even enable and activate the TPM before first use. It's typically under 'security'.
Can the info be passed in from QEmu?
To a certain limit, yes. I have an experimental patch that allows one to pass in a parameter via command line to Qemu which then passes this parameter on to the BIOS via the firmware interface. It tells the BIOS what it should do with the TPM upon VM startup / reboot. I can for example tell it to always enable and activate the TPM so that the user doesn't have to go through the BIOS once he gave up ownership of the device. However, I cannot tell the BIOS to give up ownership every time there is a startup/reboot since otherwise all keys that the TPM owner generated will be lost. So the case where the user forgot his password is problematic and in that case he would have to get to the BIOS menu, give up ownership and then boot the machine again. The reason is that only during an early bootup the TPM is in a state that would allow certain commands to be sent that among other things allow giving up ownership.
I'll have a look at the 'romfile' mechanism for patch 8.
I only post patch 9 for someone who is interested to be able to run the tests. Since the 128kb are slowly filling up, it's not going to be compilable with it for much longer and I don't expect it to go into the repo.
So regarding the romfile mechanism for patch 8: In patch 8 I (expect Qemu to) hash for example files passed via -kernel, -initrd and data provided via -append or modules in case of multiboot, write them into concatenated data structures and pass the byte array to the firmware interface for SeaBIOS to pick up. Now with the romfile mechanism I have the impression that these are more or less static data, whereas what is happening in patch 8 depends on the parameters pass to qemu.
Here an example for such a command line when Qemu starts in this type of paravirtualized mode:
qemu -kernel /boot/vmlinuz-2.6.36 -initrd /boot/initrd-2.6.36 -append "ro [...]" [...]
In effect Qemu does a
sha1sum /boot/vmlinuz-2.6.36 sha1sum /boot/initrd-2.6.36 echo -n "ro [...] | sha1sum
and logs what it measured, i.e., kernel, initrd, or command line.
And here's the corresponding patch for Qemu:
http://www.mail-archive.com/qemu-devel@nongnu.org/msg69677.html
There is no limit at 128K - if it's exceeded the build will start using a 256K rom.
Good to know.
Stefan
More important than the total size is the "fixed" size reported at the end of the build - that's how much space is used under 1 Meg after the "post" phase completes. Ideally it would stay under 64K though that's not a hard limit either.
-Kevin