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.
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.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- src/tpm_drivers.c | 215 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tpm_drivers.h | 55 +++++++++++++ 2 files changed, 270 insertions(+)
Index: seabios/src/tpm_drivers.c =================================================================== --- /dev/null +++ seabios/src/tpm_drivers.c @@ -0,0 +1,215 @@ +/* + * Implementation of the TCG BIOS extension according to the specification + * described in + * https://www.trustedcomputinggroup.org/specs/PCClient/TCG_PCClientImplementat... + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) IBM Corporation, 2006, 2010, 2011 + * + * Author: Stefan Berger stefanb@us.ibm.com + */ + +#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; + } + mssleep(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) { + mssleep(1); + ctr++; + } + } + + if (burst == 0) { + rc = TCG_RESPONSE_TIMEOUT; + break; + } + + while (1) { + writeb(TIS_REG(locty, TIS_REG_DATA_FIFO), data[offset++]); + burst--; + + if (burst == 0 || offset == len) + break; + } + + if (offset == len) + end = 1; + } while (end == 0); + + return rc; +} + +static u32 tis_readresp(u8 *buffer, u32 *len) +{ + u32 rc = 0; + u32 offset = 0; + u32 sts; + u8 locty = tis_find_active_locality(); + + while (offset < *len) { + buffer[offset] = readb(TIS_REG(locty, TIS_REG_DATA_FIFO)); + offset++; + sts = readb(TIS_REG(locty, TIS_REG_STS)); + /* data left ? */ + if ((sts & TIS_STS_DATA_AVAILABLE) == 0) + break; + } + + *len = offset; + + return rc; +} + + +static u32 tis_waitdatavalid(void) +{ + u32 rc = 0; + u8 locty = tis_find_active_locality(); + + if (tis_wait_sts(locty, 1000, TIS_STS_VALID, TIS_STS_VALID) != 0) + rc = TCG_NO_RESPONSE; + + return rc; +} + +static u32 tis_waitrespready(u32 timeout) +{ + u32 rc = 0; + u8 locty = tis_find_active_locality(); + + writeb(TIS_REG(locty ,TIS_REG_STS), TIS_STS_TPM_GO); + + if (tis_wait_sts(locty, timeout, + TIS_STS_DATA_AVAILABLE, TIS_STS_DATA_AVAILABLE) != 0) + rc = TCG_NO_RESPONSE; + + return rc; +} + +struct tpm_driver tpm_drivers[TPM_NUM_DRIVERS] = { + { + .probe = tis_probe, + .init = tis_init, + .activate = tis_activate, + .ready = tis_ready, + .senddata = tis_senddata, + .readresp = tis_readresp, + .waitdatavalid = tis_waitdatavalid, + .waitrespready = tis_waitrespready, + }, +}; Index: seabios/src/tpm_drivers.h =================================================================== --- /dev/null +++ seabios/src/tpm_drivers.h @@ -0,0 +1,55 @@ +#ifndef TPM_DRIVERS_H + +#include "types.h" + +/* low level driver implementation */ +struct tpm_driver { + u32 (*probe)(void); + u32 (*init)(void); + u32 (*activate)(u8 locty); + u32 (*ready)(void); + u32 (*senddata)(const u8 *const data, u32 len); + u32 (*readresp)(u8 *buffer, u32 *len); + u32 (*waitdatavalid)(void); + u32 (*waitrespready)(u32 timeout); +}; + +#define TPM_NUM_DRIVERS 1 + +#define TPM_INVALID_DRIVER -1 + +/* TIS driver */ +/* address of locality 0 (TIS) */ +#define TPM_TIS_BASE_ADDRESS 0xfed40000 + +#define TIS_REG(LOCTY, REG) \ + (void *)(TPM_TIS_BASE_ADDRESS + (LOCTY << 12) + REG) + +/* hardware registers */ +#define TIS_REG_ACCESS 0x0 +#define TIS_REG_INT_ENABLE 0x8 +#define TIS_REG_INT_VECTOR 0xc +#define TIS_REG_INT_STATUS 0x10 +#define TIS_REG_INTF_CAPABILITY 0x14 +#define TIS_REG_STS 0x18 +#define TIS_REG_DATA_FIFO 0x24 +#define TIS_REG_DID_VID 0xf00 +#define TIS_REG_RID 0xf04 + +#define TIS_STS_VALID (1 << 7) /* 0x80 */ +#define TIS_STS_COMMAND_READY (1 << 6) /* 0x40 */ +#define TIS_STS_TPM_GO (1 << 5) /* 0x20 */ +#define TIS_STS_DATA_AVAILABLE (1 << 4) /* 0x10 */ +#define TIS_STS_EXPECT (1 << 3) /* 0x08 */ +#define TIS_STS_RESPONSE_RETRY (1 << 1) /* 0x02 */ + +#define TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) /* 0x80 */ +#define TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) /* 0x20 */ +#define TIS_ACCESS_BEEN_SEIZED (1 << 4) /* 0x10 */ +#define TIS_ACCESS_SEIZE (1 << 3) /* 0x08 */ +#define TIS_ACCESS_PENDING_REQUEST (1 << 2) /* 0x04 */ +#define TIS_ACCESS_REQUEST_USE (1 << 1) /* 0x02 */ +#define TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) /* 0x01 */ + + +#endif /* TPM_DRIVERS_H */
This patch provides ACPI support for the TPM device. It probes for the TPM device and only if a TPM device is found then the TPM's SSDT and TCPA table are created. This patch also connects them to the RSDT.
Since the logging area in the TCPA table requires 64kb, the memory reserved for ACPI tables (config.h) is increased to 96kb in case CONFIG_TCGBIOS is enabled.
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.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- Makefile | 9 ++++++++- src/acpi-tpm-ssdt.dsl | 22 ++++++++++++++++++++++ src/acpi-tpm-ssdt.hex | 34 ++++++++++++++++++++++++++++++++++ src/acpi.c | 41 +++++++++++++++++++++++++++++++++++++++++ src/acpi.h | 20 ++++++++++++++++++++ src/config.h | 6 +++++- 6 files changed, 130 insertions(+), 2 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,34 @@ +/* + * + * Intel ACPI Component Architecture + * ASL Optimizing Compiler version 20101013-64 [Nov 21 2010] + * Copyright (c) 2000 - 2010 Intel Corporation + * + * Compilation of "out/.dsl.i" - Sun Mar 13 20:44:32 2011 + * + * C source code output + * AML code block contains 0x93 bytes + * + */ +unsigned char AmlCode_TPM[] = +{ + 0x53,0x53,0x44,0x54,0x93,0x00,0x00,0x00, /* 00000000 "SSDT...." */ + 0x01,0xC3,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,0x4D,0x06, /* 00000020 "... [.M." */ + 0x54,0x50,0x4D,0x5F,0x08,0x5F,0x48,0x49, /* 00000028 "TPM_._HI" */ + 0x44,0x0C,0x41,0xD0,0x0C,0x31,0x08,0x5F, /* 00000030 "D.A..1._" */ + 0x53,0x54,0x52,0x11,0x33,0x0A,0x30,0x45, /* 00000038 "STR.3.0E" */ + 0x00,0x6D,0x00,0x75,0x00,0x6C,0x00,0x61, /* 00000040 ".m.u.l.a" */ + 0x00,0x74,0x00,0x65,0x00,0x64,0x00,0x20, /* 00000048 ".t.e.d. " */ + 0x00,0x54,0x00,0x50,0x00,0x4D,0x00,0x20, /* 00000050 ".T.P.M. " */ + 0x00,0x54,0x00,0x49,0x00,0x53,0x00,0x20, /* 00000058 ".T.I.S. " */ + 0x00,0x64,0x00,0x65,0x00,0x76,0x00,0x69, /* 00000060 ".d.e.v.i" */ + 0x00,0x63,0x00,0x65,0x00,0x00,0x00,0x08, /* 00000068 ".c.e...." */ + 0x5F,0x43,0x52,0x53,0x11,0x14,0x0A,0x11, /* 00000070 "_CRS...." */ + 0x86,0x09,0x00,0x01,0x00,0x00,0xD4,0xFE, /* 00000078 "........" */ + 0x00,0x50,0x00,0x00,0x22,0x00,0x08,0x79, /* 00000080 ".P.."..y" */ + 0x00,0x14,0x09,0x5F,0x53,0x54,0x41,0x00, /* 00000088 "..._STA." */ + 0xA4,0x0A,0x0F /* 00000090 "..." */ +}; Index: seabios/Makefile =================================================================== --- seabios.orig/Makefile +++ seabios/Makefile @@ -192,13 +192,20 @@ $(OUT)vgabios.bin: $(OUT)vgabios.bin.raw $(Q)./tools/buildrom.py $< $@
####### dsdt build rules +src/acpi-tpm-ssdt.hex: src/acpi-tpm-ssdt.dsl + @echo "Compiling TPM SSDT" + $(Q)cpp -P $< > $(OUT)$*.dsl.i + $(Q)iasl -tc -p $(OUT)$* $(OUT)$*.dsl.i + $(Q)cp $(OUT)$*.hex $@ + $(Q)sed -i 's/AmlCode/AmlCode_TPM/' $@ + src/%.hex: src/%.dsl @echo "Compiling DSDT" $(Q)cpp -P $< > $(OUT)$*.dsl.i $(Q)iasl -tc -p $(OUT)$* $(OUT)$*.dsl.i $(Q)cp $(OUT)$*.hex $@
-$(OUT)ccode32flat.o: src/acpi-dsdt.hex +$(OUT)ccode32flat.o: src/acpi-dsdt.hex src/acpi-tpm-ssdt.hex
####### Kconfig rules export HOSTCC := $(CC) Index: seabios/src/acpi.c =================================================================== --- seabios.orig/src/acpi.c +++ seabios/src/acpi.c @@ -13,6 +13,8 @@ #include "pci_regs.h" // PCI_INTERRUPT_LINE #include "paravirt.h" #include "dev-i440fx.h" // piix4_fadt_init +#include "acpi-tpm-ssdt.hex" +#include "tcgbios.h" // has_working_tpm
/****************************************************/ /* ACPI tables init */ @@ -586,6 +588,39 @@ static const struct pci_device_id acpi_f PCI_DEVICE_END, };
+ +static u32 add_tpm_device(void **tpm_addr, void **tcpa_addr) +{ + struct tcpa_descriptor_rev2 *tcpa; + + *tpm_addr = NULL; + *tcpa_addr = NULL; + + if (has_working_tpm()) { + u32 laml = 64 * 1024; + *tpm_addr = malloc_high(sizeof(AmlCode_TPM)); + + tcpa = malloc_high(sizeof(*tcpa) + laml); + if (!tcpa || !*tpm_addr) { + warn_noalloc(); + return 1; + } + + if (*tpm_addr) + memcpy(*tpm_addr, AmlCode_TPM, sizeof(AmlCode_TPM)); + + memset(tcpa, 0x0, sizeof(*tcpa) + laml); + u64 lasa = (u32)tcpa + sizeof(*tcpa); + + tcpa->laml = laml; + tcpa->lasa = lasa; + build_header((void*)tcpa, TCPA_SIGNATURE, sizeof(*tcpa), 2); + + *tcpa_addr = tcpa; + } + return 0; +} + struct rsdp_descriptor *RsdpAddr;
#define MAX_ACPI_TABLES 20 @@ -642,6 +677,12 @@ acpi_bios_init(void) } }
+ void *tcpa, *tpm; + if (add_tpm_device(&tpm, &tcpa)) + return; + ACPI_INIT_TABLE(tpm); + ACPI_INIT_TABLE(tcpa); + struct rsdt_descriptor_rev1 *rsdt; size_t rsdt_len = sizeof(*rsdt) + sizeof(u32) * tbl_idx; rsdt = malloc_high(rsdt_len); Index: seabios/src/acpi.h =================================================================== --- seabios.orig/src/acpi.h +++ seabios/src/acpi.h @@ -98,4 +98,24 @@ struct fadt_descriptor_rev1 #endif } PACKED;
+ +struct rsdt_descriptor { + ACPI_TABLE_HEADER_DEF + u32 entry[1]; +} PACKED; + +#define TCPA_SIGNATURE 0x41504354 +struct tcpa_descriptor_rev2 +{ + ACPI_TABLE_HEADER_DEF + u16 platform_class; + u32 laml; + u64 lasa; +} PACKED; + +/* TCPA ACPI definitions */ +#define TCPA_ACPI_CLASS_CLIENT 0 +#define TCPA_ACPI_CLASS_SERVER 1 + + #endif // acpi.h Index: seabios/src/config.h =================================================================== --- seabios.orig/src/config.h +++ seabios/src/config.h @@ -26,7 +26,11 @@ // 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) +#if CONFIG_TCGBIOS +# define CONFIG_MAX_HIGHTABLE (96*1024) +#else +# define CONFIG_MAX_HIGHTABLE (64*1024) +#endif // Largest supported externaly facing drive id #define CONFIG_MAX_EXTDRIVE 16
On Wed, Mar 30, 2011 at 01:55:36PM -0400, Stefan Berger wrote:
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.
[...]
// Space to reserve in high-memory for tables -#define CONFIG_MAX_HIGHTABLE (64*1024) +#if CONFIG_TCGBIOS +# define CONFIG_MAX_HIGHTABLE (96*1024) +#else +# define CONFIG_MAX_HIGHTABLE (64*1024) +#endif
I'm a bit confused on patch ordering - it seems CONFIG_TCGBIOS is defined in patch 4 but used in patch 2.
Also, I'd prefer to avoid ifdefs - just up the size to 96 always, or do something like:
#define CONFIG_MAX_HIGHTABLE ((64*1024) + CONFIG_TCGBIOS*(32*1024))
-Kevin
On 04/04/2011 12:17 AM, Kevin O'Connor wrote:
On Wed, Mar 30, 2011 at 01:55:36PM -0400, Stefan Berger wrote:
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.
[...]
// Space to reserve in high-memory for tables -#define CONFIG_MAX_HIGHTABLE (64*1024) +#if CONFIG_TCGBIOS +# define CONFIG_MAX_HIGHTABLE (96*1024) +#else +# define CONFIG_MAX_HIGHTABLE (64*1024) +#endif
I'm a bit confused on patch ordering - it seems CONFIG_TCGBIOS is defined in patch 4 but used in patch 2.
I had it in patch 2 since there I am allocating 64kb in the high memory... I'll move it to patch 4.
Also, I'd prefer to avoid ifdefs - just up the size to 96 always, or do something like:
#define CONFIG_MAX_HIGHTABLE ((64*1024) + CONFIG_TCGBIOS*(32*1024))
Done that.
Stefan
-Kevin
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 - a utility function called mssleep is added. It waits for a number of milliseconds before it returns. I had tried to build a function like that based on calc_future_time() and check_timer(), but those didn't work once in an S3 resume.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- src/boot.c | 2 src/post.c | 5 src/resume.c | 2 src/tcgbios.c | 525 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 386 ++++++++++++++++++++++++++++++++++++++++++ src/util.c | 18 + src/util.h | 5 7 files changed, 943 insertions(+)
Index: seabios/src/tcgbios.c =================================================================== --- /dev/null +++ seabios/src/tcgbios.c @@ -0,0 +1,525 @@ +/* + * Implementation of the TCG BIOS extension according to the specification + * described in + * https://www.trustedcomputinggroup.org/specs/PCClient/TCG_PCClientImplementat... + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) IBM Corporation, 2006,2010,2011 + * + * Author: Stefan Berger stefanb@us.ibm.com + */ + +#include "config.h" + +#if CONFIG_TCGBIOS + +#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 +#include "smbios.h" // smbios_entry_point + + +//#define DEBUG_TCGBIOS + +static const u8 Startup_ST_CLEAR[2] = { 0x00, TPM_ST_CLEAR }; +static const u8 Startup_ST_STATE[2] = { 0x00, TPM_ST_STATE }; + +static const u8 PhysicalPresence_CMD_ENABLE[2] = { 0x00, 0x20 }; +static const u8 PhysicalPresence_CMD_DISABLE[2] = { 0x01, 0x00 }; +static const u8 PhysicalPresence_PRESENT[2] = { 0x00, 0x08 }; +static const u8 PhysicalPresence_NOT_PRESENT[2] = { 0x00, 0x10 }; +static const u8 PhysicalPresence_LOCK[2] = { 0x00, 0x04 }; + +static const u8 CommandFlag_FALSE[1] = { 0x00 }; +static const u8 CommandFlag_TRUE[1] = { 0x01 }; + +static const u8 GetCapability_Permanent_Flags[12] = { + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x08 +}; + +static const u8 GetCapability_OwnerAuth[12] = { + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x11 +}; + + +#define RSDP_CAST(ptr) ((struct rsdp_descriptor *)ptr) + + +/* helper functions */ + +static inline void *input_buf32(struct bregs *regs) +{ + return MAKE_FLATPTR(regs->es, regs->di); +} + +static inline void *output_buf32(struct bregs *regs) +{ + return MAKE_FLATPTR(regs->ds, regs->si); +} + + +typedef struct { + 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; +} + + +static u8 +calc_checksum(const u8 *addr, u32 length) +{ + u8 sum = 0; + u32 ctr; + + for (ctr = 0; ctr < length; ctr++) + sum += addr[ctr]; + + return sum; +} + + +/* + * Search for the RSDP ACPI table in the memory starting at addr and + * ending at addr + len - 1. + */ +static struct rsdp_descriptor * +find_rsdp(u8 *start, unsigned int len) +{ + u8 *end = start + len; + + /* scan memory in steps of 16 bytes */ + while (start < end) { + /* check for expected string */ + if (RSDP_CAST(start)->signature == RSDP_SIGNATURE && + calc_checksum(start, + sizeof(struct rsdp_descriptor)) == 0) + return RSDP_CAST(start); + start += 0x10; + } + + return 0; +} + + + + +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 && + calc_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; + u16 ebda_seg; + + /* RSDP in EBDA? */ + ebda_seg = *(u16 *)MAKE_FLATPTR(0x40, 0xe); + rsdp = find_rsdp(MAKE_FLATPTR(ebda_seg, 0), 1024); + + if (rsdp) + tcpa = find_tcpa_by_rsdp(rsdp); + + if (!tcpa) { + rsdp = find_rsdp((u8 *)0xE0000, 0x20000); + if (rsdp) + tcpa = find_tcpa_by_rsdp(rsdp); + } + + if (!rsdp) + tcpa_state.if_shutdown = 1; + +#ifdef DEBUG_TCGBIOS + if (! rsdp ) + dprintf(1, "TCGBIOS: RSDP was NOT found! -- Disabling interface.\n"); + else if ( !tcpa ) + dprintf(1, "TCGBIOS: TCPA ACPI was NOT found!\n"); +#endif + + 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) +{ + 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) { +#ifdef DEBUG_TCGBIOS + dprintf(1, "TCGBIOS: size of requested buffers too big."); +#endif + 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 = 0; + u32 returnCode; + + if (!has_working_tpm()) + return 0; + +#ifdef DEBUG_TCGBIOS + dprintf(1,"TCGBIOS: Starting with TPM_Startup(ST_CLEAR)\n"); +#endif + rc = build_and_send_cmd(TPM_ORD_Startup, + Startup_ST_CLEAR, sizeof(Startup_ST_CLEAR), + NULL, 10, &returnCode); +#ifdef DEBUG_TCGBIOS + dprintf(1,"Return code from TPM_Startup = 0x%08x\n", + returnCode); +#endif + if (rc && returnCode) + goto err_exit; + + rc = build_and_send_cmd(TPM_ORD_SelfTestFull, NULL, 0, + NULL, 10, &returnCode); +#ifdef DEBUG_TCGBIOS + dprintf(1,"Return code from TPM_SelfTestFull = 0x%08x\n", + returnCode); +#endif + if (rc || returnCode) + goto err_exit; + + return 0; + +err_exit: +#ifdef DEBUG_TCGBIOS + dprintf(1,"TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); +#endif + tcpa_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +u32 +tcpa_leave_bios(void) +{ + u32 rc = 0; + u32 returnCode; + + if (!has_working_tpm()) + return 0; + + rc = build_and_send_cmd(TPM_ORD_PhysicalPresence, + PhysicalPresence_CMD_ENABLE, + sizeof(PhysicalPresence_CMD_ENABLE), + NULL, 10, &returnCode); + if (rc || returnCode) + goto err_exit; + + rc = build_and_send_cmd(TPM_ORD_PhysicalPresence, + PhysicalPresence_NOT_PRESENT, + sizeof(PhysicalPresence_NOT_PRESENT), + NULL, 10, &returnCode); + if (rc || returnCode) + goto err_exit; + + return 0; + +err_exit: +#ifdef DEBUG_TCGBIOS + dprintf(1,"TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); +#endif + tcpa_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +u32 +tcpa_s3_resume(void) +{ + u32 rc = 0; + u32 returnCode; + + if (has_working_tpm()) { + rc = build_and_send_cmd(TPM_ORD_Startup, + Startup_ST_STATE, + sizeof(Startup_ST_STATE), + NULL, 10, &returnCode); +#ifdef DEBUG_TCGBIOS + dprintf(1,"TCGBIOS: Resuming with TPM_Startup(ST_STATE)\n"); + dprintf(1,"TCGBIOS: ReturnCode from TPM_Startup = 0x%08x\n", + returnCode); +#endif + if (rc || returnCode) + goto err_exit; + } + + return 0; + +err_exit: +#ifdef DEBUG_TCGBIOS + dprintf(1,"TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); +#endif + tcpa_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +#endif /* CONFIG_TCGBIOS */ Index: seabios/src/tcgbios.h =================================================================== --- /dev/null +++ seabios/src/tcgbios.h @@ -0,0 +1,386 @@ +#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 +#define TCG_GENERAL_ERROR (TPM_RET_BASE + 0x0) +#define TCG_TPM_IS_LOCKED (TPM_RET_BASE + 0x1) +#define TCG_NO_RESPONSE (TPM_RET_BASE + 0x2) +#define TCG_INVALID_RESPONSE (TPM_RET_BASE + 0x3) +#define TCG_INVALID_ACCESS_REQUEST (TPM_RET_BASE + 0x4) +#define TCG_FIRMWARE_ERROR (TPM_RET_BASE + 0x5) +#define TCG_INTEGRITY_CHECK_FAILED (TPM_RET_BASE + 0x6) +#define TCG_INVALID_DEVICE_ID (TPM_RET_BASE + 0x7) +#define TCG_INVALID_VENDOR_ID (TPM_RET_BASE + 0x8) +#define TCG_UNABLE_TO_OPEN (TPM_RET_BASE + 0x9) +#define TCG_UNABLE_TO_CLOSE (TPM_RET_BASE + 0xa) +#define TCG_RESPONSE_TIMEOUT (TPM_RET_BASE + 0xb) +#define TCG_INVALID_COM_REQUEST (TPM_RET_BASE + 0xc) +#define TCG_INVALID_ADR_REQUEST (TPM_RET_BASE + 0xd) +#define TCG_WRITE_BYTE_ERROR (TPM_RET_BASE + 0xe) +#define TCG_READ_BYTE_ERROR (TPM_RET_BASE + 0xf) +#define TCG_BLOCK_WRITE_TIMEOUT (TPM_RET_BASE + 0x10) +#define TCG_CHAR_WRITE_TIMEOUT (TPM_RET_BASE + 0x11) +#define TCG_CHAR_READ_TIMEOUT (TPM_RET_BASE + 0x12) +#define TCG_BLOCK_READ_TIMEOUT (TPM_RET_BASE + 0x13) +#define TCG_TRANSFER_ABORT (TPM_RET_BASE + 0x14) +#define TCG_INVALID_DRV_FUNCTION (TPM_RET_BASE + 0x15) +#define TCG_OUTPUT_BUFFER_TOO_SHORT (TPM_RET_BASE + 0x16) +#define TCG_FATAL_COM_ERROR (TPM_RET_BASE + 0x17) +#define TCG_INVALID_INPUT_PARA (TPM_RET_BASE + 0x18) +#define TCG_TCG_COMMAND_ERROR (TPM_RET_BASE + 0x19) +#define TCG_INTERFACE_SHUTDOWN (TPM_RET_BASE + 0x20) +//define TCG_PC_UNSUPPORTED (TPM_RET_BASE + 0x21) +#define TCG_PC_TPM_NOT_PRESENT (TPM_RET_BASE + 0x22) +#define TCG_PC_TPM_DEACTIVATED (TPM_RET_BASE + 0x23) + + +#define TPM_INVALID_ADR_REQUEST TCG_INVALID_ADR_REQUEST +#define TPM_IS_LOCKED TCG_TPM_IS_LOCKED +#define TPM_INVALID_DEVICE_ID TCG_INVALID_DEVICE_ID +#define TPM_INVALID_VENDOR_ID TCG_INVALID_VENDOR_ID +//define TPM_RESERVED_REG_INVALID +#define TPM_FIRMWARE_ERROR TCG_FIRMWARE_ERROR +#define TPM_UNABLE_TO_OPEN TCG_UNABLE_TO_OPEN +#define TPM_UNABLE_TO_CLOSE TCG_UNABLE_TO_CLOSE +#define TPM_INVALID_RESPONSE TCG_INVALID_RESPONSE +#define TPM_RESPONSE_TIMEOUT TCG_RESPONSE_TIMEOUT +#define TPM_INVALID_ACCESS_REQUEST TCG_INVALID_ACCESS_REQUEST +#define TPM_TRANSFER_ABORT TCG_TRANSFER_ABORT +#define TPM_GENERAL_ERROR TCG_GENERAL_ERROR + + +#define TPM_ORD_SelfTestFull 0x00000050 +#define TPM_ORD_ForceClear 0x0000005d +#define TPM_ORD_GetCapability 0x00000065 +#define TPM_ORD_PhysicalEnable 0x0000006f +#define TPM_ORD_PhysicalDisable 0x00000070 +#define TPM_ORD_SetOwnerInstall 0x00000071 +#define TPM_ORD_PhysicalSetDeactivated 0x00000072 +#define TPM_ORD_Startup 0x00000099 +#define TPM_ORD_PhysicalPresence 0x4000000a +#define TPM_ORD_Extend 0x00000014 +#define TPM_ORD_SHA1Start 0x000000a0 +#define TPM_ORD_SHA1Update 0x000000a1 +#define TPM_ORD_SHA1Complete 0x000000a2 + + +#define 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 +}; + +#if CONFIG_TCGBIOS +void tcpa_acpi_init(void); +int has_working_tpm(void); +u32 tcpa_startup(void); +u32 tcpa_leave_bios(void); +u32 tcpa_s3_resume(void); +#else +static inline void tcpa_acpi_init(void) { +} +static inline int has_working_tpm(void) { + return 0; +} +static inline u32 tcpa_startup(void) { + return 0; +} +static inline u32 tcpa_leave_bios(void) { + return 0; +} +static inline u32 tcpa_s3_resume(void) { + return 0; +} +#endif +void tcpa_menu(void); + +#endif /* TCGBIOS_H */ Index: seabios/src/util.c =================================================================== --- seabios.orig/src/util.c +++ seabios/src/util.c @@ -322,3 +322,21 @@ get_keystroke(int msec) wait_irq(); } } + + +// wait a given time +// this function also works in case of a resume +void +mssleep(u32 msec) +{ + u32 i; + u8 x, y = inb(0x61) & 0x10; + + /* Poll the DRAM refresh timer: I/O port 61h, bit 4 toggles every 15us. */ + msec *= (1000/15); /* Convert milliseconds to multiples of 15us. */ + for ( i = 0; i < msec; i++ ) { + while ( (x = inb(0x61) & 0x10) == y ) + continue; + y = x; + } +} Index: seabios/src/util.h =================================================================== --- seabios.orig/src/util.h +++ seabios/src/util.h @@ -497,4 +497,9 @@ extern u8 BiosChecksum; // version (auto generated file out/version.c) extern const char VERSION[];
+ +void mssleep(u32 time); + +void tcpa_interrupt_handler16(struct bregs *regs); + #endif // util.h Index: seabios/src/post.c =================================================================== --- seabios.orig/src/post.c +++ seabios/src/post.c @@ -25,6 +25,7 @@ #include "paravirt.h" // qemu_cfg_port_probe #include "ps2port.h" // ps2port_setup #include "virtio-blk.h" // virtio_blk_setup +#include "tcgbios.h" // tcpa_*
/**************************************************************** @@ -238,6 +239,10 @@ maininit(void) mouse_setup(); init_bios_tables();
+ // Initialize tpm (after acpi tables were written) + tcpa_acpi_init(); + tcpa_startup(); + // Run vga option rom vga_setup();
Index: seabios/src/boot.c =================================================================== --- seabios.orig/src/boot.c +++ seabios/src/boot.c @@ -14,6 +14,7 @@ #include "cmos.h" // inb_cmos #include "paravirt.h" // romfile_loadfile #include "pci.h" //pci_bdf_to_* +#include "tcgbios.h" // tcpa_*
/**************************************************************** @@ -449,6 +450,7 @@ boot_prep(void) // Allow user to modify BCV/IPL order. interactive_bootmenu(); wait_threads(); + tcpa_leave_bios();
// Map drives and populate BEV list struct bootentry_s *pos = BootList; Index: seabios/src/resume.c =================================================================== --- seabios.orig/src/resume.c +++ seabios/src/resume.c @@ -10,6 +10,7 @@ #include "biosvar.h" // struct bios_data_area_s #include "bregs.h" // struct bregs #include "acpi.h" // find_resume_vector +#include "tcgbios.h" // tcpa_s3_resume
// Reset DMA controller void @@ -116,6 +117,7 @@ s3_resume(void) if (s3_resume_vector) { dprintf(1, "Jump to resume vector (%x)\n", s3_resume_vector); br.code = FLATPTR_TO_SEGOFF((void*)s3_resume_vector); + tcpa_s3_resume(); } else { dprintf(1, "No resume vector set!\n"); // Jump to the post vector to restart with a normal boot.
Hi Stefan,
I haven't had a chance to fully review, but I do have some comments.
On Wed, Mar 30, 2011 at 01:55:37PM -0400, Stefan Berger wrote: [...]
- a utility function called mssleep is added. It waits for a number of milliseconds before it returns. I had tried to build a function like that based on calc_future_time() and check_timer(), but those didn't work once in an S3 resume.
There is already msleep() for this. The inb(0x61) thing doesn't actually work anywhere but on bochs. If msleep() isn't working for you, try rerunning calibrate_tsc() after S3 resume.
[...]
+/*
- Implementation of the TCG BIOS extension according to the specification
- described in
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
SeaBIOS is LGPLv3. Also, I'm not a big fan of these big copyright notices on the top of every file - is it necessary?
[...]
+#if CONFIG_TCGBIOS
I'd prefer to avoid ifdefs in the code. Everything should be setup to have the compiler/linker automatically drop anything unused. (So, a simple "if (!CONFIG_TCGBIOS) return;" should drop any function and all variables/functions only reachable from it.)
[...]
+//#define DEBUG_TCGBIOS
Same here. Can you use just set DEBUG_tcg to a value in config.h and replace all the dprintf(1, ...) with dprintf(DEBUG_tcg, ...)?
+static u8 +calc_checksum(const u8 *addr, u32 length)
util.c:checksum()
+static struct rsdp_descriptor * +find_rsdp(u8 *start, unsigned int len)
Should be unneeded - see how acpi.c:find_resume_vector() works.
-Kevin
On 04/04/2011 12:14 AM, Kevin O'Connor wrote:
Hi Stefan,
I haven't had a chance to fully review, but I do have some comments.
Thanks. I can post a v2 as a followup to your changes.
On Wed, Mar 30, 2011 at 01:55:37PM -0400, Stefan Berger wrote: [...]
- a utility function called mssleep is added. It waits for a number of milliseconds before it returns. I had tried to build a function like that based on calc_future_time() and check_timer(), but those didn't work once in an S3 resume.
There is already msleep() for this. The inb(0x61) thing doesn't actually work anywhere but on bochs. If msleep() isn't working for you, try rerunning calibrate_tsc() after S3 resume.
Using that one now.
[...]
+/*
- Implementation of the TCG BIOS extension according to the specification
- described in
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
SeaBIOS is LGPLv3. Also, I'm not a big fan of these big copyright notices on the top of every file - is it necessary?
I will change it to look like the other files.
[...]
+#if CONFIG_TCGBIOS
I'd prefer to avoid ifdefs in the code. Everything should be setup to have the compiler/linker automatically drop anything unused. (So, a simple "if (!CONFIG_TCGBIOS) return;" should drop any function and all variables/functions only reachable from it.)
Ok, I will adapt it to that.
[...]
+//#define DEBUG_TCGBIOS
Same here. Can you use just set DEBUG_tcg to a value in config.h and replace all the dprintf(1, ...) with dprintf(DEBUG_tcg, ...)?
Did that now.
+static u8 +calc_checksum(const u8 *addr, u32 length)
util.c:checksum()
Using that one.
+static struct rsdp_descriptor * +find_rsdp(u8 *start, unsigned int len)
Should be unneeded - see how acpi.c:find_resume_vector() works.
I was going to keep the function find_rsdp but change its implementation to the test you are doing in find_resume_vector() regarding rsdp signature.
Stefan
-Kevin
This patch allows to configure the TCGBIOS extensions to be built into SeaBIOS, depending on not COREBOOT being selected.
All TCG BIOS extensions are activated with CONFIG_TCGBIOS.
Add the two new code files (tcgbios.c, tpm_drivers.c) to be built.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- Makefile | 2 +- src/Kconfig | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-)
Index: seabios/Makefile =================================================================== --- seabios.orig/Makefile +++ seabios/Makefile @@ -20,7 +20,7 @@ SRC16=$(SRCBOTH) system.c disk.c font.c SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \ acpi.c smm.c mptable.c smbios.c pciinit.c optionroms.c mtrr.c \ lzmadecode.c bootsplash.c jpeg.c usb-hub.c paravirt.c dev-i440fx.c \ - pci_region.c + pci_region.c tcgbios.c tpm_drivers.c SRC32SEG=util.c output.c pci.c pcibios.c apm.c stacks.c
cc-option = $(shell if test -z "`$(1) $(2) -S -o /dev/null -xc \ Index: seabios/src/Kconfig =================================================================== --- seabios.orig/src/Kconfig +++ seabios/src/Kconfig @@ -314,6 +314,14 @@ menu "BIOS interfaces" default n help Disable A20 on 16bit boot. + + config TCGBIOS + depends on !COREBOOT + bool "TPM support and TCG BIOS extensions" + default y + help + Provide TPM support along with TCG BIOS extensions + endmenu
menu "BIOS Tables"
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.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- src/Kconfig | 8 src/clock.c | 9 src/stacks.c | 14 + src/tcgbios.c | 714 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 3 5 files changed, 748 insertions(+)
Index: seabios/src/tcgbios.c =================================================================== --- seabios.orig/src/tcgbios.c +++ seabios/src/tcgbios.c @@ -62,6 +62,9 @@ static const u8 GetCapability_OwnerAuth[ #define RSDP_CAST(ptr) ((struct rsdp_descriptor *)ptr)
+static u32 sha1(const u8 *data, u32 length, u8 *hash); + + /* helper functions */
static inline void *input_buf32(struct bregs *regs) @@ -522,4 +525,715 @@ 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; +} + + +/******************************************************************* + Calculation of SHA1 in SW + + See: http://www.itl.nist.gov/fipspubs/fip180-1.htm + RFC3174, Wikipedia's SHA1 alogrithm description + ******************************************************************/ +typedef struct _sha1_ctx { + u32 h[5]; +} sha1_ctx; + + +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); +} + + +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]); +} + + +static u32 +sha1_fast(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; +} + + + + +#if CONFIG_TPM_FOR_SHA1 + +static u32 +sha1(const u8 *data, u32 length, u8 *hash) +{ + u32 rc; + u32 returnCode; + struct tpm_res_sha1start start; + struct tpm_res_sha1complete complete; + u32 blocks = length / 64; + u32 rest = length % 0x3f; + u32 numbytes, numbytes_no; + u32 offset = 0; + + if (length > 100 * 1024) + return sha1_fast(data, length, hash); + + rc = build_and_send_cmd(TPM_ORD_SHA1Start, + NULL, 0, + (u8 *)&start, + sizeof(struct tpm_res_sha1start), + &returnCode); + + if (rc || returnCode) + goto err_exit; + + while (blocks > 0) { + + numbytes = ntohl(start.max_num_bytes); + if (numbytes > blocks * 64) + numbytes = blocks * 64; + + numbytes_no = htonl(numbytes); + + rc = build_and_send_cmd_od(TPM_ORD_SHA1Update, + (u8 *)&numbytes_no, sizeof(numbytes_no), + NULL, 0, &returnCode, + &data[offset], numbytes); + + if (rc || returnCode) + goto err_exit; + + offset += numbytes; + blocks -= (numbytes / 64); + } + + numbytes_no = htonl(rest); + + rc = build_and_send_cmd_od(TPM_ORD_SHA1Complete, + (u8 *)&numbytes_no, sizeof(numbytes_no), + (u8 *)&complete, + sizeof(struct tpm_res_sha1complete), + &returnCode, + &data[offset], rest); + + if (rc || returnCode) + goto err_exit; + + memcpy(hash, complete.hash, sizeof(complete.hash)); + + return 0; + +err_exit: +#ifdef DEBUG_TCGBIOS + dprintf(1,"TCGBIOS: TPM SHA1 malfunctioning.\n"); +#endif + tcpa_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + +#else + +static u32 +sha1(const u8 *data, u32 length, u8 *hash) +{ + return sha1_fast(data, length, hash); +} + +#endif +/* + * 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); + +#ifdef DEBUG_TCGBIOS + dprintf(1, "TCGBIOS: LASA_BASE = %p, LASA_NEXT = %p\n",lasa_base, lasa_next); +#endif + + if (lasa_next == NULL || laml == 0) + return TCG_PC_LOGOVERFLOW; + + size = pcpes->eventdatasize + offsetof(struct pcpes, event); + + if ((lasa_next + size - lasa_base) > laml) { +#ifdef DEBUG_TCGBIOS + dprintf(1, "TCGBIOS: LOG OVERFLOW: size = %d\n", size); +#endif + 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((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((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) +{ + 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); + set_cf(regs, 0);; + } + break; + + case TCG_HashLogExtendEvent: + regs->eax = + hash_log_extend_event( + (struct hleei_short *)input_buf32(regs), + (struct hleeo *)output_buf32(regs)); + set_cf(regs, 0); + break; + + case TCG_PassThroughToTPM: + regs->eax = + pass_through_to_tpm((struct pttti *)input_buf32(regs), + (struct pttto *)output_buf32(regs)); + set_cf(regs, 0); + break; + + case TCG_ShutdownPreBootInterface: + regs->eax = shutdown_preboot_interface(); + set_cf(regs, 0); + break; + + case TCG_HashLogEvent: + regs->eax = hash_log_event((struct hlei*)input_buf32(regs), + (struct hleo*)output_buf32(regs)); + set_cf(regs, 0); + break; + + case TCG_HashAll: + regs->eax = + hash_all((struct hai*)input_buf32(regs), + (u8 *)output_buf32(regs)); + set_cf(regs, 0); + break; + + case TCG_TSS: + regs->eax = tss((struct ti*)input_buf32(regs), + (struct to*)output_buf32(regs)); + set_cf(regs, 0); + break; + + case TCG_CompactHashLogExtendEvent: + regs->eax = + compact_hash_log_extend_event((u8 *)input_buf32(regs), + regs->esi, + regs->ecx, + regs->edx, + ®s->edx); + set_cf(regs, 0); + break; + + default: + set_cf(regs, 1); + } + + return; +} + + #endif /* CONFIG_TCGBIOS */ Index: seabios/src/stacks.c =================================================================== --- seabios.orig/src/stacks.c +++ seabios/src/stacks.c @@ -7,6 +7,7 @@ #include "biosvar.h" // get_ebda_seg #include "util.h" // dprintf #include "bregs.h" // CR0_PE +#include "tcgbios.h"
// Thread info - stored at bottom of each thread stack - don't change // without also updating the inline assembler below. @@ -393,3 +394,16 @@ check_preempt(void) extern void _cfunc32flat_yield_preempt(void); call32(_cfunc32flat_yield_preempt, 0, 0); } + + +#ifdef CONFIG_TCGBIOS +void tcpa_interrupt_handler16(struct bregs *regs) +{ + if (MODESEGMENT) { + dprintf(3, "16: Calling tcpa_interrupt_handler\n"); + call32(_cfunc32flat_tcpa_interrupt_handler32, (u32)regs, 0); + } else { + _cfunc32flat_tcpa_interrupt_handler32(regs); + } +} +#endif /* CONFIG_TCGBIOS */ Index: seabios/src/clock.c =================================================================== --- seabios.orig/src/clock.c +++ seabios/src/clock.c @@ -434,6 +434,14 @@ handle_1a07(struct bregs *regs) set_success(regs); }
+static void +handle_1abb(struct bregs *regs) +{ +#if CONFIG_TCGBIOS + tcpa_interrupt_handler16(regs); +#endif +} + // Unsupported static void handle_1aXX(struct bregs *regs) @@ -456,6 +464,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/tcgbios.h =================================================================== --- seabios.orig/src/tcgbios.h +++ seabios/src/tcgbios.h @@ -359,6 +359,9 @@ enum ipltype { IPL_EL_TORITO_2 };
+void tcpa_interrupt_handler32(struct bregs *regs); +void _cfunc32flat_tcpa_interrupt_handler32(struct bregs *regs); + #if CONFIG_TCGBIOS void tcpa_acpi_init(void); int has_working_tpm(void); Index: seabios/src/Kconfig =================================================================== --- seabios.orig/src/Kconfig +++ seabios/src/Kconfig @@ -322,6 +322,14 @@ menu "BIOS interfaces" help Provide TPM support along with TCG BIOS extensions
+ config TPM_FOR_SHA1 + depends on TCGBIOS + bool "Use the TPM for SHA1 calculations" + default y + help + Either you may use the TPM for SHA1 calculations or + use the internal sha1 algorithm to do it (faster). + endmenu
menu "BIOS Tables"
On Wed, Mar 30, 2011 at 01:55:39PM -0400, Stefan Berger wrote:
This patch implements the TCG BIOS interrupt handler 1ah. It is for example used by trusted grub.
[...]
+/*******************************************************************
- Calculation of SHA1 in SW
- See: http://www.itl.nist.gov/fipspubs/fip180-1.htm
RFC3174, Wikipedia's SHA1 alogrithm description
- ******************************************************************/
Looks like tcgbios.c is awfully big - can we move the sha1 code to it's own file (eg, sha1.c)?
[...]
+static inline u32 rol(u32 val, u16 rol)
Should move to util.h.
+static inline u64 bswap_64(u64 val)
Same.
[...]
+++ seabios/src/stacks.c
[...]
+#ifdef CONFIG_TCGBIOS +void tcpa_interrupt_handler16(struct bregs *regs) +{
- if (MODESEGMENT) {
I'm a bit confused here - MODESEGMENT will always be true. Also, this code doesn't need to be in stacks.c - just invoke call32() directly from handle_1abb().
Also, as before, just do "if (!CONFIG_TCGBIOS) return;" instead of the #ifdef.
-Kevin
On 04/04/2011 12:30 AM, Kevin O'Connor wrote:
On Wed, Mar 30, 2011 at 01:55:39PM -0400, Stefan Berger wrote:
This patch implements the TCG BIOS interrupt handler 1ah. It is for example used by trusted grub.
[...]
+/*******************************************************************
- Calculation of SHA1 in SW
- See: http://www.itl.nist.gov/fipspubs/fip180-1.htm
RFC3174, Wikipedia's SHA1 alogrithm description
- ******************************************************************/
Looks like tcgbios.c is awfully big - can we move the sha1 code to it's own file (eg, sha1.c)?
Done.
[...]
+static inline u32 rol(u32 val, u16 rol)
Should move to util.h.
Done.
+static inline u64 bswap_64(u64 val)
Same.
Done.
[...]
+++ seabios/src/stacks.c
[...]
+#ifdef CONFIG_TCGBIOS +void tcpa_interrupt_handler16(struct bregs *regs) +{
- if (MODESEGMENT) {
I'm a bit confused here - MODESEGMENT will always be true. Also, this code doesn't need to be in stacks.c - just invoke call32() directly from handle_1abb().
Will adapt it to that. Other code was checking the MODESEGMENT and so I thought I better do that, too.
Also, as before, just do "if (!CONFIG_TCGBIOS) return;" instead of the #ifdef.
Did that.
Stefan
-Kevin
This patch adds invocactions of functions that measure various parts of the code and data through various parts of the BIOS code. It follows TCG specifications on what needs to be measured. It also adds the implementation of the called functions.
Reference for what needs to be measured can be found in section 3.2.2++ in
http://www.trustedcomputinggroup.org/resources/pc_client_work_group_specific...
The first measurements are done once the ACPI tables have been initialized. The whole 0xe and 0xf segment are measure for measuring the 'POST'.
Once booted into Linux, the current measurements produce the following logs which can be found in /sys/kernel/security/tpm0/ascii_bios_measurements. The below log also shows measurements from trusted grub.
0 0f9a2ad992c04a24e081b2a49b984616c4358386 01 [POST CODE] 1 3fb240d2a04085a4e84f81e4398e070ed5a18163 06 [SMBIOS] 2 cc812353fc277c1fab99e0b721752a1392984566 06 [Option ROM] 2 9dbd87163112e5670378abe4510491259a61f411 05 [Start Option ROM Scan] 2 6f74e357331b8dee11bbad85f27bc66cb873106c 06 [Option ROM] 2 5626eb7ac05c7231e46d7461e7d3839b03ae9fad 06 [Option ROM] 4 c1e25c3f6b0dc78d57296aa2870ca6f782ccf80f 05 [Calling INT 19h] 0 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 1 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 2 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 3 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 4 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 5 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 6 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 7 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 4 8cf2fe6c87d4d0b2998a43da630292e6d85ee8b6 05 [Booting BCV device 80h (HDD)] 4 5dff94459a3e2d13a433ef94afdc306144565bf7 0d [IPL] 5 d1b33afde65ad47502332af957c60f20c84c1edc 0e [IPL Partition Data] 4 487ce764b527ccad17f1d04243d0136fa981e6c4 0d [IPL] 4 91d285e4dead566324c8938a3cc75803f462d9a1 0d [IPL] 4 8ba79ac98bb491524fef29defc724daaf6263d35 0d [IPL] 4 c591c15b82e4ff30e7383a4ff1ef3b41b38521ac 06 [] 4 8cdc27ec545eda33fbba1e8b8dae4da5c7206972 04 [Grub Event Separator] 5 8cdc27ec545eda33fbba1e8b8dae4da5c7206972 04 [Grub Event Separator] 5 e8673b9e14b02dc12d8ccfd0176bca7a3de7fc3c 0e [IPL Partition Data] 5 0163e375a0af7525c5dac1a8e74b277359e40d1d 1105 [] 8 4be30f67c3d48ab7f04d9c0fd07f06d4c68379be 1205 [] 8 54c83965978de9708d026016ecb0e70660e04388 1305 [] 5 2431ed60130faeaf3a045f21963f71cacd46a029 04 [OS Event Separator] 8 2431ed60130faeaf3a045f21963f71cacd46a029 04 [OS Event Separator] 8 f3973cae05d6e2055062119d6e6e1e077b7df876 1005 []
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- src/boot.c | 5 src/cdrom.c | 10 + src/optionroms.c | 4 src/post.c | 5 src/tcgbios.c | 397 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 36 ++++ 6 files changed, 457 insertions(+)
Index: seabios/src/post.c =================================================================== --- seabios.orig/src/post.c +++ seabios/src/post.c @@ -190,6 +190,9 @@ init_hw(void) void VISIBLE32FLAT startBoot(void) { + tcpa_calling_int19h(); + tcpa_add_event_separators(); + // Clear low-memory allocations (required by PMM spec). memset((void*)BUILD_STACK_ADDR, 0, BUILD_EBDA_MINIMUM - BUILD_STACK_ADDR);
@@ -242,6 +245,8 @@ maininit(void) // Initialize tpm (after acpi tables were written) tcpa_acpi_init(); tcpa_startup(); + tcpa_measure_post((void *)0xE0000, (void *)0xFFFFF); + 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(FLATPTR_TO_SEG(rom), len); return 1; }
@@ -385,6 +387,8 @@ optionrom_setup(void) memset(sources, 0, sizeof(sources)); u32 post_vga = RomEnd;
+ tcpa_start_option_rom_scan(); + if (CONFIG_OPTIONROMS_DEPLOYED) { // Option roms are already deployed on the system. u32 pos = RomEnd; Index: seabios/src/boot.c =================================================================== --- seabios.orig/src/boot.c +++ seabios/src/boot.c @@ -533,6 +533,9 @@ boot_disk(u8 bootdrv, int checksig) } }
+ tcpa_add_bootdevice(0, bootdrv); + tcpa_ipl(IPL_BCV, bootseg, 0, 512); /* specs: 8.2.3 steps 4 and 5 */ + /* Canonicalize bootseg:bootip */ u16 bootip = (bootseg & 0x0fff) << 4; bootseg &= 0xf000; @@ -620,6 +623,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, GET_SEG(SS), (u32)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, 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 @@ -58,6 +58,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)
@@ -1236,4 +1238,399 @@ 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; +} + + +static u32 +tcpa_add_pcpes_to_log(const struct pcpes *pcpes) +{ + struct hleeo hleeo; + struct hleei_short hleei = { + .ipblength = sizeof(hleei), + .pcrindex = pcpes->pcrindex, + .logdataptr = pcpes, + .logdatalen = sizeof(pcpes), + }; + + return hash_log_extend_event(&hleei, &hleeo); +} + + +/* + * 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, + 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 (!has_working_tpm()) + return 0; + + return tcpa_add_measurement(4, EV_ACTION, + "Calling INT 19h"); +} + + +u32 +tcpa_returned_via_int18h(void) +{ + if (!has_working_tpm()) + return 0; + + 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 (!has_working_tpm()) + return 0; + + 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 (!has_working_tpm()) + return 0; + + 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 (!has_working_tpm()) + return 0; + + return tcpa_add_measurement(2, EV_ACTION, + "Start Option ROM Scan"); +} + + +/* + * Add measurement to the log about an option rom + */ +u32 +tcpa_option_rom(u16 seg, u32 len) +{ + u32 rc; + struct pcctes_romex pcctes = { + .eventid = 7, /* 10.4.2.3.7 */ + .eventdatasize = sizeof(u16) + sizeof(u16) + SHA1_BUFSIZE, + }; + + if (!has_working_tpm()) + return 0; + + rc = sha1((const u8 *)MAKE_FLATPTR(seg, 0), len, pcctes.digest); + if (rc) + return rc; + + return tcpa_add_measurement_to_log(2, + EV_EVENT_TAG, + (const char *)&pcctes, + sizeof(pcctes)); +} + + +static struct smbios_entry_point * +find_smbios_entry_point(void) +{ + void *smbios = (void *)0xf0000; + + while (smbios <(void *) 0x100000) { + if (memcmp(smbios, "_SM_", 4) == 0 && + calc_checksum(smbios, + sizeof(struct smbios_entry_point)) == 0) + return smbios; + smbios += 0x10; + } + + return NULL; +} + + +u32 +tcpa_smbios_measure(void) +{ + u32 rc; + struct pcctes pcctes = { + .eventid = 1, /* 10.4.2.3.1 */ + .eventdatasize = SHA1_BUFSIZE, + }; + struct smbios_entry_point *sep = find_smbios_entry_point(); + + if (!has_working_tpm()) + return 0; + +#ifdef DEBUG_TCGBIOS + dprintf(1, "TCGBIOS: SMBIOS at %p\n", sep); +#endif + 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 + * seg : segment where the IPL data are located + * off : offset where the IPL data are located + * count : length in bytes + */ +u32 +tcpa_ipl(enum ipltype bootcd, u16 seg, u16 off, u32 count) +{ + u32 rc; + u8 *addr = (u8 *)MAKE_FLATPTR(seg, off); + + if (!has_working_tpm()) + return 0; + + 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, + count); + 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, + count); + 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; +} + + +u32 +tcpa_measure_post(void *from, void *to) +{ + u32 rc = 0; + int len = to - from; + struct pcpes pcpes = { + .eventtype = EV_POST_CODE, + .eventdatasize = 0, + .pcrindex = 0, + }; + + if (!has_working_tpm()) + return 0; + + if (len > 0) { + rc = sha1((u8 *)from, to - from + 1, pcpes.digest); + if (rc) + return rc; + + rc = tcpa_add_pcpes_to_log(&pcpes); + } else { + rc = TCG_FIRMWARE_ERROR; + } + + return rc; +} + + #endif /* CONFIG_TCGBIOS */ Index: seabios/src/tcgbios.h =================================================================== --- seabios.orig/src/tcgbios.h +++ seabios/src/tcgbios.h @@ -368,6 +368,15 @@ 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, u16 seg, u16 off, u32 count); +u32 tcpa_start_option_rom_scan(void); +u32 tcpa_option_rom(u16 seg, u32 len); +u32 tcpa_smbios_measure(void); +u32 tcpa_measure_post(void *from, void *to); #else static inline void tcpa_acpi_init(void) { } @@ -383,6 +392,33 @@ static inline u32 tcpa_leave_bios(void) static inline u32 tcpa_s3_resume(void) { return 0; } +static inline u32 tcpa_calling_int19h(void) { + return 0; +} +static inline u32 tcpa_returned_via_int18h(void) { + return 0; +} +static inline u32 tcpa_add_bootdevice(u32 bootcd, u32 bootdrv) { + return 0; +} +static inline u32 tcpa_add_event_separators(void) { + return 0; +} +static inline u32 tcpa_ipl(enum ipltype bootcd, u16 seg, u16 off, u32 count) { + return 0; +} +static inline u32 tcpa_start_option_rom_scan(void) { + return 0; +} +static inline u32 tcpa_option_rom(u16 seg, u32 len) { + return 0; +} +static inline u32 tcpa_smbios_measure(void) { + return 0; +} +static inline u32 tcpa_measure_post(void *from, void *to) { + return 0; +} #endif void tcpa_menu(void);
On Wed, Mar 30, 2011 at 01:55:40PM -0400, Stefan Berger wrote:
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.
[...]
void VISIBLE32FLAT startBoot(void) {
- tcpa_calling_int19h();
- tcpa_add_event_separators();
Why add two functions here, instead of just one function that does both actions?
[...]
// Initialize tpm (after acpi tables were written) tcpa_acpi_init(); tcpa_startup();
- tcpa_measure_post((void *)0xE0000, (void *)0xFFFFF);
- tcpa_smbios_measure();
Same here. Also, I'm not sure I understand why you're measuring 0xE0000-0xFFFFF - if the intent is to measure the code, then it's a bit more complicated as the init code has already been relocated by this point.
[...]
- tcpa_option_rom(FLATPTR_TO_SEG(rom), len);
You're better off just defining tcpa_option_rom() to take a regular pointer.
[...]
- /* 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, GET_SEG(SS), (u32)buffer, 2048);
Same here for tcpa_ipl(). GET_SEG() always returns 0 in 32bit mode and this code is only run in 32bit mode.
[...]
+static struct smbios_entry_point * +find_smbios_entry_point(void) +{
The smbios table is built in smbios.c:smbios_entry_point_init() - instead of scanning for the table, just record where the table is in a global variable.
[...]
+u32 +tcpa_smbios_measure(void) +{
- u32 rc;
- struct pcctes pcctes = {
.eventid = 1, /* 10.4.2.3.1 */
.eventdatasize = SHA1_BUFSIZE,
- };
- struct smbios_entry_point *sep = find_smbios_entry_point();
- if (!has_working_tpm())
return 0;
BTW - SeaBIOS uses C99, so it's fine to move "if (!has_working_tpm())" above the variable declarations.
-Kevin
On 04/04/2011 12:57 AM, Kevin O'Connor wrote:
On Wed, Mar 30, 2011 at 01:55:40PM -0400, Stefan Berger wrote:
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.
[...]
void VISIBLE32FLAT startBoot(void) {
- tcpa_calling_int19h();
- tcpa_add_event_separators();
Why add two functions here, instead of just one function that does both actions?
I kept them separate since they're two different 'events', which would allow to put another call in between, if another event was to be measured - currently there is none. I'll combine the calls.
[...]
// Initialize tpm (after acpi tables were written) tcpa_acpi_init(); tcpa_startup();
- tcpa_measure_post((void *)0xE0000, (void *)0xFFFFF);
- tcpa_smbios_measure();
Same here. Also, I'm not sure I understand why you're measuring 0xE0000-0xFFFFF - if the intent is to measure the code, then it's a bit more complicated as the init code has already been relocated by this point.
Basically, I wanted to start the measuring as early as possible, ideally measuring layers or functions sequentially, which turns out to be quite 'tricky'. So this was the brute force compromise. I'll drop it and will start with the smbios measurement for now.
[...]
- tcpa_option_rom(FLATPTR_TO_SEG(rom), len);
You're better off just defining tcpa_option_rom() to take a regular pointer.
Done.
[...]
- /* 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, GET_SEG(SS), (u32)buffer, 2048);
Same here for tcpa_ipl(). GET_SEG() always returns 0 in 32bit mode and this code is only run in 32bit mode.
Done.
[...]
+static struct smbios_entry_point * +find_smbios_entry_point(void) +{
The smbios table is built in smbios.c:smbios_entry_point_init() - instead of scanning for the table, just record where the table is in a global variable.
Done.
[...]
+u32 +tcpa_smbios_measure(void) +{
- u32 rc;
- struct pcctes pcctes = {
.eventid = 1, /* 10.4.2.3.1 */
.eventdatasize = SHA1_BUFSIZE,
- };
- struct smbios_entry_point *sep = find_smbios_entry_point();
- if (!has_working_tpm())
return 0;
BTW - SeaBIOS uses C99, so it's fine to move "if (!has_working_tpm())" above the variable declarations.
I am moving all these checks above the var decls.
Stefan
-Kevin
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.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- src/boot.c | 10 - src/tcgbios.c | 576 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 584 insertions(+), 2 deletions(-)
Index: seabios/src/tcgbios.c =================================================================== --- seabios.orig/src/tcgbios.c +++ seabios/src/tcgbios.c @@ -1,4 +1,4 @@ -/* +;/* * Implementation of the TCG BIOS extension according to the specification * described in * https://www.trustedcomputinggroup.org/specs/PCClient/TCG_PCClientImplementat... @@ -95,6 +95,11 @@ static tcpa_state_t tcpa_state = {
extern struct tpm_driver tpm_drivers[];
+typedef struct { + u16 revision; + u8 op; +} tpm_bios_cfg_t; +
/******************************************************** Extensions for TCG-enabled BIOS @@ -1633,4 +1638,573 @@ tcpa_measure_post(void *from, void *to) }
+ +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); + +#ifdef DEBUG_TCGBIOS + printf("Return code from TSC_PhysicalPresence(CMD_ENABLE) = 0x%08x\n", + returnCode); +#endif + if (rc || returnCode) + goto err_exit; + + rc = build_and_send_cmd(TPM_ORD_PhysicalPresence, + PhysicalPresence_PRESENT, + sizeof(PhysicalPresence_PRESENT), + NULL, 10, &returnCode); + +#ifdef DEBUG_TCGBIOS + printf("Return code from TSC_PhysicalPresence(PRESENT) = 0x%08x\n", + returnCode); +#endif + if (rc || returnCode) + goto err_exit; + + return 0; + +err_exit: +#ifdef DEBUG_TCGBIOS + dprintf(1,"TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); +#endif + 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); + +#ifdef DEBUG_TCGBIOS + dprintf(1, "TCGBIOS: Return code from TPM_GetCapability() = 0x%08x\n", + returnCode); +#endif + + if (rc || returnCode) + goto err_exit; + + memcpy(buf, &pf.perm_flags, buf_len); + + return 0; + +err_exit: +#ifdef DEBUG_TCGBIOS + dprintf(1,"TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); +#endif + 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); + +#ifdef DEBUG_TCGBIOS + dprintf(1, "TCGBIOS: Return code from TPM_GetCapability() = 0x%08x\n", + returnCode); +#endif + + if (rc || returnCode) + goto err_exit; + + *has_owner = oauth.flag; + + return 0; + +err_exit: +#ifdef DEBUG_TCGBIOS + dprintf(1,"TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); +#endif + 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) { +#ifdef DEBUG_TCGBIOS + dprintf(1, "TCGBIOS: Asserting physical presence " + "failed.\n"); +#endif + return rc; + } + + rc = build_and_send_cmd(disable ? TPM_ORD_PhysicalDisable + : TPM_ORD_PhysicalEnable, + NULL, 0, NULL, 10, &returnCode); +#ifdef DEBUG_TCGBIOS + printf("Return code from TPM_Physical%sable = 0x%08x\n", + disable ? "Dis" : "En", returnCode); +#endif + if (rc || returnCode) + goto err_exit; + + + return 0; + +err_exit: +#ifdef DEBUG_TCGBIOS + dprintf(1, "TCGBIOS: %sabling the TPM failed.\n", + disable ? "Dis" : "En"); + dprintf(1,"TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); +#endif + 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) { +#ifdef DEBUG_TCGBIOS + dprintf(1, "TCGBIOS: Asserting physical presence " + "failed.\n"); +#endif + 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); +#ifdef DEBUG_TCGBIOS + printf("Return code from PhysicalSetDeactivated(%d) = 0x%08x\n", + deactivate ? 1 : 0, returnCode); +#endif + 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: +#ifdef DEBUG_TCGBIOS + dprintf(1,"TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); +#endif + 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) { +#ifdef DEBUG_TCGBIOS + dprintf(1, "TCGBIOS: Enabling/activating the TPM " + "failed.\n"); +#endif + return rc; + } + } + + rc = assert_physical_presence(); + if (rc) { +#ifdef DEBUG_TCGBIOS + dprintf(1, "TCGBIOS: Asserting physical presence " + "failed.\n"); +#endif + return rc; + } + + rc = build_and_send_cmd(TPM_ORD_ForceClear, + NULL, 0, NULL, 10, &returnCode); +#ifdef DEBUG_TCGBIOS + printf("Return code from TPM_ForceClear() = 0x%08x\n", returnCode); +#endif + 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: +#ifdef DEBUG_TCGBIOS + dprintf(1,"TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); +#endif + 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) { +#ifdef DEBUG_TCGBIOS + dprintf(1, "TCGBIOS: Asserting physical presence failed.\n"); +#endif + return rc; + } + + rc = build_and_send_cmd(TPM_ORD_SetOwnerInstall, + (allow) ? CommandFlag_TRUE : + CommandFlag_FALSE, + sizeof(CommandFlag_TRUE), + NULL, 10, &returnCode); +#ifdef DEBUG_TCGBIOS + printf("Return code from TPM_SetOwnerInstall() = 0x%08x\n", + returnCode); +#endif + if (rc || returnCode) + goto err_exit; + + if (verbose) + printf("Installation of owner %s.\n", allow ? "enabled" : "disabled"); + + return 0; + +err_exit: +#ifdef DEBUG_TCGBIOS + dprintf(1,"TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); +#endif + 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; + +#ifdef DEBUG_TCGBIOS + printf("TCGBIOS: cfg rev = %d, op = %d\n", cfg->revision, cfg->op); +#endif + if (cfg->revision < 1) + return 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(0, 0, verbose); + break; + + case 6: + rc = enable_activate(1, verbose); + break; + + case 7: + rc = deactivate_tpm(1, 1, verbose); + if (!rc) + rc = disable_tpm(1, verbose); + break; + + case 8: + rc = set_owner_install(1, verbose); + break; + + case 9: + rc = set_owner_install(0, verbose); + break; + + case 10: + rc = enable_activate(1, verbose); + if (!rc) + rc = set_owner_install(1, verbose); + break; + + case 11: + rc = set_owner_install(0, verbose); + if (!rc) { + rc = deactivate_tpm(1, 1, verbose); + if (!rc) + rc = disable_tpm(1, verbose); + } + break; + + case 14: + rc = force_clear(0, 1, verbose); + break; + + case 21: + rc = force_clear(1, 0, verbose); + break; + + case 22: + rc = force_clear(1, 1, verbose); + break; + + default: + break; + } + + if (rc) + printf("Op %d: An error occurred: 0x%x\n", cfg->op, rc); + + return rc; +} + + +void +tcpa_menu(void) +{ + int show_menu = 1; + int scan_code; + u32 rc; + tpm_bios_cfg_t cfg = { + .revision = 1, + .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 ... 5: + cfg.op = scan_code - 1; + break; + case 6: /* force clear */ + cfg.op = 21; + break; + case 7 ... 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; + } +} + #endif /* CONFIG_TCGBIOS */ Index: seabios/src/boot.c =================================================================== --- seabios.orig/src/boot.c +++ seabios/src/boot.c @@ -364,11 +364,19 @@ interactive_bootmenu(void) while (get_keystroke(0) >= 0) ;
- printf("Press F12 for boot menu.\n\n"); +again: + printf("Press F12 for boot menu.\n"); + if (has_working_tpm()) + printf("Press F11 to TPM menu.\n"); + printf("\n");
enable_bootsplash(); int scan_code = get_keystroke(CONFIG_BOOTMENU_WAIT); disable_bootsplash(); + if (has_working_tpm() && scan_code == 0x85) { + tcpa_menu(); + goto again; + } if (scan_code != 0x86) /* not F12 */ return;
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%.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
--- Makefile | 2 src/Kconfig | 7 src/tcgbios.c | 34 +- src/tis_test.c | 846 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tis_test.h | 52 +++ 5 files changed, 932 insertions(+), 9 deletions(-)
Index: seabios/Makefile =================================================================== --- seabios.orig/Makefile +++ seabios/Makefile @@ -20,7 +20,7 @@ SRC16=$(SRCBOTH) system.c disk.c font.c SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \ acpi.c smm.c mptable.c smbios.c pciinit.c optionroms.c mtrr.c \ lzmadecode.c bootsplash.c jpeg.c usb-hub.c paravirt.c dev-i440fx.c \ - pci_region.c tcgbios.c tpm_drivers.c + pci_region.c tcgbios.c tpm_drivers.c tis_test.c SRC32SEG=util.c output.c pci.c pcibios.c apm.c stacks.c
cc-option = $(shell if test -z "`$(1) $(2) -S -o /dev/null -xc \ Index: seabios/src/tcgbios.c =================================================================== --- seabios.orig/src/tcgbios.c +++ seabios/src/tcgbios.c @@ -33,6 +33,9 @@ #include "acpi.h" // RSDP_SIGNATURE, rsdt_descriptor #include "smbios.h" // smbios_entry_point
+#ifdef CONFIG_TIS_TEST +#include "tis_test.h" +#endif
//#define DEBUG_TCGBIOS
@@ -927,6 +930,10 @@ pass_through_to_tpm(struct pttti *pttti, iovec[1].data = NULL; iovec[1].length = 0;
+#ifdef CONFIG_TIS_TEST + locty = pttti->reserved; +#endif + rc = transmit(locty, iovec, pttto->tpmopout, &resbuflen); if (rc) goto err_exit; @@ -2022,26 +2029,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) @@ -2054,6 +2064,7 @@ show_tpm_state(void) printf("and an owner cannot be installed.\n"); }
+ return state; }
@@ -2152,7 +2163,7 @@ void tcpa_menu(void) { int show_menu = 1; - int scan_code; + int scan_code, state; u32 rc; tpm_bios_cfg_t cfg = { .revision = 1, @@ -2172,9 +2183,12 @@ tcpa_menu(void) "5. Clear ownership\n" "6. Allow installation of owner\n" "7. Prevent installation of owner\n" +#ifdef 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; @@ -2194,6 +2208,10 @@ tcpa_menu(void) case 7 ... 8: cfg.op = scan_code + 1; break; +#ifdef 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,846 @@ +/* + * TIS interface tests + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) IBM Corporation, 2006,2011 + * + * Author: Stefan Berger stefanb@us.ibm.com + */ +#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" + +#ifdef CONFIG_TIS_TEST + +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; + + mssleep(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"); + } + } +} + + +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) +{ + 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(); +} + +#endif /* CONFIG_TIS_TEST */ Index: seabios/src/tis_test.h =================================================================== --- /dev/null +++ seabios/src/tis_test.h @@ -0,0 +1,52 @@ +#ifndef TIS_TEST +#define TIS_TEST + +#include "tcgbios.h" + +#define TPM_ORD_PCR_Reset 0x000000c8 +#define TPM_ORD_PcrRead 0x00000015 + +void tis_test(int tpm_state); + +struct tpm_req_pcrread { + TPM_REQ_HEADER + u32 pcrindex; +} PACKED; + +struct tpm_rsp_pcrread { + TPM_RSP_HEADER + u8 digest[SHA1_BUFSIZE]; +} PACKED; + +struct pttti_pcrread { + struct pttti pttti; + struct tpm_req_pcrread req; +} PACKED; + +struct pttto_pcrread { + struct pttto pttto; + struct tpm_rsp_pcrread rsp; +} PACKED; + + +struct tpm_req_pcrreset { + TPM_REQ_HEADER + u16 sizeOfSelect; + u8 pcrSelect[3]; +} PACKED; + +struct tpm_rsp_pcrreset { + TPM_RSP_HEADER +} PACKED; + +struct pttti_pcrreset { + struct pttti pttti; + struct tpm_req_pcrreset req; +} PACKED; + +struct pttto_pcrreset { + struct pttto pttto; + struct tpm_rsp_pcrreset rsp; +} PACKED; + +#endif /* TIS_TEST */ Index: seabios/src/Kconfig =================================================================== --- seabios.orig/src/Kconfig +++ seabios/src/Kconfig @@ -330,6 +330,13 @@ menu "BIOS interfaces" Either you may use the TPM for SHA1 calculations or use the internal sha1 algorithm to do it (faster).
+ config TIS_TEST + depends on TCGBIOS + bool "TPM TIS test" + default n + help + Test cases for the TPM TIS interface + endmenu
menu "BIOS Tables"