This is a repost of a series of patches providing TPM support to SeaBIOS.
As an addition, this patch series now works on the Acer C720 Chromebook with limitations (S3 not getting invoked; no logging into TCPA table).
The patch series cleanly applies to a checkout of tags/rel-1.7.5.
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
All implementations necessarily follow specifications.
When all patches are applied the following services are available - SSDT ACPI table for TPM support - initialization of the TPM upon VM start and S3 resume - Static root of trust for measurements (SRTM) that measures (some) data of SeaBIOS in TCPA ACPI table - 1ah interrupt handler offering APIs for measuring and sending commands to the TPM (trusted grub uses them) - User menu for controlling aspects of the state of the TPM
Stefan Berger (8): Add an implementation of a TPM TIS driver Provide ACPI SSDT table for TPM device + S3 resume support Implementation of the TCG BIOS extensions Support for BIOS interrupt handler Add 'measurement' code to the BIOS Add a menu for TPM control Add a menu item for displaying TPM diagnostics Make the TPM menu work on a Chromebook (Acer C720)
Makefile | 12 +- src/Kconfig | 15 + src/boot.c | 28 +- src/cdrom.c | 10 + src/clock.c | 12 + src/config.h | 1 + src/fw/acpi-tpm-ssdt.dsl | 24 + src/fw/acpi-tpm-ssdt.hex | 27 + src/fw/acpi.c | 41 + src/hw/tpm_drivers.c | 273 ++++++ src/hw/tpm_drivers.h | 91 ++ src/optionroms.c | 4 + src/post.c | 9 + src/resume.c | 2 + src/sha1.c | 145 +++ src/sha1.h | 8 + src/std/acpi.h | 20 + src/tcgbios.c | 2208 ++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 431 +++++++++ src/util.h | 32 + 20 files changed, 3388 insertions(+), 5 deletions(-) create mode 100644 src/fw/acpi-tpm-ssdt.dsl create mode 100644 src/fw/acpi-tpm-ssdt.hex create mode 100644 src/hw/tpm_drivers.c create mode 100644 src/hw/tpm_drivers.h create mode 100644 src/sha1.c create mode 100644 src/sha1.h create mode 100644 src/tcgbios.c create mode 100644 src/tcgbios.h
This patch adds an implementation of a TPM TIS driver for the TPM TIS emulation supported by Qemu (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.
v7: - moving declaration of tpm_drivers[] into tpm_drivers.h
v6: - reworked timeouts; not hardcoded anymore
v5: - introducing a configurable threashold as part of the driver interface structure below which the TPM is used for calculating the sha1
v2: - adapted tpm_drivers.c to be under LGPLv3
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com --- src/hw/tpm_drivers.c | 259 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/hw/tpm_drivers.h | 90 ++++++++++++++++++ 2 files changed, 349 insertions(+) create mode 100644 src/hw/tpm_drivers.c create mode 100644 src/hw/tpm_drivers.h
diff --git a/src/hw/tpm_drivers.c b/src/hw/tpm_drivers.c new file mode 100644 index 0000000..3d42fe8 --- /dev/null +++ b/src/hw/tpm_drivers.c @@ -0,0 +1,259 @@ +// Implementation of a TPM driver for the TPM TIS interface +// +// Copyright (C) 2006-2011 IBM Corporation +// +// Authors: +// Stefan Berger stefanb@linux.vnet.ibm.com +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "config.h" +#include "malloc.h" // malloc_low +#include "string.h" +#include "util.h" +#include "x86.h" // readl +#include "hw/tpm_drivers.h" +#include "tcgbios.h" + +static const u32 tis_default_timeouts[4] = { + TIS_DEFAULT_TIMEOUT_A, + TIS_DEFAULT_TIMEOUT_B, + TIS_DEFAULT_TIMEOUT_C, + TIS_DEFAULT_TIMEOUT_D, +}; + +static const u32 tpm_default_durations[3] = { + TPM_DEFAULT_DURATION_SHORT, + TPM_DEFAULT_DURATION_MEDIUM, + TPM_DEFAULT_DURATION_LONG, +}; + +/* determined values */ +static u32 tpm_default_dur[3]; +static u32 tpm_default_to[4]; + + +/* if device is not there, return '0', '1' otherwise */ +static u32 tis_probe(void) +{ + u32 rc = 0; + u32 didvid = readl(TIS_REG(0, TIS_REG_DID_VID)); + + if ((didvid != 0) && (didvid != 0xffffffff)) + rc = 1; + + return rc; +} + +static u32 tis_init(void) +{ + writeb(TIS_REG(0, TIS_REG_INT_ENABLE), 0); + + if (tpm_drivers[TIS_DRIVER_IDX].durations == NULL) { + u32 *durations = tpm_default_dur; + memcpy(durations, tpm_default_durations, + sizeof(tpm_default_durations)); + tpm_drivers[TIS_DRIVER_IDX].durations = durations; + } + + if (tpm_drivers[TIS_DRIVER_IDX].timeouts == NULL) { + u32 *timeouts = tpm_default_to; + memcpy(timeouts, tis_default_timeouts, + sizeof(tis_default_timeouts)); + tpm_drivers[TIS_DRIVER_IDX].timeouts = timeouts; + } + + return 1; +} + + +static void set_timeouts(u32 timeouts[4], u32 durations[3]) +{ + u32 *tos = tpm_drivers[TIS_DRIVER_IDX].timeouts; + u32 *dus = tpm_drivers[TIS_DRIVER_IDX].durations; + + if (tos && tos != tis_default_timeouts && timeouts) + memcpy(tos, timeouts, 4 * sizeof(u32)); + if (dus && dus != tpm_default_durations && durations) + memcpy(dus, durations, 3 * sizeof(u32)); +} + + +static u32 tis_wait_sts(u8 locty, u32 time, u8 mask, u8 expect) +{ + u32 rc = 1; + + while (time > 0) { + u8 sts = readb(TIS_REG(locty, TIS_REG_STS)); + if ((sts & mask) == expect) { + rc = 0; + break; + } + msleep(1); + time--; + } + return rc; +} + +static u32 tis_activate(u8 locty) +{ + u32 rc = 0; + u8 acc; + int l; + u32 timeout_a = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_A]; + + if (!(readb(TIS_REG(locty, TIS_REG_ACCESS)) & + TIS_ACCESS_ACTIVE_LOCALITY)) { + /* release locality in use top-downwards */ + for (l = 4; l >= 0; l--) + writeb(TIS_REG(l, TIS_REG_ACCESS), + TIS_ACCESS_ACTIVE_LOCALITY); + } + + /* request access to locality */ + writeb(TIS_REG(locty, TIS_REG_ACCESS), TIS_ACCESS_REQUEST_USE); + + acc = readb(TIS_REG(locty, TIS_REG_ACCESS)); + if ((acc & TIS_ACCESS_ACTIVE_LOCALITY)) { + writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY); + rc = tis_wait_sts(locty, timeout_a, + TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY); + } + + return rc; +} + +static u32 tis_find_active_locality(void) +{ + u8 locty; + + for (locty = 0; locty <= 4; locty++) { + if ((readb(TIS_REG(locty, TIS_REG_ACCESS)) & + TIS_ACCESS_ACTIVE_LOCALITY)) + return locty; + } + + tis_activate(0); + + return 0; +} + +static u32 tis_ready(void) +{ + u32 rc = 0; + u8 locty = tis_find_active_locality(); + u32 timeout_b = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_B]; + + writeb(TIS_REG(locty, TIS_REG_STS), TIS_STS_COMMAND_READY); + rc = tis_wait_sts(locty, timeout_b, + TIS_STS_COMMAND_READY, TIS_STS_COMMAND_READY); + + return rc; +} + +static u32 tis_senddata(const u8 *const data, u32 len) +{ + u32 rc = 0; + u32 offset = 0; + u32 end = 0; + u16 burst = 0; + u32 ctr = 0; + u8 locty = tis_find_active_locality(); + u32 timeout_d = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_D]; + + do { + while (burst == 0 && ctr < timeout_d) { + burst = readl(TIS_REG(locty, TIS_REG_STS)) >> 8; + if (burst == 0) { + msleep(1); + ctr++; + } + } + + if (burst == 0) { + rc = TCG_RESPONSE_TIMEOUT; + break; + } + + while (1) { + writeb(TIS_REG(locty, TIS_REG_DATA_FIFO), data[offset++]); + burst--; + + if (burst == 0 || offset == len) + break; + } + + if (offset == len) + end = 1; + } while (end == 0); + + return rc; +} + +static u32 tis_readresp(u8 *buffer, u32 *len) +{ + u32 rc = 0; + u32 offset = 0; + u32 sts; + u8 locty = tis_find_active_locality(); + + while (offset < *len) { + buffer[offset] = readb(TIS_REG(locty, TIS_REG_DATA_FIFO)); + offset++; + sts = readb(TIS_REG(locty, TIS_REG_STS)); + /* data left ? */ + if ((sts & TIS_STS_DATA_AVAILABLE) == 0) + break; + } + + *len = offset; + + return rc; +} + + +static u32 tis_waitdatavalid(void) +{ + u32 rc = 0; + u8 locty = tis_find_active_locality(); + u32 timeout_c = tpm_drivers[TIS_DRIVER_IDX].timeouts[TIS_TIMEOUT_TYPE_C]; + + if (tis_wait_sts(locty, timeout_c, TIS_STS_VALID, TIS_STS_VALID) != 0) + rc = TCG_NO_RESPONSE; + + return rc; +} + +static u32 tis_waitrespready(enum tpmDurationType to_t) +{ + u32 rc = 0; + u8 locty = tis_find_active_locality(); + u32 timeout = tpm_drivers[TIS_DRIVER_IDX].durations[to_t]; + + writeb(TIS_REG(locty ,TIS_REG_STS), TIS_STS_TPM_GO); + + if (tis_wait_sts(locty, timeout, + TIS_STS_DATA_AVAILABLE, TIS_STS_DATA_AVAILABLE) != 0) + rc = TCG_NO_RESPONSE; + + return rc; +} + + +struct tpm_driver tpm_drivers[TPM_NUM_DRIVERS] = { + [TIS_DRIVER_IDX] = + { + .timeouts = NULL, + .durations = NULL, + .set_timeouts = set_timeouts, + .probe = tis_probe, + .init = tis_init, + .activate = tis_activate, + .ready = tis_ready, + .senddata = tis_senddata, + .readresp = tis_readresp, + .waitdatavalid = tis_waitdatavalid, + .waitrespready = tis_waitrespready, + .sha1threshold = 100 * 1024, + }, +}; diff --git a/src/hw/tpm_drivers.h b/src/hw/tpm_drivers.h new file mode 100644 index 0000000..34bb12d --- /dev/null +++ b/src/hw/tpm_drivers.h @@ -0,0 +1,90 @@ +#ifndef TPM_DRIVERS_H +#define TPM_DRIVERS_H + +#include "types.h" // u32 + + +enum tpmDurationType { + TPM_DURATION_TYPE_SHORT = 0, + TPM_DURATION_TYPE_MEDIUM, + TPM_DURATION_TYPE_LONG, +}; + +/* low level driver implementation */ +struct tpm_driver { + u32 *timeouts; + u32 *durations; + void (*set_timeouts)(u32 timeouts[4], u32 durations[3]); + u32 (*probe)(void); + u32 (*init)(void); + u32 (*activate)(u8 locty); + u32 (*ready)(void); + u32 (*senddata)(const u8 *const data, u32 len); + u32 (*readresp)(u8 *buffer, u32 *len); + u32 (*waitdatavalid)(void); + u32 (*waitrespready)(enum tpmDurationType to_t); + /* the TPM will be used for buffers of sizes below the sha1threshold + for calculating the hash */ + u32 sha1threshold; +}; + +extern struct tpm_driver tpm_drivers[]; + + +#define TIS_DRIVER_IDX 0 +#define TPM_NUM_DRIVERS 1 + +#define TPM_INVALID_DRIVER -1 + +/* TIS driver */ +/* address of locality 0 (TIS) */ +#define TPM_TIS_BASE_ADDRESS 0xfed40000 + +#define TIS_REG(LOCTY, REG) \ + (void *)(TPM_TIS_BASE_ADDRESS + (LOCTY << 12) + REG) + +/* hardware registers */ +#define TIS_REG_ACCESS 0x0 +#define TIS_REG_INT_ENABLE 0x8 +#define TIS_REG_INT_VECTOR 0xc +#define TIS_REG_INT_STATUS 0x10 +#define TIS_REG_INTF_CAPABILITY 0x14 +#define TIS_REG_STS 0x18 +#define TIS_REG_DATA_FIFO 0x24 +#define TIS_REG_DID_VID 0xf00 +#define TIS_REG_RID 0xf04 + +#define TIS_STS_VALID (1 << 7) /* 0x80 */ +#define TIS_STS_COMMAND_READY (1 << 6) /* 0x40 */ +#define TIS_STS_TPM_GO (1 << 5) /* 0x20 */ +#define TIS_STS_DATA_AVAILABLE (1 << 4) /* 0x10 */ +#define TIS_STS_EXPECT (1 << 3) /* 0x08 */ +#define TIS_STS_RESPONSE_RETRY (1 << 1) /* 0x02 */ + +#define TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) /* 0x80 */ +#define TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) /* 0x20 */ +#define TIS_ACCESS_BEEN_SEIZED (1 << 4) /* 0x10 */ +#define TIS_ACCESS_SEIZE (1 << 3) /* 0x08 */ +#define TIS_ACCESS_PENDING_REQUEST (1 << 2) /* 0x04 */ +#define TIS_ACCESS_REQUEST_USE (1 << 1) /* 0x02 */ +#define TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) /* 0x01 */ + +#define SCALER 10 + +#define TIS_DEFAULT_TIMEOUT_A (750 * SCALER) +#define TIS_DEFAULT_TIMEOUT_B (2000 * SCALER) +#define TIS_DEFAULT_TIMEOUT_C (750 * SCALER) +#define TIS_DEFAULT_TIMEOUT_D (750 * SCALER) + +enum tisTimeoutType { + TIS_TIMEOUT_TYPE_A = 0, + TIS_TIMEOUT_TYPE_B, + TIS_TIMEOUT_TYPE_C, + TIS_TIMEOUT_TYPE_D, +}; + +#define TPM_DEFAULT_DURATION_SHORT (2000 * SCALER) +#define TPM_DEFAULT_DURATION_MEDIUM (20000 * SCALER) +#define TPM_DEFAULT_DURATION_LONG (60000 * SCALER) + +#endif /* TPM_DRIVERS_H */
This patch 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.
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.
v6: - following Andreas Niederl's suggestion: enclosing Device(TPM) in Scope(_SB) { ... } to have Linux 2.6.33 recognize the device properly; seems to work fine with 3.0.0
v2: - Increasing the CONFIG_MAX_HIGHTABLE to 96kb - Adding cut-down tcgbios.c|h to keep SeaBIOS compiling - Build tpm_drivers.c and tcgbios.c -> TPM's SSDT and TCPA tables are now visible in Linux
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com --- Makefile | 12 +++++-- src/fw/acpi-tpm-ssdt.dsl | 24 ++++++++++++++ src/fw/acpi-tpm-ssdt.hex | 27 ++++++++++++++++ src/fw/acpi.c | 41 ++++++++++++++++++++++++ src/std/acpi.h | 20 ++++++++++++ src/tcgbios.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 57 ++++++++++++++++++++++++++++++++++ 7 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 src/fw/acpi-tpm-ssdt.dsl create mode 100644 src/fw/acpi-tpm-ssdt.hex create mode 100644 src/tcgbios.c create mode 100644 src/tcgbios.h
diff --git a/Makefile b/Makefile index 78b598e..08b05c0 100644 --- a/Makefile +++ b/Makefile @@ -38,11 +38,12 @@ SRCBOTH=misc.c stacks.c output.c string.c x86.c block.c cdrom.c mouse.c kbd.c \ hw/lsi-scsi.c hw/esp-scsi.c hw/megasas.c SRC16=$(SRCBOTH) system.c disk.c font.c SRC32FLAT=$(SRCBOTH) post.c memmap.c malloc.c pmm.c romfile.c optionroms.c \ - boot.c bootsplash.c jpeg.c bmp.c \ + boot.c bootsplash.c jpeg.c bmp.c tcgbios.c \ hw/ahci.c hw/pvscsi.c hw/usb-xhci.c hw/usb-hub.c \ fw/coreboot.c fw/lzmadecode.c fw/csm.c fw/biostables.c \ fw/paravirt.c fw/shadow.c fw/pciinit.c fw/smm.c fw/mtrr.c fw/xen.c \ - fw/acpi.c fw/mptable.c fw/pirtable.c fw/smbios.c fw/romfile_loader.c + fw/acpi.c fw/mptable.c fw/pirtable.c fw/smbios.c fw/romfile_loader.c \ + hw/tpm_drivers.c SRC32SEG=string.c output.c pcibios.c apm.c stacks.c hw/pci.c hw/serialio.c DIRS=src src/hw src/fw vgasrc
@@ -241,6 +242,13 @@ $(OUT)vgabios.bin: $(OUT)vgabios.bin.raw scripts/buildrom.py iasl-option=$(shell if test -z "`$(1) $(2) 2>&1 > /dev/null`" \ ; then echo "$(2)"; else echo "$(3)"; fi ;)
+src/fw/acpi-tpm-ssdt.hex: src/fw/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/' $@ + $(OUT)%.hex: %.dsl ./scripts/acpi_extract_preprocess.py ./scripts/acpi_extract.py @echo " Compiling IASL $@" $(Q)$(CPP) $(CPPFLAGS) $< -o $(OUT)$*.dsl.i.orig diff --git a/src/fw/acpi-tpm-ssdt.dsl b/src/fw/acpi-tpm-ssdt.dsl new file mode 100644 index 0000000..080bae4 --- /dev/null +++ b/src/fw/acpi-tpm-ssdt.dsl @@ -0,0 +1,24 @@ +DefinitionBlock ( + "acpi-tpm-ssdt.aml",// Output Filename + "SSDT", // Signature + 0x01, // SSDT Compliance Revision + "BXPC", // OEMID + "BXSSDT", // TABLE ID + 0x1 // OEM Revision + ) +{ + Scope(_SB) { + /* TPM with emulated TPM TIS interface */ + Device (TPM) { + Name (_HID, EisaID ("PNP0C31")) + Name (_CRS, ResourceTemplate () + { + Memory32Fixed (ReadWrite, 0xFED40000, 0x00005000) + //IRQNoFlags () {5} + }) + Method (_STA, 0, NotSerialized) { + Return (0x0F) + } + } + } +} diff --git a/src/fw/acpi-tpm-ssdt.hex b/src/fw/acpi-tpm-ssdt.hex new file mode 100644 index 0000000..acbb78f --- /dev/null +++ b/src/fw/acpi-tpm-ssdt.hex @@ -0,0 +1,27 @@ +/* + * + * Intel ACPI Component Architecture + * ASL Optimizing Compiler version 20101013-64 [Nov 21 2010] + * Copyright (c) 2000 - 2010 Intel Corporation + * + * Compilation of "out/.dsl.i" - Mon Jan 30 16:03:18 2012 + * + * C source code output + * AML code block contains 0x5D bytes + * + */ +unsigned char AmlCode_TPM[] = +{ + 0x53,0x53,0x44,0x54,0x5D,0x00,0x00,0x00, /* 00000000 "SSDT]..." */ + 0x01,0x15,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,0x10,0x38,0x5C,0x5F, /* 00000020 "... .8_" */ + 0x53,0x42,0x5F,0x5B,0x82,0x30,0x54,0x50, /* 00000028 "SB_[.0TP" */ + 0x4D,0x5F,0x08,0x5F,0x48,0x49,0x44,0x0C, /* 00000030 "M_._HID." */ + 0x41,0xD0,0x0C,0x31,0x08,0x5F,0x43,0x52, /* 00000038 "A..1._CR" */ + 0x53,0x11,0x11,0x0A,0x0E,0x86,0x09,0x00, /* 00000040 "S......." */ + 0x01,0x00,0x00,0xD4,0xFE,0x00,0x50,0x00, /* 00000048 "......P." */ + 0x00,0x79,0x00,0x14,0x09,0x5F,0x53,0x54, /* 00000050 ".y..._ST" */ + 0x41,0x00,0xA4,0x0A,0x0F /* 00000058 "A...." */ +}; diff --git a/src/fw/acpi.c b/src/fw/acpi.c index 733ca4d..a6c63a7 100644 --- a/src/fw/acpi.c +++ b/src/fw/acpi.c @@ -20,8 +20,10 @@ #include "string.h" // memset #include "util.h" // MaxCountCPUs #include "x86.h" // readl +#include "tcgbios.h" // detected_tpm
#include "src/fw/acpi-dsdt.hex" +#include "acpi-tpm-ssdt.hex"
static void build_header(struct acpi_table_header *h, u32 sig, int len, u8 rev) @@ -590,6 +592,39 @@ static const struct pci_device_id acpi_find_tbl[] = { 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 (detected_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; +} + #define MAX_ACPI_TABLES 20 void acpi_setup(void) @@ -664,6 +699,12 @@ acpi_setup(void) build_header(dsdt, DSDT_SIGNATURE, sizeof(AmlCode), 1); }
+ void *tcpa, *tpm; + if (add_tpm_device(&tpm, &tcpa)) + return; + ACPI_INIT_TABLE(tpm); + ACPI_INIT_TABLE(tcpa); + // Build final rsdt table struct rsdt_descriptor_rev1 *rsdt; size_t rsdt_len = sizeof(*rsdt) + sizeof(u32) * tbl_idx; diff --git a/src/std/acpi.h b/src/std/acpi.h index fad6ac2..813e9ad 100644 --- a/src/std/acpi.h +++ b/src/std/acpi.h @@ -269,4 +269,24 @@ struct acpi_table_mcfg { struct acpi_mcfg_allocation allocation[0]; } PACKED;
+ +struct rsdt_descriptor { + ACPI_TABLE_HEADER_DEF + u32 entry[1]; +} PACKED; + +#define TCPA_SIGNATURE 0x41504354 +struct tcpa_descriptor_rev2 +{ + ACPI_TABLE_HEADER_DEF + u16 platform_class; + u32 laml; + u64 lasa; +} PACKED; + +/* TCPA ACPI definitions */ +#define TCPA_ACPI_CLASS_CLIENT 0 +#define TCPA_ACPI_CLASS_SERVER 1 + + #endif // acpi.h diff --git a/src/tcgbios.c b/src/tcgbios.c new file mode 100644 index 0000000..e4f0d46 --- /dev/null +++ b/src/tcgbios.c @@ -0,0 +1,81 @@ +// Implementation of the TCG BIOS extension according to the specification +// described in +// https://www.trustedcomputinggroup.org/specs/PCClient/TCG_PCClientImplementat... +// +// Copyright (C) 2006-2011 IBM Corporation +// +// Authors: +// Stefan Berger stefanb@linux.vnet.ibm.com +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + + +#include "config.h" + +#include "types.h" +#include "hw/tpm_drivers.h" // tpm_drivers[] + + +typedef struct { + u8 tpm_probed:1; + u8 tpm_found:1; + u8 tpm_working:1; + u8 if_shutdown:1; + u8 tpm_driver_to_use:4; +} tcpa_state_t; + + +static tcpa_state_t tcpa_state = { + .tpm_driver_to_use = TPM_INVALID_DRIVER, +}; + + +/******************************************************** + 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; +} + +static void +probe_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; + } +} + +int +detected_tpm(void) +{ + probe_tpm(); + + return tcpa_state.tpm_found; +} + +static int +has_working_tpm(void) +{ + probe_tpm(); + + return tcpa_state.tpm_working; +} diff --git a/src/tcgbios.h b/src/tcgbios.h new file mode 100644 index 0000000..ed3f781 --- /dev/null +++ b/src/tcgbios.h @@ -0,0 +1,57 @@ +#ifndef TCGBIOS_H +#define TCGBIOS_H + + +#define TPM_OK 0x0 +#define TPM_RET_BASE 0x1 +#define TCG_GENERAL_ERROR (TPM_RET_BASE + 0x0) +#define TCG_TPM_IS_LOCKED (TPM_RET_BASE + 0x1) +#define TCG_NO_RESPONSE (TPM_RET_BASE + 0x2) +#define TCG_INVALID_RESPONSE (TPM_RET_BASE + 0x3) +#define TCG_INVALID_ACCESS_REQUEST (TPM_RET_BASE + 0x4) +#define TCG_FIRMWARE_ERROR (TPM_RET_BASE + 0x5) +#define TCG_INTEGRITY_CHECK_FAILED (TPM_RET_BASE + 0x6) +#define TCG_INVALID_DEVICE_ID (TPM_RET_BASE + 0x7) +#define TCG_INVALID_VENDOR_ID (TPM_RET_BASE + 0x8) +#define TCG_UNABLE_TO_OPEN (TPM_RET_BASE + 0x9) +#define TCG_UNABLE_TO_CLOSE (TPM_RET_BASE + 0xa) +#define TCG_RESPONSE_TIMEOUT (TPM_RET_BASE + 0xb) +#define TCG_INVALID_COM_REQUEST (TPM_RET_BASE + 0xc) +#define TCG_INVALID_ADR_REQUEST (TPM_RET_BASE + 0xd) +#define TCG_WRITE_BYTE_ERROR (TPM_RET_BASE + 0xe) +#define TCG_READ_BYTE_ERROR (TPM_RET_BASE + 0xf) +#define TCG_BLOCK_WRITE_TIMEOUT (TPM_RET_BASE + 0x10) +#define TCG_CHAR_WRITE_TIMEOUT (TPM_RET_BASE + 0x11) +#define TCG_CHAR_READ_TIMEOUT (TPM_RET_BASE + 0x12) +#define TCG_BLOCK_READ_TIMEOUT (TPM_RET_BASE + 0x13) +#define TCG_TRANSFER_ABORT (TPM_RET_BASE + 0x14) +#define TCG_INVALID_DRV_FUNCTION (TPM_RET_BASE + 0x15) +#define TCG_OUTPUT_BUFFER_TOO_SHORT (TPM_RET_BASE + 0x16) +#define TCG_FATAL_COM_ERROR (TPM_RET_BASE + 0x17) +#define TCG_INVALID_INPUT_PARA (TPM_RET_BASE + 0x18) +#define TCG_TCG_COMMAND_ERROR (TPM_RET_BASE + 0x19) +#define TCG_INTERFACE_SHUTDOWN (TPM_RET_BASE + 0x20) +//define TCG_PC_UNSUPPORTED (TPM_RET_BASE + 0x21) +#define TCG_PC_TPM_NOT_PRESENT (TPM_RET_BASE + 0x22) +#define TCG_PC_TPM_DEACTIVATED (TPM_RET_BASE + 0x23) + + +#define TPM_INVALID_ADR_REQUEST TCG_INVALID_ADR_REQUEST +#define TPM_IS_LOCKED TCG_TPM_IS_LOCKED +#define TPM_INVALID_DEVICE_ID TCG_INVALID_DEVICE_ID +#define TPM_INVALID_VENDOR_ID TCG_INVALID_VENDOR_ID +//define TPM_RESERVED_REG_INVALID +#define TPM_FIRMWARE_ERROR TCG_FIRMWARE_ERROR +#define TPM_UNABLE_TO_OPEN TCG_UNABLE_TO_OPEN +#define TPM_UNABLE_TO_CLOSE TCG_UNABLE_TO_CLOSE +#define TPM_INVALID_RESPONSE TCG_INVALID_RESPONSE +#define TPM_RESPONSE_TIMEOUT TCG_RESPONSE_TIMEOUT +#define TPM_INVALID_ACCESS_REQUEST TCG_INVALID_ACCESS_REQUEST +#define TPM_TRANSFER_ABORT TCG_TRANSFER_ABORT +#define TPM_GENERAL_ERROR TCG_GENERAL_ERROR + + +int detected_tpm(void); + + +#endif /* TCGBIOS_H */
On Wed, Jul 02, 2014 at 11:38:46AM -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.
Thanks, but at this point we don't want to make changes to the QEMU specific ACPI tables compiled into SeaBIOS. SeaBIOS now gets the tables from QEMU, and enhancements should go there.
-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 - enable configuration of SeaBIOS to be built with TCGBIOS extensions depending on COREBOOT not being selected All TCG BIOS extensions are activated with CONFIG_TCGBIOS.
Structures that are needed in subsequent patches are also included in tcgbios.h at this point.
The effect of this patch is that it initialized the TPM upon VM start and S3 resume.
v6: - passing durations of commands to the transmission function - acquire timeouts and durations from TPM and use them
v5: - adding the lock flag to the 'not present' Physcial_presence_NOT_PRESENT structure
v4: - return TCG_GENERAL_ERROR if ! has_working_tpm()
v3: - upon S3 resume call timer_setup()
v2: - replace mssleep() with calls to msleep() - Moving Kconfig patch to this file - converting code to call dprintf(DEBUG_tcg, ...) - use the get_rsdp call to get hold of the RSDP - use util.c:checksum() - Adapting tcgbios.c to be under LGPLv3 - using if (!CONFIG_TCGBIOS) everywhere
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com --- src/Kconfig | 7 + src/boot.c | 2 + src/config.h | 1 + src/hw/tpm_drivers.c | 4 + src/post.c | 5 + src/resume.c | 2 + src/tcgbios.c | 480 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 326 ++++++++++++++++++++++++++++++++++ 8 files changed, 827 insertions(+)
diff --git a/src/Kconfig b/src/Kconfig index a863866..9e65449 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -411,6 +411,13 @@ menu "BIOS interfaces" modified by programs. However, some old DOS high memory managers may require the UMB region to be read-only.
+ config TCGBIOS + select S3_RESUME + bool "TPM support and TCG BIOS extensions" + default y + help + Provide TPM support along with TCG BIOS extensions + endmenu
menu "BIOS Tables" diff --git a/src/boot.c b/src/boot.c index 133e206..f36f3d6 100644 --- a/src/boot.c +++ b/src/boot.c @@ -19,6 +19,7 @@ #include "std/disk.h" // struct mbr_s #include "string.h" // memset #include "util.h" // irqtimer_calc +#include "tcgbios.h" // tcpa_*
/**************************************************************** @@ -475,6 +476,7 @@ interactive_bootmenu(void)
printf("Select boot device:\n\n"); wait_threads(); + tcpa_leave_bios();
// Show menu items int maxmenu = 0; diff --git a/src/config.h b/src/config.h index d705615..efaf669 100644 --- a/src/config.h +++ b/src/config.h @@ -103,5 +103,6 @@ #define DEBUG_unimplemented 2 #define DEBUG_invalid 3 #define DEBUG_thread 2 +#define DEBUG_tcg 20
#endif // config.h diff --git a/src/hw/tpm_drivers.c b/src/hw/tpm_drivers.c index 3d42fe8..2b45a94 100644 --- a/src/hw/tpm_drivers.c +++ b/src/hw/tpm_drivers.c @@ -7,6 +7,8 @@ // // This file may be distributed under the terms of the GNU LGPLv3 license.
+#if CONFIG_TCGBIOS == 1 + #include "config.h" #include "malloc.h" // malloc_low #include "string.h" @@ -257,3 +259,5 @@ struct tpm_driver tpm_drivers[TPM_NUM_DRIVERS] = { .sha1threshold = 100 * 1024, }, }; + +#endif /* CONFIG_TCGBIOS == 1 */ diff --git a/src/post.c b/src/post.c index 0fdd28e..1cb7304 100644 --- a/src/post.c +++ b/src/post.c @@ -28,6 +28,7 @@ #include "output.h" // dprintf #include "string.h" // memset #include "util.h" // kbd_init +#include "tcgbios.h" // tcpa_*
/**************************************************************** @@ -219,6 +220,10 @@ maininit(void) if (threads_during_optionroms()) device_hardware_setup();
+ // Initialize tpm (after acpi tables were written) + tcpa_acpi_init(); + tcpa_startup(); + // Run vga option rom vgarom_setup();
diff --git a/src/resume.c b/src/resume.c index 1903174..ef643e3 100644 --- a/src/resume.c +++ b/src/resume.c @@ -16,6 +16,7 @@ #include "std/bda.h" // struct bios_data_area_s #include "string.h" // memset #include "util.h" // dma_setup +#include "tcgbios.h" // tcpa_s3_resume
// Handler for post calls that look like a resume. void VISIBLE16 @@ -108,6 +109,7 @@ s3_resume(void) memset(&br, 0, sizeof(br)); dprintf(1, "Jump to resume vector (%x)\n", s3_resume_vector); br.code = FLATPTR_TO_SEGOFF((void*)s3_resume_vector); + tcpa_s3_resume(); farcall16big(&br); }
diff --git a/src/tcgbios.c b/src/tcgbios.c index e4f0d46..b3f65f6 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -13,7 +13,62 @@ #include "config.h"
#include "types.h" +#include "byteorder.h" // cpu_to_* #include "hw/tpm_drivers.h" // tpm_drivers[] +#include "farptr.h" // MAKE_FLATPTR +#include "string.h" // checksum +#include "tcgbios.h"// tcpa_*, prototypes +#include "util.h" // printf, get_keystroke +#include "output.h" // dprintf +#include "std/acpi.h" // RSDP_SIGNATURE, rsdt_descriptor + + +static const u8 Startup_ST_CLEAR[2] = { 0x00, TPM_ST_CLEAR }; +static const u8 Startup_ST_STATE[2] = { 0x00, TPM_ST_STATE }; + +static const u8 PhysicalPresence_CMD_ENABLE[2] = { 0x00, 0x20 }; +static const u8 PhysicalPresence_CMD_DISABLE[2] = { 0x01, 0x00 }; +static const u8 PhysicalPresence_PRESENT[2] = { 0x00, 0x08 }; +static const u8 PhysicalPresence_NOT_PRESENT_LOCK[2] = { 0x00, 0x14 }; + +static const u8 CommandFlag_FALSE[1] = { 0x00 }; +static const u8 CommandFlag_TRUE[1] = { 0x01 }; + +static const u8 GetCapability_Permanent_Flags[12] = { + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x08 +}; + +static const u8 GetCapability_OwnerAuth[12] = { + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x11 +}; + +static const u8 GetCapability_Timeouts[] = { + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x15 +}; + +static const u8 GetCapability_Durations[] = { + 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x20 +}; + + +#define RSDP_CAST(ptr) ((struct rsdp_descriptor *)ptr) + + +/* helper functions */ + +static inline void *input_buf32(struct bregs *regs) +{ + return MAKE_FLATPTR(regs->es, regs->di); +} + +static inline void *output_buf32(struct bregs *regs) +{ + return MAKE_FLATPTR(regs->ds, regs->si); +}
typedef struct { @@ -67,6 +122,9 @@ probe_tpm(void) int detected_tpm(void) { + if (!CONFIG_TCGBIOS) + return 0; + probe_tpm();
return tcpa_state.tpm_found; @@ -79,3 +137,425 @@ has_working_tpm(void)
return tcpa_state.tpm_working; } + +static struct tcpa_descriptor_rev2 * +find_tcpa_by_rsdp(struct rsdp_descriptor *rsdp) +{ + u32 ctr = 0; + struct tcpa_descriptor_rev2 *tcpa = NULL; + struct rsdt_descriptor *rsdt; + u32 length; + u16 off; + + rsdt = (struct rsdt_descriptor *)rsdp->rsdt_physical_address; + if (!rsdt) + return NULL; + + length = rsdt->length; + off = offsetof(struct rsdt_descriptor, entry); + + while ((off + sizeof(rsdt->entry[0])) <= length) { + /* try all pointers to structures */ + tcpa = (struct tcpa_descriptor_rev2 *)(int)rsdt->entry[ctr]; + + /* valid TCPA ACPI table ? */ + if (tcpa->signature == TCPA_SIGNATURE && + checksum((u8 *)tcpa, tcpa->length) == 0) + break; + + tcpa = NULL; + off += sizeof(rsdt->entry[0]); + ctr++; + } + + return tcpa; +} + + +static struct tcpa_descriptor_rev2 * +find_tcpa_table(void) +{ + struct tcpa_descriptor_rev2 *tcpa = NULL; + struct rsdp_descriptor *rsdp = RsdpAddr; + + if (rsdp) + tcpa = find_tcpa_by_rsdp(rsdp); + else + tcpa_state.if_shutdown = 1; + + if (!rsdp) + dprintf(DEBUG_tcg, + "TCGBIOS: RSDP was NOT found! -- Disabling interface.\n"); + else if (!tcpa) + dprintf(DEBUG_tcg, "TCGBIOS: TCPA ACPI was NOT found!\n"); + + return tcpa; +} + + +static u8 * +get_lasa_base_ptr(u32 *laml) +{ + u8 *lasa = 0; + struct tcpa_descriptor_rev2 *tcpa = find_tcpa_table(); + + if (tcpa) { + lasa = (u8 *)(long)tcpa->lasa; + if (laml) + *laml = tcpa->laml; + } + + return lasa; +} + + +/* clear the ACPI log */ +static void +reset_acpi_log(void) +{ + u32 laml; + u8 *lasa = get_lasa_base_ptr(&laml); + + if (lasa) + memset(lasa, 0x0, laml); +} + + +/* + initialize the TCPA ACPI subsystem; find the ACPI tables and determine + where the TCPA table is. + */ +void +tcpa_acpi_init(void) +{ + if (!CONFIG_TCGBIOS) + return; + + tcpa_state.if_shutdown = 0; + tcpa_state.tpm_probed = 0; + tcpa_state.tpm_found = 0; + tcpa_state.tpm_working = 0; + + if (!has_working_tpm()) { + tcpa_state.if_shutdown = 1; + return; + } + + reset_acpi_log(); +} + + +static u32 +transmit(u8 locty, const struct iovec iovec[], + u8 *respbuffer, u32 *respbufferlen, + enum tpmDurationType to_t) +{ + 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(to_t); + if (irc != 0) + return TCG_FATAL_COM_ERROR; + + irc = td->readresp(respbuffer, + respbufferlen); + if (irc != 0) + return TCG_FATAL_COM_ERROR; + + td->ready(); + + return rc; +} + + +/* + * Send a TPM command with the given ordinal. Append the given buffer + * containing all data in network byte order to the command (this is + * the custom part per command) and expect a response of the given size. + * If a buffer is provided, the response will be copied into it. + */ +static u32 +build_and_send_cmd_od(u32 ordinal, const u8 *append, u32 append_size, + u8 *resbuffer, u32 return_size, u32 *returnCode, + const u8 *otherdata, u32 otherdata_size, + enum tpmDurationType to_t) +{ +#define MAX_APPEND_SIZE 12 +#define MAX_RESPONSE_SIZE sizeof(struct tpm_res_getcap_perm_flags) + u32 rc; + u8 ibuffer[TPM_REQ_HEADER_SIZE + MAX_APPEND_SIZE]; + u8 obuffer[MAX_RESPONSE_SIZE]; + struct tpm_req_header *trqh = (struct tpm_req_header *)ibuffer; + struct tpm_rsp_header *trsh = (struct tpm_rsp_header *)obuffer; + u8 locty = 0; + struct iovec iovec[3]; + u32 obuffer_len = sizeof(obuffer); + u32 idx = 1; + + if (append_size > MAX_APPEND_SIZE || + return_size > MAX_RESPONSE_SIZE) { + dprintf(DEBUG_tcg, "TCGBIOS: size of requested buffers too big."); + return TCG_FIRMWARE_ERROR; + } + + iovec[0].data = trqh; + iovec[0].length = TPM_REQ_HEADER_SIZE + append_size; + + if (otherdata) { + iovec[1].data = (void *)otherdata; + iovec[1].length = otherdata_size; + idx = 2; + } + + iovec[idx].data = NULL; + iovec[idx].length = 0; + + memset(ibuffer, 0x0, sizeof(ibuffer)); + memset(obuffer, 0x0, sizeof(obuffer)); + + trqh->tag = cpu_to_be16(0xc1); + trqh->totlen = cpu_to_be32(TPM_REQ_HEADER_SIZE + append_size + otherdata_size); + trqh->ordinal = cpu_to_be32(ordinal); + + if (append_size) + memcpy((char *)trqh + sizeof(*trqh), + append, append_size); + + rc = transmit(locty, iovec, obuffer, &obuffer_len, to_t); + if (rc) + return rc; + + *returnCode = be32_to_cpu(trsh->errcode); + + if (resbuffer) + memcpy(resbuffer, trsh, return_size); + + return 0; +} + + +static u32 +build_and_send_cmd(u32 ordinal, const u8 *append, u32 append_size, + u8 *resbuffer, u32 return_size, u32 *returnCode, + enum tpmDurationType to_t) +{ + return build_and_send_cmd_od(ordinal, append, append_size, + resbuffer, return_size, returnCode, + NULL, 0, to_t); +} + + +static u32 +determine_timeouts(void) +{ + u32 rc; + u32 returnCode; + struct tpm_res_getcap_timeouts timeouts; + struct tpm_res_getcap_durations durations; + struct tpm_driver *td = &tpm_drivers[tcpa_state.tpm_driver_to_use]; + u32 i; + + rc = build_and_send_cmd(TPM_ORD_GetCapability, + GetCapability_Timeouts, + sizeof(GetCapability_Timeouts), + (u8 *)&timeouts, + sizeof(struct tpm_res_getcap_timeouts), + &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "TCGBIOS: Return code from TPM_GetCapability(Timeouts)" + " = 0x%08x\n", returnCode); + + if (rc || returnCode) + goto err_exit; + + rc = build_and_send_cmd(TPM_ORD_GetCapability, + GetCapability_Durations, + sizeof(GetCapability_Durations), + (u8 *)&durations, + sizeof(struct tpm_res_getcap_durations), + &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "TCGBIOS: Return code from TPM_GetCapability(Durations)" + " = 0x%08x\n", returnCode); + + if (rc || returnCode) + goto err_exit; + + for (i = 0; i < 3; i++) + durations.durations[i] = be32_to_cpu(durations.durations[i]); + + for (i = 0; i < 4; i++) + timeouts.timeouts[i] = be32_to_cpu(timeouts.timeouts[i]); + + dprintf(DEBUG_tcg, "TCGBIOS: timeouts: %u %u %u %u\n", + timeouts.timeouts[0], + timeouts.timeouts[1], + timeouts.timeouts[2], + timeouts.timeouts[3]); + + dprintf(DEBUG_tcg, "TCGBIOS: durations: %u %u %u\n", + durations.durations[0], + durations.durations[1], + durations.durations[2]); + + + td->set_timeouts(timeouts.timeouts, durations.durations); + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tcpa_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +u32 +tcpa_startup(void) +{ + u32 rc; + u32 returnCode; + + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + dprintf(DEBUG_tcg, "TCGBIOS: Starting with TPM_Startup(ST_CLEAR)\n"); + rc = build_and_send_cmd(TPM_ORD_Startup, + Startup_ST_CLEAR, sizeof(Startup_ST_CLEAR), + NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "Return code from TPM_Startup = 0x%08x\n", + returnCode); + + if (rc || returnCode) + goto err_exit; + + rc = build_and_send_cmd(TPM_ORD_SelfTestFull, NULL, 0, + NULL, 10, &returnCode, TPM_DURATION_TYPE_LONG); + + dprintf(DEBUG_tcg, "Return code from TPM_SelfTestFull = 0x%08x\n", + returnCode); + + if (rc || returnCode) + goto err_exit; + + rc = determine_timeouts(); + if (rc) + goto err_exit; + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tcpa_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +u32 +tcpa_leave_bios(void) +{ + u32 rc; + u32 returnCode; + + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + rc = build_and_send_cmd(TPM_ORD_PhysicalPresence, + PhysicalPresence_CMD_ENABLE, + sizeof(PhysicalPresence_CMD_ENABLE), + NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT); + if (rc || returnCode) + goto err_exit; + + rc = build_and_send_cmd(TPM_ORD_PhysicalPresence, + PhysicalPresence_NOT_PRESENT_LOCK, + sizeof(PhysicalPresence_NOT_PRESENT_LOCK), + NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT); + if (rc || returnCode) + goto err_exit; + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tcpa_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +u32 +tcpa_s3_resume(void) +{ + u32 rc; + u32 returnCode; + + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + timer_setup(); + + dprintf(DEBUG_tcg, "TCGBIOS: Resuming with TPM_Startup(ST_STATE)\n"); + + rc = build_and_send_cmd(TPM_ORD_Startup, + Startup_ST_STATE, sizeof(Startup_ST_STATE), + NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "TCGBIOS: ReturnCode from TPM_Startup = 0x%08x\n", + returnCode); + + if (rc || returnCode) + goto err_exit; + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tcpa_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} diff --git a/src/tcgbios.h b/src/tcgbios.h index ed3f781..461e141 100644 --- a/src/tcgbios.h +++ b/src/tcgbios.h @@ -1,6 +1,22 @@ #ifndef TCGBIOS_H #define TCGBIOS_H
+#include "types.h" +#include "bregs.h" /* struct bregs */ + +#define TCG_MAGIC 0x41504354L + +/* Define for section 12.3 */ +#define TCG_PC_OK 0x0 +#define TCG_PC_TPMERROR 0x1 +#define TCG_PC_LOGOVERFLOW 0x2 +#define TCG_PC_UNSUPPORTED 0x3 + +#define TPM_ALG_SHA 0x4 + +#define TCG_MAGIC 0x41504354L +#define TCG_VERSION_MAJOR 1 +#define TCG_VERSION_MINOR 2
#define TPM_OK 0x0 #define TPM_RET_BASE 0x1 @@ -51,7 +67,317 @@ #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_getcap_timeouts { + TPM_RSP_HEADER + u32 size; + u32 timeouts[4]; +} PACKED; + + +struct tpm_res_getcap_durations { + TPM_RSP_HEADER + u32 size; + u32 durations[3]; +} PACKED; + + +struct tpm_res_sha1start { + TPM_RSP_HEADER + u32 max_num_bytes; +} PACKED; + + +struct tpm_res_sha1complete { + TPM_RSP_HEADER + u8 hash[20]; +} PACKED; + +struct pttti_extend { + struct pttti pttti; + struct tpm_req_extend req; +} PACKED; + + +struct pttto_extend { + struct pttto pttto; + struct tpm_rsp_extend rsp; +} PACKED; + + +enum ipltype { + IPL_BCV = 0, + IPL_EL_TORITO_1, + IPL_EL_TORITO_2 +}; + +void tcpa_acpi_init(void); int detected_tpm(void); +u32 tcpa_startup(void); +u32 tcpa_leave_bios(void); +u32 tcpa_s3_resume(void);
#endif /* TCGBIOS_H */
On Wed, Jul 02, 2014 at 11:38:47AM -0400, Stefan Berger wrote:
This patch implements the main part of the TCG BIOS extensions. It provides the following functionality:
- initialization of the TCPA ACPI table used for logging of measurements
- initialization of the TPM by sending a sequence of commands to it
- proper setup of the TPM once the BIOS hands over control to the bootloader
- support for S3 resume; BIOS sends TPM_Startup(ST_STATE) to TPM
- enable configuration of SeaBIOS to be built with TCGBIOS extensions depending on COREBOOT not being selected All TCG BIOS extensions are activated with CONFIG_TCGBIOS.
Structures that are needed in subsequent patches are also included in tcgbios.h at this point.
The effect of this patch is that it initialized the TPM upon VM start and S3 resume.
v6:
- passing durations of commands to the transmission function
- acquire timeouts and durations from TPM and use them
v5:
- adding the lock flag to the 'not present' Physcial_presence_NOT_PRESENT structure
v4:
- return TCG_GENERAL_ERROR if ! has_working_tpm()
v3:
- upon S3 resume call timer_setup()
v2:
- replace mssleep() with calls to msleep()
- Moving Kconfig patch to this file
- converting code to call dprintf(DEBUG_tcg, ...)
- use the get_rsdp call to get hold of the RSDP
- use util.c:checksum()
- Adapting tcgbios.c to be under LGPLv3
- using if (!CONFIG_TCGBIOS) everywhere
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
src/Kconfig | 7 + src/boot.c | 2 + src/config.h | 1 + src/hw/tpm_drivers.c | 4 + src/post.c | 5 + src/resume.c | 2 + src/tcgbios.c | 480 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 326 ++++++++++++++++++++++++++++++++++ 8 files changed, 827 insertions(+)
diff --git a/src/Kconfig b/src/Kconfig index a863866..9e65449 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -411,6 +411,13 @@ menu "BIOS interfaces" modified by programs. However, some old DOS high memory managers may require the UMB region to be read-only.
- config TCGBIOS
select S3_RESUME
bool "TPM support and TCG BIOS extensions"
default y
help
Provide TPM support along with TCG BIOS extensions
endmenu
menu "BIOS Tables" diff --git a/src/boot.c b/src/boot.c index 133e206..f36f3d6 100644 --- a/src/boot.c +++ b/src/boot.c @@ -19,6 +19,7 @@ #include "std/disk.h" // struct mbr_s #include "string.h" // memset #include "util.h" // irqtimer_calc +#include "tcgbios.h" // tcpa_*
/**************************************************************** @@ -475,6 +476,7 @@ interactive_bootmenu(void)
printf("Select boot device:\n\n"); wait_threads();
- tcpa_leave_bios();
This is an odd place for a tcpa call. Shouldn't it go with the normal _setup() and _prepboot() calls?
--- a/src/hw/tpm_drivers.c +++ b/src/hw/tpm_drivers.c @@ -7,6 +7,8 @@ // // This file may be distributed under the terms of the GNU LGPLv3 license.
+#if CONFIG_TCGBIOS == 1
We try to avoid ifdefs in seabios. This should go in the start of exported functions as "if (!CONFIG_TCGBIOS) return;".
-Kevin
On 08/26/2014 10:41 AM, Kevin O'Connor wrote:
On Wed, Jul 02, 2014 at 11:38:47AM -0400, Stefan Berger wrote:
diff --git a/src/boot.c b/src/boot.c index 133e206..f36f3d6 100644 --- a/src/boot.c +++ b/src/boot.c @@ -19,6 +19,7 @@ #include "std/disk.h" // struct mbr_s #include "string.h" // memset #include "util.h" // irqtimer_calc +#include "tcgbios.h" // tcpa_*
/**************************************************************** @@ -475,6 +476,7 @@ interactive_bootmenu(void)
printf("Select boot device:\n\n"); wait_threads();
- tcpa_leave_bios();
This is an odd place for a tcpa call. Shouldn't it go with the normal _setup() and _prepboot() calls?
A few lines further up is the function call into the TPM's menu where commands are issued to the TPM under physical presence. There's a loop to allow the user to invoke that menu multiple times. In tcpa_leave_bios() we give up physical presence before we leave the BIOS for the boot loader.
--- a/src/hw/tpm_drivers.c +++ b/src/hw/tpm_drivers.c @@ -7,6 +7,8 @@ // // This file may be distributed under the terms of the GNU LGPLv3 license.
+#if CONFIG_TCGBIOS == 1
We try to avoid ifdefs in seabios. This should go in the start of exported functions as "if (!CONFIG_TCGBIOS) return;".
I did this so that the size of the created code consumes 0 bytes in case CONFIG_TCGBIOS is not set. I can certainly change this to how it is usually done in SeaBIOS.
Stefan
This patch implements the TCG BIOS interrupt handler 1ah. It is for example used by trusted grub.
This patch adds an implementation of SHA1 (following NIST specs., IETF RFC 3147 and Wikipedia) for speeding up measurements of code. Trusted Grub for example makes use of this interface and measures (calculates SHA1) of the Linux kernel and initrd. Those files can be rather large and hunting their bytes through the TIS interface as part of the int handler commands invoked by trusted grub does take quite some time due to the many vmexits the interface is creating (one per byte).
There is also a threshold for the size of data to hash (100k) below which the TPM is used and above the internal faster SHA1 algorithm is used.
This patch for example enables trusted grub to interact with the TPM and take additional measurements.
v6: - passing durations of commands to the transmission function
v5: - adapted code to use SHA1 threshold of driver - support in Kconfig for SHA1 threshold called CONFIG_TPM_TIS_SHA1THRESHOLD - using CONFIG_TPM_TIS_SHA1THRESHOLD in the tpm driver structure to set the threshold for having the TPM do the SHA1 or do it in software - fixing placement of set_cf in inthandler
v4: - return TCG_GENERAL_ERROR if ! has_working_tpm() - in the int handler return TCG_GENERAL_ERROR in eax if ! has_working_tpm() - in the inthanlder call set_cf() before the switch and then in the default case for functions that are not implemented
v3: - use if (CONFIG_TPM_FOR_SHA1)
v2: - use if (!CONFIG_TCGBIOS) everywhere - put sha1() into its own file - move rol() and bswap64() into util.h - move inthandler invocation from stacks.c into handle_1abb()
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com --- Makefile | 2 +- src/clock.c | 12 ++ src/hw/tpm_drivers.c | 11 +- src/sha1.c | 145 ++++++++++++++ src/sha1.h | 8 + src/tcgbios.c | 540 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 4 + src/util.h | 31 +++ 8 files changed, 746 insertions(+), 7 deletions(-) create mode 100644 src/sha1.c create mode 100644 src/sha1.h
diff --git a/Makefile b/Makefile index 08b05c0..8b9de2f 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ SRCBOTH=misc.c stacks.c output.c string.c x86.c block.c cdrom.c mouse.c kbd.c \ hw/lsi-scsi.c hw/esp-scsi.c hw/megasas.c SRC16=$(SRCBOTH) system.c disk.c font.c SRC32FLAT=$(SRCBOTH) post.c memmap.c malloc.c pmm.c romfile.c optionroms.c \ - boot.c bootsplash.c jpeg.c bmp.c tcgbios.c \ + boot.c bootsplash.c jpeg.c bmp.c tcgbios.c sha1.c \ hw/ahci.c hw/pvscsi.c hw/usb-xhci.c hw/usb-hub.c \ fw/coreboot.c fw/lzmadecode.c fw/csm.c fw/biostables.c \ fw/paravirt.c fw/shadow.c fw/pciinit.c fw/smm.c fw/mtrr.c fw/xen.c \ diff --git a/src/clock.c b/src/clock.c index 9ab0ac0..c8d91ff 100644 --- a/src/clock.c +++ b/src/clock.c @@ -14,6 +14,7 @@ #include "stacks.h" // yield #include "string.h" // memset #include "util.h" // clock_setup +#include "tcgbios.h" // _cfunc32flat_tcpa_interrupt_handler32
/**************************************************************** @@ -239,6 +240,16 @@ handle_1a07(struct bregs *regs) set_success(regs); }
+static void +handle_1abb(struct bregs *regs) +{ + if (!CONFIG_TCGBIOS) + return; + + dprintf(DEBUG_tcg, "16: Calling tcpa_interrupt_handler\n"); + call32(_cfunc32flat_tcpa_interrupt_handler32, (u32)regs, 0); +} + // Unsupported static void handle_1aXX(struct bregs *regs) @@ -260,6 +271,7 @@ handle_1a(struct bregs *regs) case 0x05: handle_1a05(regs); break; case 0x06: handle_1a06(regs); break; case 0x07: handle_1a07(regs); break; + case 0xbb: handle_1abb(regs); break; default: handle_1aXX(regs); break; } } diff --git a/src/hw/tpm_drivers.c b/src/hw/tpm_drivers.c index 2b45a94..1d5a779 100644 --- a/src/hw/tpm_drivers.c +++ b/src/hw/tpm_drivers.c @@ -9,13 +9,12 @@
#if CONFIG_TCGBIOS == 1
-#include "config.h" -#include "malloc.h" // malloc_low -#include "string.h" -#include "util.h" +#include "config.h" // CONFIG_TPM_TIS_SHA1THRESHOLD +#include "string.h" // memcpy +#include "util.h" // msleep #include "x86.h" // readl -#include "hw/tpm_drivers.h" -#include "tcgbios.h" +#include "hw/tpm_drivers.h" // struct tpm_driver +#include "tcgbios.h" // TCG_*
static const u32 tis_default_timeouts[4] = { TIS_DEFAULT_TIMEOUT_A, diff --git a/src/sha1.c b/src/sha1.c new file mode 100644 index 0000000..c335b95 --- /dev/null +++ b/src/sha1.c @@ -0,0 +1,145 @@ +// Support for Calculation of SHA1 in SW +// +// Copyright (C) 2006-2011 IBM Corporation +// +// Authors: +// Stefan Berger stefanb@linux.vnet.ibm.com +// +// See: http://www.itl.nist.gov/fipspubs/fip180-1.htm +// RFC3174, Wikipedia's SHA1 alogrithm description +// + +#include "config.h" +#include "byteorder.h" // cpu_to_* +#include "sha1.h" // sha1 +#include "string.h" // memcpy +#include "util.h" // rol, bswap_64 + +typedef struct _sha1_ctx { + u32 h[5]; +} sha1_ctx; + + +static void +sha1_block(u32 *w, sha1_ctx *ctx) +{ + u32 i; + u32 a,b,c,d,e,f; + u32 tmp; + u32 idx; + + static const u32 sha_ko[4] = { + 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 }; + + /* change endianess of given data */ + for (i = 0; i < 16; i++) + w[i] = be32_to_cpu(w[i]); + + for (i = 16; i <= 79; i++) { + tmp = w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]; + w[i] = rol(tmp,1); + } + + a = ctx->h[0]; + b = ctx->h[1]; + c = ctx->h[2]; + d = ctx->h[3]; + e = ctx->h[4]; + + for (i = 0; i <= 79; i++) { + if (i <= 19) { + f = (b & c) | ((b ^ 0xffffffff) & d); + idx = 0; + } else if (i <= 39) { + f = b ^ c ^ d; + idx = 1; + } else if (i <= 59) { + f = (b & c) | (b & d) | (c & d); + idx = 2; + } else { + f = b ^ c ^ d; + idx = 3; + } + + tmp = rol(a, 5) + + f + + e + + sha_ko[idx] + + w[i]; + e = d; + d = c; + c = rol(b, 30); + b = a; + a = tmp; + } + + ctx->h[0] += a; + ctx->h[1] += b; + ctx->h[2] += c; + ctx->h[3] += d; + ctx->h[4] += e; +} + + +static void +sha1_do(sha1_ctx *ctx, const u8 *data32, u32 length) +{ + u32 offset; + u16 num; + u32 bits = 0; + u32 w[80]; + u64 tmp; + + /* treat data in 64-byte chunks */ + for (offset = 0; length - offset >= 64; offset += 64) { + memcpy(w, data32 + offset, 64); + sha1_block((u32 *)w, ctx); + bits += (64 * 8); + } + + /* last block with less than 64 bytes */ + num = length - offset; + bits += (num << 3); + + memcpy(w, data32 + offset, num); + ((u8 *)w)[num] = 0x80; + if (64 - (num + 1) > 0) + memset( &((u8 *)w)[num + 1], 0x0, 64 - (num + 1)); + + if (num >= 56) { + /* cannot append number of bits here */ + sha1_block((u32 *)w, ctx); + memset(w, 0x0, 60); + } + + /* write number of bits to end of block */ + tmp = 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] = cpu_to_be32(ctx->h[num]); +} + + +u32 +sha1(const u8 *data, u32 length, u8 *hash) +{ + if (!CONFIG_TCGBIOS) + return 0; + + sha1_ctx ctx = { + .h[0] = 0x67452301, + .h[1] = 0xefcdab89, + .h[2] = 0x98badcfe, + .h[3] = 0x10325476, + .h[4] = 0xc3d2e1f0, + }; + + sha1_do(&ctx, data, length); + memcpy(hash, &ctx.h[0], 20); + + return 0; +} diff --git a/src/sha1.h b/src/sha1.h new file mode 100644 index 0000000..07aabf3 --- /dev/null +++ b/src/sha1.h @@ -0,0 +1,8 @@ +#ifndef __SHA1_H +#define __SHA1_H + +#include "types.h" // u32 + +u32 sha1(const u8 *data, u32 length, u8 *hash); + +#endif // sha1.h diff --git a/src/tcgbios.c b/src/tcgbios.c index b3f65f6..9e4f721 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -21,6 +21,7 @@ #include "util.h" // printf, get_keystroke #include "output.h" // dprintf #include "std/acpi.h" // RSDP_SIGNATURE, rsdt_descriptor +#include "sha1.h" // sha1
static const u8 Startup_ST_CLEAR[2] = { 0x00, TPM_ST_CLEAR }; @@ -522,6 +523,545 @@ err_exit: return TCG_TCG_COMMAND_ERROR; }
+static int +is_valid_pcpes(struct pcpes *pcpes) +{ + return (pcpes->eventtype != 0); +} + + +static u8 * +get_lasa_last_ptr(u16 *entry_count, u8 **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 (!is_valid_pcpes(pcpes)) + break; + if (entry_count) + (*entry_count)++; + size = pcpes->eventdatasize + offsetof(struct pcpes, event); + lasa_last = lasa_base; + lasa_base += size; + } + + if (lasa_next) + *lasa_next = lasa_base; + + return lasa_last; +} + + +static u32 +tpm_sha1_calc(const u8 *data, u32 length, u8 *hash) +{ + u32 rc; + u32 returnCode; + struct tpm_res_sha1start start; + struct tpm_res_sha1complete complete; + u32 blocks = length / 64; + u32 rest = length & 0x3f; + u32 numbytes, numbytes_no; + u32 offset = 0; + + rc = build_and_send_cmd(TPM_ORD_SHA1Start, + NULL, 0, + (u8 *)&start, + sizeof(struct tpm_res_sha1start), + &returnCode, TPM_DURATION_TYPE_SHORT); + + if (rc || returnCode) + goto err_exit; + + while (blocks > 0) { + + numbytes = be32_to_cpu(start.max_num_bytes); + if (numbytes > blocks * 64) + numbytes = blocks * 64; + + numbytes_no = cpu_to_be32(numbytes); + + rc = build_and_send_cmd_od(TPM_ORD_SHA1Update, + (u8 *)&numbytes_no, sizeof(numbytes_no), + NULL, 0, &returnCode, + &data[offset], numbytes, + TPM_DURATION_TYPE_SHORT); + + if (rc || returnCode) + goto err_exit; + + offset += numbytes; + blocks -= (numbytes / 64); + } + + numbytes_no = cpu_to_be32(rest); + + rc = build_and_send_cmd_od(TPM_ORD_SHA1Complete, + (u8 *)&numbytes_no, sizeof(numbytes_no), + (u8 *)&complete, + sizeof(struct tpm_res_sha1complete), + &returnCode, + &data[offset], rest, TPM_DURATION_TYPE_SHORT); + + if (rc || returnCode) + goto err_exit; + + memcpy(hash, complete.hash, sizeof(complete.hash)); + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM SHA1 malfunctioning.\n"); + + tcpa_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +static u32 +sha1_calc(const u8 *data, u32 length, u8 *hash) +{ + if (length < tpm_drivers[tcpa_state.tpm_driver_to_use].sha1threshold) + return tpm_sha1_calc(data, length, hash); + + return sha1(data, length, hash); +} + + +/* + * Extend the ACPI log with the given entry by copying the + * entry data into the log. + * Input + * Pointer to the structure to be copied into the log + * + * Output: + * lower 16 bits of return code contain entry number + * if entry number is '0', then upper 16 bits contain error code. + */ +static u32 +tcpa_extend_acpi_log(void *entry_ptr, u16 *entry_count) +{ + u32 laml, size; + u8 *lasa_base = get_lasa_base_ptr(&laml), *lasa_next = NULL; + struct pcpes *pcpes = (struct pcpes *)entry_ptr; + + get_lasa_last_ptr(entry_count, &lasa_next); + + dprintf(DEBUG_tcg, "TCGBIOS: LASA_BASE = %p, LASA_NEXT = %p\n",lasa_base, lasa_next); + + if (lasa_next == NULL || laml == 0) + return TCG_PC_LOGOVERFLOW; + + size = pcpes->eventdatasize + offsetof(struct pcpes, event); + + if ((lasa_next + size - lasa_base) > laml) { + dprintf(DEBUG_tcg, "TCGBIOS: LOG OVERFLOW: size = %d\n", size); + return TCG_PC_LOGOVERFLOW; + } + + memcpy(lasa_next, entry_ptr, size); + + (*entry_count)++; + + return 0; +} + + +static u32 +is_preboot_if_shutdown(void) +{ + return tcpa_state.if_shutdown; +} + + +static u32 +shutdown_preboot_interface(void) +{ + u32 rc = 0; + + if (!is_preboot_if_shutdown()) { + tcpa_state.if_shutdown = 1; + } else { + rc = TCG_INTERFACE_SHUTDOWN; + } + + return rc; +} + + +static void +tcpa_shutdown(void) +{ + reset_acpi_log(); + shutdown_preboot_interface(); +} + + +static u32 +pass_through_to_tpm(struct pttti *pttti, struct pttto *pttto) +{ + u32 rc = 0; + u32 resbuflen = 0; + struct tpm_req_header *trh; + u8 locty = 0; + struct iovec iovec[2]; + const u32 *tmp; + + if (is_preboot_if_shutdown()) { + rc = TCG_INTERFACE_SHUTDOWN; + goto err_exit; + } + + trh = (struct tpm_req_header *)pttti->tpmopin; + + if (pttti->ipblength < sizeof(struct pttti) + TPM_REQ_HEADER_SIZE || + pttti->opblength < sizeof(struct pttto) || + be32_to_cpu(trh->totlen) + sizeof(struct pttti) > pttti->ipblength ) { + rc = TCG_INVALID_INPUT_PARA; + goto err_exit; + } + + resbuflen = pttti->opblength - offsetof(struct pttto, tpmopout); + + iovec[0].data = pttti->tpmopin; + tmp = (const u32 *)&((u8 *)iovec[0].data)[2]; + iovec[0].length = cpu_to_be32(*tmp); + + iovec[1].data = NULL; + iovec[1].length = 0; + + rc = transmit(locty, iovec, pttto->tpmopout, &resbuflen, + TPM_DURATION_TYPE_LONG /* worst case */); + if (rc) + goto err_exit; + + pttto->opblength = offsetof(struct pttto, tpmopout) + resbuflen; + pttto->reserved = 0; + +err_exit: + if (rc != 0) { + pttto->opblength = 4; + pttto->reserved = 0; + } + + return rc; +} + + +static u32 +tpm_extend(u8 *hash, u32 pcrindex) +{ + u32 rc; + struct pttto_extend pttto; + struct pttti_extend pttti = { + .pttti = { + .ipblength = sizeof(struct pttti_extend), + .opblength = sizeof(struct pttto_extend), + }, + .req = { + .tag = cpu_to_be16(0xc1), + .totlen = cpu_to_be32(sizeof(pttti.req)), + .ordinal = cpu_to_be32(TPM_ORD_Extend), + .pcrindex = cpu_to_be32(pcrindex), + }, + }; + + memcpy(pttti.req.digest, hash, sizeof(pttti.req.digest)); + + rc = pass_through_to_tpm(&pttti.pttti, &pttto.pttto); + + if (rc == 0) { + if (pttto.pttto.opblength < TPM_RSP_HEADER_SIZE || + pttto.pttto.opblength != + sizeof(struct pttto) + be32_to_cpu(pttto.rsp.totlen) || + be16_to_cpu(pttto.rsp.tag) != 0xc4) { + rc = TCG_FATAL_COM_ERROR; + } + } + + if (rc) + tcpa_shutdown(); + + return rc; +} + + +static u32 +hash_all(const struct hai *hai, u8 *hash) +{ + if (is_preboot_if_shutdown() != 0) + return TCG_INTERFACE_SHUTDOWN; + + if (hai->ipblength != sizeof(struct hai) || + hai->hashdataptr == 0 || + hai->hashdatalen == 0 || + hai->algorithmid != TPM_ALG_SHA) + return TCG_INVALID_INPUT_PARA; + + return sha1_calc((const u8 *)hai->hashdataptr, hai->hashdatalen, hash); +} + + +static u32 +hash_log_event(const struct hlei *hlei, struct hleo *hleo) +{ + u32 rc = 0; + u16 size; + struct pcpes *pcpes; + u16 entry_count; + + if (is_preboot_if_shutdown() != 0) { + rc = TCG_INTERFACE_SHUTDOWN; + goto err_exit; + } + + size = hlei->ipblength; + if (size != sizeof(*hlei)) { + rc = TCG_INVALID_INPUT_PARA; + goto err_exit; + } + + pcpes = (struct pcpes *)hlei->logdataptr; + + if (pcpes->pcrindex >= 24 || + pcpes->pcrindex != hlei->pcrindex || + pcpes->eventtype != hlei->logeventtype) { + rc = TCG_INVALID_INPUT_PARA; + goto err_exit; + } + + if ((hlei->hashdataptr != 0) && (hlei->hashdatalen != 0)) { + rc = sha1_calc((const u8 *)hlei->hashdataptr, + hlei->hashdatalen, pcpes->digest); + if (rc) + return rc; + } + + rc = tcpa_extend_acpi_log((void *)hlei->logdataptr, &entry_count); + if (rc) + goto err_exit; + + /* updating the log was fine */ + hleo->opblength = sizeof(struct hleo); + hleo->reserved = 0; + hleo->eventnumber = entry_count; + +err_exit: + if (rc != 0) { + hleo->opblength = 2; + hleo->reserved = 0; + } + + return rc; +} + + +static u32 +hash_log_extend_event(const struct hleei_short *hleei_s, struct hleeo *hleeo) +{ + u32 rc = 0; + struct hleo hleo; + struct hleei_long *hleei_l = (struct hleei_long *)hleei_s; + const void *logdataptr; + u32 logdatalen; + struct pcpes *pcpes; + + /* short or long version? */ + switch (hleei_s->ipblength) { + case sizeof(struct hleei_short): + /* short */ + logdataptr = hleei_s->logdataptr; + logdatalen = hleei_s->logdatalen; + break; + + case sizeof(struct hleei_long): + /* long */ + logdataptr = hleei_l->logdataptr; + logdatalen = hleei_l->logdatalen; + break; + + default: + /* bad input block */ + rc = TCG_INVALID_INPUT_PARA; + goto err_exit; + } + + pcpes = (struct pcpes *)logdataptr; + + struct hlei hlei = { + .ipblength = sizeof(hlei), + .hashdataptr = hleei_s->hashdataptr, + .hashdatalen = hleei_s->hashdatalen, + .pcrindex = hleei_s->pcrindex, + .logeventtype= pcpes->eventtype, + .logdataptr = logdataptr, + .logdatalen = logdatalen, + }; + + rc = hash_log_event(&hlei, &hleo); + if (rc) + goto err_exit; + + hleeo->opblength = sizeof(struct hleeo); + hleeo->reserved = 0; + hleeo->eventnumber = hleo.eventnumber; + + rc = tpm_extend(pcpes->digest, hleei_s->pcrindex); + +err_exit: + if (rc != 0) { + hleeo->opblength = 4; + hleeo->reserved = 0; + } + + return rc; + +} + + +static u32 +tss(struct ti *ti, struct to *to) +{ + u32 rc = 0; + + if (is_preboot_if_shutdown() == 0) { + rc = TCG_PC_UNSUPPORTED; + } else { + rc = TCG_INTERFACE_SHUTDOWN; + } + + to->opblength = sizeof(struct to); + to->reserved = 0; + + return rc; +} + + +static u32 +compact_hash_log_extend_event(u8 *buffer, + u32 info, + u32 length, + u32 pcrindex, + u32 *edx_ptr) +{ + u32 rc = 0; + struct hleeo hleeo; + struct pcpes pcpes = { + .pcrindex = pcrindex, + .eventtype = EV_COMPACT_HASH, + .eventdatasize = sizeof(info), + .event = info, + }; + struct hleei_short hleei = { + .ipblength = sizeof(hleei), + .hashdataptr = buffer, + .hashdatalen = length, + .pcrindex = pcrindex, + .logdataptr = &pcpes, + .logdatalen = sizeof(pcpes), + }; + + rc = hash_log_extend_event(&hleei, &hleeo); + if (rc == 0) + *edx_ptr = hleeo.eventnumber; + + return rc; +} + + +void VISIBLE32FLAT +tcpa_interrupt_handler32(struct bregs *regs) +{ + if (!CONFIG_TCGBIOS) + return; + + set_cf(regs, 0); + + if (!has_working_tpm()) { + regs->eax = TCG_GENERAL_ERROR; + return; + } + + switch ((enum irq_ids)regs->al) { + case TCG_StatusCheck: + if (is_tpm_present() == 0) { + /* no TPM available */ + regs->eax = TCG_PC_TPM_NOT_PRESENT; + } else { + regs->eax = 0; + regs->ebx = TCG_MAGIC; + regs->ch = TCG_VERSION_MAJOR; + regs->cl = TCG_VERSION_MINOR; + regs->edx = 0x0; + regs->esi = (u32)get_lasa_base_ptr(NULL); + regs->edi = + (u32)get_lasa_last_ptr(NULL, NULL); + } + break; + + case TCG_HashLogExtendEvent: + regs->eax = + hash_log_extend_event( + (struct hleei_short *)input_buf32(regs), + (struct hleeo *)output_buf32(regs)); + break; + + case TCG_PassThroughToTPM: + regs->eax = + pass_through_to_tpm((struct pttti *)input_buf32(regs), + (struct pttto *)output_buf32(regs)); + break; + + case TCG_ShutdownPreBootInterface: + regs->eax = shutdown_preboot_interface(); + break; + + case TCG_HashLogEvent: + regs->eax = hash_log_event((struct hlei*)input_buf32(regs), + (struct hleo*)output_buf32(regs)); + break; + + case TCG_HashAll: + regs->eax = + hash_all((struct hai*)input_buf32(regs), + (u8 *)output_buf32(regs)); + break; + + case TCG_TSS: + regs->eax = tss((struct ti*)input_buf32(regs), + (struct to*)output_buf32(regs)); + break; + + case TCG_CompactHashLogExtendEvent: + regs->eax = + compact_hash_log_extend_event((u8 *)input_buf32(regs), + regs->esi, + regs->ecx, + regs->edx, + ®s->edx); + break; + + default: + set_cf(regs, 1); + } + + return; +} +
u32 tcpa_s3_resume(void) diff --git a/src/tcgbios.h b/src/tcgbios.h index 461e141..df75963 100644 --- a/src/tcgbios.h +++ b/src/tcgbios.h @@ -373,6 +373,10 @@ enum ipltype { IPL_EL_TORITO_2 };
+ +void tcpa_interrupt_handler32(struct bregs *regs); +void _cfunc32flat_tcpa_interrupt_handler32(struct bregs *regs); + void tcpa_acpi_init(void); int detected_tpm(void); u32 tcpa_startup(void); diff --git a/src/util.h b/src/util.h index b54271b..b795ffb 100644 --- a/src/util.h +++ b/src/util.h @@ -76,6 +76,7 @@ u32 find_resume_vector(void); void acpi_reboot(void); void find_acpi_features(void); extern struct smbios_entry_point *SMBiosAddr; +struct smbios_entry_point *get_smbios_entry_point(); void copy_smbios(void *pos); void display_uuid(void); void copy_table(void *pos); @@ -234,4 +235,34 @@ void vgahook_setup(struct pci_device *pci); // version (auto generated file out/version.c) extern const char VERSION[];
+ +static inline u32 rol(u32 val, u16 rol) +{ + u32 res; + + __asm__ __volatile__ ("rol %%cl, %%eax" + : "=a" (res) + : "a" (val), "c" (rol)); + + return res; +} + + +static inline u64 bswap_64(u64 val) +{ + u32 hi = (u32)(val >> 32); + u32 lo = (u32)val; + + __asm__ __volatile__ ("bswap %%eax" + : "=a" (lo) + : "a" (lo)); + + __asm__ __volatile__ ("bswap %%eax" + : "=a" (hi) + : "a" (hi)); + + return ((u64)lo << 32 | hi); +} + + #endif // util.h
On Wed, Jul 02, 2014 at 11:38:48AM -0400, Stefan Berger wrote:
This patch implements the TCG BIOS interrupt handler 1ah. It is for example used by trusted grub.
This patch adds an implementation of SHA1 (following NIST specs., IETF RFC 3147 and Wikipedia) for speeding up measurements of code. Trusted Grub for example makes use of this interface and measures (calculates SHA1) of the Linux kernel and initrd. Those files can be rather large and hunting their bytes through the TIS interface as part of the int handler commands invoked by trusted grub does take quite some time due to the many vmexits the interface is creating (one per byte).
There is also a threshold for the size of data to hash (100k) below which the TPM is used and above the internal faster SHA1 algorithm is used.
This patch for example enables trusted grub to interact with the TPM and take additional measurements.
[...]
--- a/src/tcgbios.h +++ b/src/tcgbios.h @@ -373,6 +373,10 @@ enum ipltype { IPL_EL_TORITO_2 };
+void tcpa_interrupt_handler32(struct bregs *regs); +void _cfunc32flat_tcpa_interrupt_handler32(struct bregs *regs);
The _cfunc32flat_XXX functions shouldn't be put in headers. Just put the declaration next to where it is used.
--- a/src/util.h +++ b/src/util.h @@ -76,6 +76,7 @@ u32 find_resume_vector(void); void acpi_reboot(void); void find_acpi_features(void); extern struct smbios_entry_point *SMBiosAddr; +struct smbios_entry_point *get_smbios_entry_point(); void copy_smbios(void *pos); void display_uuid(void); void copy_table(void *pos); @@ -234,4 +235,34 @@ void vgahook_setup(struct pci_device *pci); // version (auto generated file out/version.c) extern const char VERSION[];
+static inline u32 rol(u32 val, u16 rol) +{
- u32 res;
- __asm__ __volatile__ ("rol %%cl, %%eax"
: "=a" (res)
: "a" (val), "c" (rol));
- return res;
+}
This should go in x86.h.
+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);
+}
An equivalent of the above is already in byteorder.h.
-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.
Once booted into Linux, the current measurements produce the following logs which can be found in /sys/kernel/security/tpm0/ascii_bios_measurements. The below log also shows measurements from trusted grub.
1 3fb240d2a04085a4e84f81e4398e070ed5a18163 06 [SMBIOS] 2 cc812353fc277c1fab99e0b721752a1392984566 06 [Option ROM] 2 9dbd87163112e5670378abe4510491259a61f411 05 [Start Option ROM Scan] 2 6f74e357331b8dee11bbad85f27bc66cb873106c 06 [Option ROM] 2 5626eb7ac05c7231e46d7461e7d3839b03ae9fad 06 [Option ROM] 4 c1e25c3f6b0dc78d57296aa2870ca6f782ccf80f 05 [Calling INT 19h] 0 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 1 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 2 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 3 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 4 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 5 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 6 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 7 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 4 8cf2fe6c87d4d0b2998a43da630292e6d85ee8b6 05 [Booting BCV device 80h (HDD)] 4 5dff94459a3e2d13a433ef94afdc306144565bf7 0d [IPL] 5 d1b33afde65ad47502332af957c60f20c84c1edc 0e [IPL Partition Data] 4 487ce764b527ccad17f1d04243d0136fa981e6c4 0d [IPL] 4 91d285e4dead566324c8938a3cc75803f462d9a1 0d [IPL] 4 8ba79ac98bb491524fef29defc724daaf6263d35 0d [IPL] 4 c591c15b82e4ff30e7383a4ff1ef3b41b38521ac 06 [] 4 8cdc27ec545eda33fbba1e8b8dae4da5c7206972 04 [Grub Event Separator] 5 8cdc27ec545eda33fbba1e8b8dae4da5c7206972 04 [Grub Event Separator] 5 e8673b9e14b02dc12d8ccfd0176bca7a3de7fc3c 0e [IPL Partition Data] 5 0163e375a0af7525c5dac1a8e74b277359e40d1d 1105 [] 8 4be30f67c3d48ab7f04d9c0fd07f06d4c68379be 1205 [] 8 54c83965978de9708d026016ecb0e70660e04388 1305 [] 5 2431ed60130faeaf3a045f21963f71cacd46a029 04 [OS Event Separator] 8 2431ed60130faeaf3a045f21963f71cacd46a029 04 [OS Event Separator] 8 f3973cae05d6e2055062119d6e6e1e077b7df876 1005 []
v5: - call code for measuring CDROM boot sector
v4: - return TCG_GENERAL_ERROR if ! has_working_tpm()
v2: - dropping call to tcpa_measure_post - converting tcpa_option_rom and tcpa_ipl functions to get pointers rather than segments passed - introduce public get_smbios_entry_point() function and use it rather than searching for the entry point - use dprintf(DEBUG_tcg, ...)
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com --- src/boot.c | 11 ++ src/cdrom.c | 10 ++ src/optionroms.c | 4 + src/post.c | 4 + src/tcgbios.c | 359 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 8 ++ 6 files changed, 396 insertions(+)
diff --git a/src/boot.c b/src/boot.c index f36f3d6..91edf9c 100644 --- a/src/boot.c +++ b/src/boot.c @@ -624,6 +624,10 @@ boot_disk(u8 bootdrv, int checksig) } }
+ tcpa_add_bootdevice(0, bootdrv); + /* specs: 8.2.3 steps 4 and 5 */ + tcpa_ipl(IPL_BCV, MAKE_FLATPTR(bootseg, 0), 512); + /* Canonicalize bootseg:bootip */ u16 bootip = (bootseg & 0x0fff) << 4; bootseg &= 0xf000; @@ -647,6 +651,11 @@ boot_cdrom(struct drive_s *drive_g)
u8 bootdrv = CDEmu.emulated_extdrive; u16 bootseg = CDEmu.load_segment; + + tcpa_add_bootdevice(1, bootdrv); + /* specs: 8.2.5.6 */ + tcpa_ipl(IPL_EL_TORITO_1, MAKE_FLATPTR(bootseg, 0), 512); + /* Canonicalize bootseg:bootip */ u16 bootip = (bootseg & 0x0fff) << 4; bootseg &= 0xf000; @@ -732,6 +741,8 @@ do_boot(int seq_nr) break; }
+ tcpa_returned_via_int18h(); + // Boot failed: invoke the boot recovery function struct bregs br; memset(&br, 0, sizeof(br)); diff --git a/src/cdrom.c b/src/cdrom.c index ff419c0..09a938e 100644 --- a/src/cdrom.c +++ b/src/cdrom.c @@ -15,6 +15,7 @@ #include "std/disk.h" // DISK_RET_SUCCESS #include "string.h" // memset #include "util.h" // cdrom_prepboot +#include "tcgbios.h" // tcpa_*
// Locks for removable devices u8 CDRom_locks[BUILD_MAX_EXTDRIVE] VARLOW; @@ -226,6 +227,11 @@ cdrom_boot(struct drive_s *drive) if (buffer[0x20] != 0x88) return 11; // Bootable
+ /* specs: 8.2.3 step 5 and 8.2.5.6, measure El Torito boot catalog */ + /* measure 2048 bytes (one sector) */ + tcpa_add_bootdevice(1, 0); + tcpa_ipl(IPL_EL_TORITO_2, MAKE_FLATPTR(GET_SEG(SS), buffer), 2048); + u8 media = buffer[0x21]; CDEmu.media = media;
@@ -251,6 +257,10 @@ cdrom_boot(struct drive_s *drive) if (ret) return 12;
+ /* specs: 8.2.3 step 4 and 8.2.5.6, measure El Torito boot image */ + /* measure 1st 512 bytes */ + tcpa_ipl(IPL_EL_TORITO_1, MAKE_FLATPTR(boot_segment, 0), 512); + if (media == 0) { // No emulation requested - return success. CDEmu.emulated_extdrive = EXTSTART_CD + cdid; diff --git a/src/optionroms.c b/src/optionroms.c index 93d9d2f..860ed44 100644 --- a/src/optionroms.c +++ b/src/optionroms.c @@ -19,6 +19,7 @@ #include "std/pnpbios.h" // PNP_SIGNATURE #include "string.h" // memset #include "util.h" // get_pnp_offset +#include "tcgbios.h" // tcpa_*
/**************************************************************** @@ -80,6 +81,7 @@ is_valid_rom(struct rom_header *rom) if (EnforceChecksum) return 0; } + tcpa_option_rom(rom, len); return 1; }
@@ -354,6 +356,8 @@ optionrom_setup(void) memset(sources, 0, sizeof(sources)); u32 post_vga = rom_get_last();
+ tcpa_start_option_rom_scan(); + if (CONFIG_OPTIONROMS_DEPLOYED) { // Option roms are already deployed on the system. u32 pos = post_vga; diff --git a/src/post.c b/src/post.c index 1cb7304..81daeb1 100644 --- a/src/post.c +++ b/src/post.c @@ -196,6 +196,9 @@ prepareboot(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);
@@ -223,6 +226,7 @@ maininit(void) // Initialize tpm (after acpi tables were written) tcpa_acpi_init(); tcpa_startup(); + tcpa_smbios_measure();
// Run vga option rom vgarom_setup(); diff --git a/src/tcgbios.c b/src/tcgbios.c index 9e4f721..bfa46e0 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -22,6 +22,7 @@ #include "output.h" // dprintf #include "std/acpi.h" // RSDP_SIGNATURE, rsdt_descriptor #include "sha1.h" // sha1 +#include "std/smbios.h"
static const u8 Startup_ST_CLEAR[2] = { 0x00, TPM_ST_CLEAR }; @@ -55,6 +56,8 @@ static const u8 GetCapability_Durations[] = { 0x00, 0x00, 0x01, 0x20 };
+static u8 evt_separator[] = {0xff,0xff,0xff,0xff}; +
#define RSDP_CAST(ptr) ((struct rsdp_descriptor *)ptr)
@@ -1062,6 +1065,362 @@ tcpa_interrupt_handler32(struct bregs *regs) return; }
+/* + * Add a measurement to the log; the data at data_seg:data/length are + * appended to the TCG_PCClientPCREventStruct + * + * Input parameters: + * pcrIndex : which PCR to extend + * event_type : type of event; specs 10.4.1 + * data : pointer to the data (i.e., string) to be added to the log + * length : length of the data + */ +static u32 +tcpa_add_measurement_to_log(u32 pcrIndex, + u32 event_type, + const char *data, u32 length) +{ + u32 rc = 0; + struct hleeo hleeo; + u8 _pcpes[offsetof(struct pcpes, event) + 400]; + struct pcpes *pcpes = (struct pcpes *)_pcpes; + + if (length < sizeof(_pcpes) - offsetof(struct pcpes, event)) { + + pcpes->pcrindex = pcrIndex; + pcpes->eventtype = event_type; + memset(&pcpes->digest, 0x0, sizeof(pcpes->digest)); + pcpes->eventdatasize = length; + memcpy(&pcpes->event, data, length); + + struct hleei_short hleei = { + .ipblength = sizeof(hleei), + .hashdataptr = &pcpes->event, + .hashdatalen = length, + .pcrindex = pcrIndex, + .logdataptr = _pcpes, + .logdatalen = length + offsetof(struct pcpes, event), + }; + + rc = hash_log_extend_event(&hleei, &hleeo); + } else { + rc = TCG_GENERAL_ERROR; + } + + return rc; +} + + +/* + * Add a measurement to the log; further description of the data + * that are to be hashed are NOT appended to the TCG_PCClientPCREventStruc. + * Input parameters: + * pcrIndex : PCR to extend + * event_type : type of event; specs 10.4.1 + * ptr : 32 bit pointer to the data to be hashed + * length : length of the data to be hashed + * + * Returns lower 16 bit of return code of TCG_HashLogExtendEvent. '0' means + * success, otherwise an error is indicated. + */ +static u32 +tcpa_add_measurement_to_log_simple(u32 pcrIndex, + u16 event_type, + const u8 *ptr, u32 length) +{ + struct hleeo hleeo; + struct pcpes pcpes = { + .pcrindex = pcrIndex, + .eventtype = event_type, + /* specs: 10.4.1, EV_IPL eventfield should not contain the code.*/ + .eventdatasize = 0, + .event = 0, + }; + struct hleei_short hleei = { + .ipblength = sizeof(hleei), + .hashdataptr = ptr, + .hashdatalen = length, + .pcrindex = pcrIndex, + .logdataptr = &pcpes, + .logdatalen = offsetof(struct pcpes, event), + }; + + return hash_log_extend_event(&hleei, &hleeo); +} + + +/* + * Add a measurement to the list of measurements + * pcrIndex : PCR to be extended + * event_type : type of event; specs 10.4.1 + * data : additional parameter; used as parameter for 10.4.3 + * 'action index' + */ +static u32 +tcpa_add_measurement(u32 pcrIndex, + u16 event_type, + const char *string) +{ + u32 rc; + + switch (event_type) { + case EV_SEPARATOR: + rc = tcpa_add_measurement_to_log_simple(pcrIndex, + event_type, + (u8 *)evt_separator, + 4); + break; + + case EV_ACTION: + rc = tcpa_add_measurement_to_log(pcrIndex, + event_type, + string, + strlen(string)); + break; + + default: + rc = TCG_INVALID_INPUT_PARA; + } + + return rc; +} + + +u32 +tcpa_calling_int19h(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + return tcpa_add_measurement(4, EV_ACTION, + "Calling INT 19h"); +} + + +u32 +tcpa_returned_via_int18h(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + return tcpa_add_measurement(4, EV_ACTION, + "Return via INT 18h"); +} + + +/* + * Add event separators for PCRs 0 to 7; specs 8.2.3 + */ +u32 +tcpa_add_event_separators(void) +{ + u32 rc; + u32 pcrIndex = 0; + + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + while (pcrIndex <= 7) { + rc = tcpa_add_measurement(pcrIndex, EV_SEPARATOR, NULL); + if (rc) + break; + pcrIndex ++; + } + + return rc; +} + + +/* + * Add a measurement regarding the boot device (CDRom, Floppy, HDD) to + * the list of measurements. + */ +u32 +tcpa_add_bootdevice(u32 bootcd, u32 bootdrv) +{ + const char *string; + + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + switch (bootcd) { + case 0: + switch (bootdrv) { + case 0: + string = "Booting BCV device 00h (Floppy)"; + break; + + case 0x80: + string = "Booting BCV device 80h (HDD)"; + break; + + default: + string = "Booting unknown device"; + break; + } + + break; + + default: + string = "Booting from CD ROM device"; + } + + return tcpa_add_measurement_to_log(4, EV_ACTION, + string, strlen(string)); +} + + +/* + * Add measurement to the log about option rom scan + * 10.4.3 : action 14 + */ +u32 +tcpa_start_option_rom_scan(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + return tcpa_add_measurement(2, EV_ACTION, + "Start Option ROM Scan"); +} + + +/* + * Add measurement to the log about an option rom + */ +u32 +tcpa_option_rom(const void *addr, u32 len) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + u32 rc; + struct pcctes_romex pcctes = { + .eventid = 7, /* 10.4.2.3.7 */ + .eventdatasize = sizeof(u16) + sizeof(u16) + SHA1_BUFSIZE, + }; + + rc = sha1((const u8 *)addr, len, pcctes.digest); + if (rc) + return rc; + + return tcpa_add_measurement_to_log(2, + EV_EVENT_TAG, + (const char *)&pcctes, + sizeof(pcctes)); +} + + +u32 +tcpa_smbios_measure(void) +{ + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + u32 rc; + struct pcctes pcctes = { + .eventid = 1, /* 10.4.2.3.1 */ + .eventdatasize = SHA1_BUFSIZE, + }; + struct smbios_entry_point *sep = SMBiosAddr; + + dprintf(DEBUG_tcg, "TCGBIOS: SMBIOS at %p\n", sep); + + if (!sep) + return 0; + + rc = sha1((const u8 *)sep->structure_table_address, + sep->structure_table_length, pcctes.digest); + if (rc) + return rc; + + return tcpa_add_measurement_to_log(1, + EV_EVENT_TAG, + (const char *)&pcctes, + sizeof(pcctes)); +} + + +/* + * Add a measurement to the log in support of 8.2.5.3 + * Creates two log entries + * + * Input parameter: + * bootcd : 0: MBR of hdd, 1: boot image, 2: boot catalog of El Torito + * addr : address where the IP data are located + * length : IP data length in bytes + */ +u32 +tcpa_ipl(enum ipltype bootcd, const u8 *addr, u32 length) +{ + u32 rc; + + if (!CONFIG_TCGBIOS) + return 0; + + if (!has_working_tpm()) + return TCG_GENERAL_ERROR; + + switch (bootcd) { + case IPL_EL_TORITO_1: + /* specs: 8.2.5.6 El Torito */ + rc = tcpa_add_measurement_to_log_simple(4, + EV_IPL, + addr, + length); + break; + + case IPL_EL_TORITO_2: + /* specs: 8.2.5.6 El Torito */ + rc = tcpa_add_measurement_to_log_simple(5, + EV_IPL_PARTITION_DATA, + addr, + length); + break; + + default: + /* specs: 8.2.5.3 */ + /* equivalent to: dd if=/dev/hda ibs=1 count=440 | sha1sum */ + rc = tcpa_add_measurement_to_log_simple(4, + EV_IPL, + addr, + 0x1b8); + + if (rc) + break; + + /* equivalent to: dd if=/dev/hda ibs=1 count=72 skip=440 | sha1sum */ + rc = tcpa_add_measurement_to_log_simple(5, + EV_IPL_PARTITION_DATA, + addr + 0x1b8, + 0x48); + } + + return rc; +} + +
u32 tcpa_s3_resume(void) diff --git a/src/tcgbios.h b/src/tcgbios.h index df75963..1965a05 100644 --- a/src/tcgbios.h +++ b/src/tcgbios.h @@ -382,6 +382,14 @@ int detected_tpm(void); u32 tcpa_startup(void); u32 tcpa_leave_bios(void); u32 tcpa_s3_resume(void); +u32 tcpa_calling_int19h(void); +u32 tcpa_returned_via_int18h(void); +u32 tcpa_add_bootdevice(u32 bootcd, u32 bootdrv); +u32 tcpa_add_event_separators(void); +u32 tcpa_ipl(enum ipltype bootcd, const u8 *addr, u32 count); +u32 tcpa_start_option_rom_scan(void); +u32 tcpa_option_rom(const void *addr, u32 len); +u32 tcpa_smbios_measure(void);
#endif /* TCGBIOS_H */
On Wed, Jul 02, 2014 at 11:38:49AM -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.
Reference for what needs to be measured can be found in section 3.2.2++ in
http://www.trustedcomputinggroup.org/resources/pc_client_work_group_specific...
The first measurements are done once the ACPI tables have been initialized.
Once booted into Linux, the current measurements produce the following logs which can be found in /sys/kernel/security/tpm0/ascii_bios_measurements. The below log also shows measurements from trusted grub.
1 3fb240d2a04085a4e84f81e4398e070ed5a18163 06 [SMBIOS] 2 cc812353fc277c1fab99e0b721752a1392984566 06 [Option ROM] 2 9dbd87163112e5670378abe4510491259a61f411 05 [Start Option ROM Scan] 2 6f74e357331b8dee11bbad85f27bc66cb873106c 06 [Option ROM] 2 5626eb7ac05c7231e46d7461e7d3839b03ae9fad 06 [Option ROM] 4 c1e25c3f6b0dc78d57296aa2870ca6f782ccf80f 05 [Calling INT 19h] 0 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 1 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 2 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 3 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 4 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 5 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 6 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 7 d9be6524a5f5047db5866813acf3277892a7a30a 04 [] 4 8cf2fe6c87d4d0b2998a43da630292e6d85ee8b6 05 [Booting BCV device 80h (HDD)] 4 5dff94459a3e2d13a433ef94afdc306144565bf7 0d [IPL] 5 d1b33afde65ad47502332af957c60f20c84c1edc 0e [IPL Partition Data] 4 487ce764b527ccad17f1d04243d0136fa981e6c4 0d [IPL] 4 91d285e4dead566324c8938a3cc75803f462d9a1 0d [IPL] 4 8ba79ac98bb491524fef29defc724daaf6263d35 0d [IPL] 4 c591c15b82e4ff30e7383a4ff1ef3b41b38521ac 06 [] 4 8cdc27ec545eda33fbba1e8b8dae4da5c7206972 04 [Grub Event Separator] 5 8cdc27ec545eda33fbba1e8b8dae4da5c7206972 04 [Grub Event Separator] 5 e8673b9e14b02dc12d8ccfd0176bca7a3de7fc3c 0e [IPL Partition Data] 5 0163e375a0af7525c5dac1a8e74b277359e40d1d 1105 [] 8 4be30f67c3d48ab7f04d9c0fd07f06d4c68379be 1205 [] 8 54c83965978de9708d026016ecb0e70660e04388 1305 [] 5 2431ed60130faeaf3a045f21963f71cacd46a029 04 [OS Event Separator] 8 2431ed60130faeaf3a045f21963f71cacd46a029 04 [OS Event Separator] 8 f3973cae05d6e2055062119d6e6e1e077b7df876 1005 []
v5:
- call code for measuring CDROM boot sector
v4:
- return TCG_GENERAL_ERROR if ! has_working_tpm()
v2:
- dropping call to tcpa_measure_post
- converting tcpa_option_rom and tcpa_ipl functions to get pointers rather than segments passed
- introduce public get_smbios_entry_point() function and use it rather than searching for the entry point
- use dprintf(DEBUG_tcg, ...)
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com
src/boot.c | 11 ++ src/cdrom.c | 10 ++ src/optionroms.c | 4 + src/post.c | 4 + src/tcgbios.c | 359 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 8 ++ 6 files changed, 396 insertions(+)
diff --git a/src/boot.c b/src/boot.c index f36f3d6..91edf9c 100644 --- a/src/boot.c +++ b/src/boot.c @@ -624,6 +624,10 @@ boot_disk(u8 bootdrv, int checksig) } }
- tcpa_add_bootdevice(0, bootdrv);
- /* specs: 8.2.3 steps 4 and 5 */
- tcpa_ipl(IPL_BCV, MAKE_FLATPTR(bootseg, 0), 512);
Minor quibble, but these repeated tcpa calls should be compacted into a single call to minimize the impact to the main bios code.
-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.
v6: - passing durations of commands to the transmission function
v5: - fixed an indentation
v2: - use if (!CONFIG_TCGBIOS) ... in all non-static functions - use dprintf(DEBUG_tcg, ...) for printing of debugging info - removing some code used for a future patch
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com --- src/boot.c | 14 +- src/tcgbios.c | 568 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tcgbios.h | 31 ++++ src/util.h | 1 + 4 files changed, 611 insertions(+), 3 deletions(-)
diff --git a/src/boot.c b/src/boot.c index 91edf9c..5533529 100644 --- a/src/boot.c +++ b/src/boot.c @@ -427,7 +427,7 @@ get_raw_keystroke(void) }
// Read a keystroke - waiting up to 'msec' milliseconds. -static int +int get_keystroke(int msec) { u32 end = irqtimer_calc(msec); @@ -461,13 +461,21 @@ interactive_bootmenu(void)
char *bootmsg = romfile_loadfile("etc/boot-menu-message", NULL); int menukey = romfile_loadint("etc/boot-menu-key", 0x86); - printf("%s", bootmsg ?: "\nPress F12 for boot menu.\n\n"); - free(bootmsg); +again: + printf("%s", bootmsg ?: "\nPress F12 for boot menu.\n"); + if (detected_tpm()) + printf("Press F11 to TPM menu.\n"); + printf("\n");
u32 menutime = romfile_loadint("etc/boot-menu-wait", DEFAULT_BOOTMENU_WAIT); enable_bootsplash(); int scan_code = get_keystroke(menutime); disable_bootsplash(); + if (detected_tpm() && scan_code == 0x85) { + tcpa_menu(); + goto again; + } + free(bootmsg); if (scan_code != menukey) return;
diff --git a/src/tcgbios.c b/src/tcgbios.c index bfa46e0..5be9bcd 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -23,6 +23,7 @@ #include "std/acpi.h" // RSDP_SIGNATURE, rsdt_descriptor #include "sha1.h" // sha1 #include "std/smbios.h" +#include "stacks.h" // wait_threads
static const u8 Startup_ST_CLEAR[2] = { 0x00, TPM_ST_CLEAR }; @@ -41,6 +42,11 @@ static const u8 GetCapability_Permanent_Flags[12] = { 0x00, 0x00, 0x01, 0x08 };
+static const u8 GetCapability_STClear_Flags[12] = { + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x01, 0x09 +}; + static const u8 GetCapability_OwnerAuth[12] = { 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x11 @@ -88,6 +94,10 @@ static tcpa_state_t tcpa_state = { .tpm_driver_to_use = TPM_INVALID_DRIVER, };
+typedef struct { + u8 op; +} tpm_bios_cfg_t; +
/******************************************************** Extensions for TCG-enabled BIOS @@ -1421,6 +1431,564 @@ tcpa_ipl(enum ipltype bootcd, const u8 *addr, u32 length) }
+static u32 +read_stclear_flags(char *buf, int buf_len) +{ + u32 rc; + u32 returnCode; + struct tpm_res_getcap_stclear_flags stcf; + + memset(buf, 0x0, buf_len); + + rc = build_and_send_cmd(TPM_ORD_GetCapability, + GetCapability_STClear_Flags, + sizeof(GetCapability_STClear_Flags), + (u8 *)&stcf, + sizeof(struct tpm_res_getcap_stclear_flags), + &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "TCGBIOS: Return code from TPM_GetCapability() " + "= 0x%08x\n", returnCode); + + if (rc || returnCode) + goto err_exit; + + memcpy(buf, &stcf.stclear_flags, buf_len); + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tcpa_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +static u32 +assert_physical_presence(int verbose) +{ + u32 rc = 0; + u32 returnCode; + struct tpm_stclear_flags stcf; + + rc = read_stclear_flags((char *)&stcf, sizeof(stcf)); + if (rc) { + dprintf(DEBUG_tcg, + "Error reading STClear flags: 0x%08x\n", rc); + return rc; + } + + if (stcf.flags[STCLEAR_FLAG_IDX_PHYSICAL_PRESENCE]) + /* physical presence already asserted */ + return 0; + + rc = build_and_send_cmd(TPM_ORD_PhysicalPresence, + PhysicalPresence_CMD_ENABLE, + sizeof(PhysicalPresence_CMD_ENABLE), + NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, + "Return code from TSC_PhysicalPresence(CMD_ENABLE) = 0x%08x\n", + returnCode); + + if (rc || returnCode) { + if (verbose) + printf("Error: Could not enable physical presence.\n\n"); + goto err_exit; + } + + rc = build_and_send_cmd(TPM_ORD_PhysicalPresence, + PhysicalPresence_PRESENT, + sizeof(PhysicalPresence_PRESENT), + NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, + "Return code from TSC_PhysicalPresence(PRESENT) = 0x%08x\n", + returnCode); + + if (rc || returnCode) { + if (verbose) + printf("Error: Could not set presence flag.\n\n"); + goto err_exit; + } + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + 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, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "TCGBIOS: Return code from TPM_GetCapability() " + "= 0x%08x\n", returnCode); + + if (rc || returnCode) + goto err_exit; + + memcpy(buf, &pf.perm_flags, buf_len); + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + 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, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "TCGBIOS: Return code from TPM_GetCapability() " + "= 0x%08x\n", returnCode); + + if (rc || returnCode) + goto err_exit; + + *has_owner = oauth.flag; + + return 0; + +err_exit: + dprintf(DEBUG_tcg,"TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + 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(verbose); + if (rc) { + dprintf(DEBUG_tcg, "TCGBIOS: Asserting physical presence failed.\n"); + return rc; + } + + rc = build_and_send_cmd(disable ? TPM_ORD_PhysicalDisable + : TPM_ORD_PhysicalEnable, + NULL, 0, NULL, 10, &returnCode, + TPM_DURATION_TYPE_SHORT); + dprintf(DEBUG_tcg, "Return code from TPM_Physical%sable = 0x%08x\n", + disable ? "Dis" : "En", returnCode); + + if (rc || returnCode) + goto err_exit; + + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: %sabling the TPM failed.\n", + disable ? "Dis" : "En"); + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + 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(verbose); + if (rc) { + dprintf(DEBUG_tcg, "TCGBIOS: Asserting physical presence failed.\n"); + return rc; + } + + rc = build_and_send_cmd(TPM_ORD_PhysicalSetDeactivated, + deactivate ? CommandFlag_TRUE + : CommandFlag_FALSE, + deactivate ? sizeof(CommandFlag_TRUE) + : sizeof(CommandFlag_FALSE), + NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, + "Return code from PhysicalSetDeactivated(%d) = 0x%08x\n", + deactivate ? 1 : 0, returnCode); + + if (rc || returnCode) + goto err_exit; + + if (!deactivate && allow_reset) { + if (verbose) { + printf("Requiring a reboot to activate the TPM.\n"); + + msleep(2000); + } + extern void reset_vector(void) __noreturn; + reset_vector(); + } + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tcpa_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +static u32 +enable_activate(int allow_reset, int verbose) +{ + u32 rc; + + rc = disable_tpm(0, verbose); + if (rc) + return rc; + + rc = deactivate_tpm(0, allow_reset, verbose); + + return rc; +} + + +static u32 +force_clear(int enable_activate_before, int enable_activate_after, int verbose) +{ + u32 rc; + u32 returnCode; + u8 has_owner; + + rc = read_has_owner(&has_owner); + if (rc) + return rc; + if (!has_owner) { + if (verbose) + printf("TPM does not have an owner.\n"); + return 0; + } + + if (enable_activate_before) { + rc = enable_activate(0, verbose); + if (rc) { + dprintf(DEBUG_tcg, + "TCGBIOS: Enabling/activating the TPM failed.\n"); + return rc; + } + } + + rc = assert_physical_presence(verbose); + if (rc) { + dprintf(DEBUG_tcg, "TCGBIOS: Asserting physical presence failed.\n"); + return rc; + } + + rc = build_and_send_cmd(TPM_ORD_ForceClear, + NULL, 0, NULL, 10, &returnCode, + TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "Return code from TPM_ForceClear() = 0x%08x\n", + returnCode); + + if (rc || returnCode) + goto err_exit; + + if (!enable_activate_after) { + if (verbose) + printf("Owner successfully cleared.\n" + "You will need to enable/activate the TPM again.\n\n"); + return 0; + } + + enable_activate(1, verbose); + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + + tcpa_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +static u32 +set_owner_install(int allow, int verbose) +{ + u32 rc, returnCode; + u8 has_owner; + struct tpm_permanent_flags pf; + + rc = read_has_owner(&has_owner); + if (rc) + return rc; + if (has_owner) { + if (verbose) + printf("Must first remove owner.\n"); + return 0; + } + + rc = read_permanent_flags((char *)&pf, sizeof(pf)); + if (rc) + return rc; + + if (pf.flags[PERM_FLAG_IDX_DISABLE]) { + if (verbose) + printf("TPM must first be enable.\n"); + return 0; + } + + rc = assert_physical_presence(verbose); + if (rc) { + dprintf(DEBUG_tcg, "TCGBIOS: Asserting physical presence failed.\n"); + return rc; + } + + rc = build_and_send_cmd(TPM_ORD_SetOwnerInstall, + (allow) ? CommandFlag_TRUE : + CommandFlag_FALSE, + sizeof(CommandFlag_TRUE), + NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT); + + dprintf(DEBUG_tcg, "Return code from TPM_SetOwnerInstall() = 0x%08x\n", + returnCode); + + if (rc || returnCode) + goto err_exit; + + if (verbose) + printf("Installation of owner %s.\n", allow ? "enabled" : "disabled"); + + return 0; + +err_exit: + dprintf(DEBUG_tcg, "TCGBIOS: TPM malfunctioning (line %d).\n", __LINE__); + tcpa_state.tpm_working = 0; + if (rc) + return rc; + return TCG_TCG_COMMAND_ERROR; +} + + +static void +show_tpm_state(void) +{ + struct tpm_permanent_flags pf; + u8 has_owner; + + if (read_permanent_flags((char *)&pf, sizeof(pf)) || + read_has_owner(&has_owner)) + return; + + printf("TPM is "); + + if (pf.flags[PERM_FLAG_IDX_DISABLE]) + printf("disabled"); + else + printf("enabled"); + + if (pf.flags[PERM_FLAG_IDX_DEACTIVATED]) + printf(", deactivated"); + else + printf(", active"); + + if (has_owner) + printf(" and has an owner.\n"); + else { + printf(", does not have an owner "); + if (pf.flags[PERM_FLAG_IDX_OWNERSHIP]) + printf("but one can be installed.\n"); + else + printf("and an owner cannot be installed.\n"); + } + +} + + +static u32 +tcpa_process_cfg(const tpm_bios_cfg_t *cfg, int verbose) +{ + u32 rc = 0; + + switch (cfg->op) { + case 0: /* no-op */ + break; + + case 1: + rc = disable_tpm(0, verbose); + break; + + case 2: + rc = disable_tpm(1, verbose); + break; + + case 3: + rc = deactivate_tpm(0, 1, verbose); + break; + + case 4: + rc = deactivate_tpm(1, 1, verbose); + break; + + case 5: + rc = force_clear(1, 0, verbose); + break; + + case 6: + rc = set_owner_install(1, verbose); + break; + + case 7: + rc = set_owner_install(0, verbose); + break; + + default: + break; + } + + if (rc) + printf("Op %d: An error occurred: 0x%x\n", cfg->op, rc); + + return rc; +} + +void +tcpa_menu(void) +{ + if (!CONFIG_TCGBIOS) + return; + + int scan_code; + u32 rc; + tpm_bios_cfg_t cfg = { + .op = 0, + }; + + while (get_keystroke(0) >= 0) + ; + wait_threads(); + + for (;;) { + if (has_working_tpm()) { + printf("1. Enable TPM\n" + "2. Disable TPM\n" + "3. Activate TPM\n" + "4. Deactivate TPM\n" + "5. Clear ownership\n" + "6. Allow installation of owner\n" + "7. Prevent installation of owner\n"); + } else { + printf("TPM is not working correctly.\n"); + } + + printf("Escape for previous menu.\n"); + + if (has_working_tpm()) { + show_tpm_state(); + } + + cfg.op = 0; + + while ((scan_code = get_keystroke(1000)) == ~0) + ; + + switch (scan_code) { + case 1: + // ESC + return; + case 2 ... 8: + cfg.op = scan_code - 1; + break; + default: + continue; + } + + if (has_working_tpm()) { + rc = tcpa_process_cfg(&cfg, 1); + + if (rc) + printf("An error occurred: 0x%x\n", rc); + } + } +} +
u32 tcpa_s3_resume(void) diff --git a/src/tcgbios.h b/src/tcgbios.h index 1965a05..b01300a 100644 --- a/src/tcgbios.h +++ b/src/tcgbios.h @@ -323,6 +323,36 @@ struct tpm_res_getcap_perm_flags { } PACKED;
+struct tpm_req_getcap_stclear_flags { + TPM_REQ_HEADER + u32 capArea; + u32 subCapSize; + u32 subCap; +} PACKED; + + +struct tpm_stclear_flags { + u16 tag; + u8 flags[5]; +} PACKED; + + +enum stclearFlagsIndex { + STCLEAR_FLAG_IDX_DEACTIVATED = 0, + STCLEAR_FLAG_IDX_DISABLE_FORCE_CLEAR, + STCLEAR_FLAG_IDX_PHYSICAL_PRESENCE, + STCLEAR_FLAG_IDX_PHYSICAL_PRESENCE_LOCK, + STCLEAR_FLAG_IDX_GLOBAL_LOCK, +}; + + +struct tpm_res_getcap_stclear_flags { + TPM_RSP_HEADER + u32 size; + struct tpm_stclear_flags stclear_flags; +} PACKED; + + struct tpm_res_getcap_ownerauth { TPM_RSP_HEADER u32 size; @@ -390,6 +420,7 @@ u32 tcpa_ipl(enum ipltype bootcd, const u8 *addr, u32 count); u32 tcpa_start_option_rom_scan(void); u32 tcpa_option_rom(const void *addr, u32 len); u32 tcpa_smbios_measure(void); +void tcpa_menu(void);
#endif /* TCGBIOS_H */ diff --git a/src/util.h b/src/util.h index b795ffb..68d5026 100644 --- a/src/util.h +++ b/src/util.h @@ -36,6 +36,7 @@ int bootprio_find_pci_rom(struct pci_device *pci, int instance); int bootprio_find_named_rom(const char *name, int instance); struct usbdevice_s; int bootprio_find_usb(struct usbdevice_s *usbdev, int lun); +int get_keystroke(int msec);
// bootsplash.c void enable_vga_console(void);
This patch adds a menu item for displaying TPM diagnostics such as timeouts and durations, and device, vendor, and revision IDs and the display of some physical presence flags of the TPM.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com --- src/Kconfig | 8 +++ src/hw/tpm_drivers.c | 11 ++++ src/hw/tpm_drivers.h | 1 + src/tcgbios.c | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/tcgbios.h | 5 ++ 5 files changed, 198 insertions(+), 1 deletion(-)
diff --git a/src/Kconfig b/src/Kconfig index 9e65449..a6e1096 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -418,6 +418,14 @@ menu "BIOS interfaces" help Provide TPM support along with TCG BIOS extensions
+ config TPM_DIAGNOSTICS + depends on TCGBIOS + bool "TPM Diagnostics menu item" + default n + help + Add a menu item for displaying of TPM diagnostics in case + of certain issues with the TPM hardware. + endmenu
menu "BIOS Tables" diff --git a/src/hw/tpm_drivers.c b/src/hw/tpm_drivers.c index 1d5a779..f7cb669 100644 --- a/src/hw/tpm_drivers.c +++ b/src/hw/tpm_drivers.c @@ -241,6 +241,16 @@ static u32 tis_waitrespready(enum tpmDurationType to_t) }
+static void tis_version_data(u16 *did, u16 *vid, u16 *rid) +{ + u8 locty = tis_find_active_locality(); + + *did = readw(TIS_REG(locty, TIS_REG_DID_VID + 2)); + *vid = readw(TIS_REG(locty, TIS_REG_DID_VID)); + *rid = readw(TIS_REG(locty, TIS_REG_RID)); +} + + struct tpm_driver tpm_drivers[TPM_NUM_DRIVERS] = { [TIS_DRIVER_IDX] = { @@ -256,6 +266,7 @@ struct tpm_driver tpm_drivers[TPM_NUM_DRIVERS] = { .waitdatavalid = tis_waitdatavalid, .waitrespready = tis_waitrespready, .sha1threshold = 100 * 1024, + .get_vers_data = tis_version_data, }, };
diff --git a/src/hw/tpm_drivers.h b/src/hw/tpm_drivers.h index 34bb12d..f13f198 100644 --- a/src/hw/tpm_drivers.h +++ b/src/hw/tpm_drivers.h @@ -26,6 +26,7 @@ struct tpm_driver { /* the TPM will be used for buffers of sizes below the sha1threshold for calculating the hash */ u32 sha1threshold; + void (*get_vers_data)(u16 *did, u16 *vid, u16 *rid); };
extern struct tpm_driver tpm_drivers[]; diff --git a/src/tcgbios.c b/src/tcgbios.c index 5be9bcd..bdeedb8 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -87,6 +87,15 @@ typedef struct { u8 tpm_working:1; u8 if_shutdown:1; u8 tpm_driver_to_use:4; +#ifdef CONFIG_TPM_DIAGNOSTICS + u32 startup_error; + u32 selftest_error; + u32 physpresence_enable_error; + u32 physpresence_lock_error; + u32 get_timeouts_error; + u32 get_durations_error; + u32 sha1_error; +#endif } tcpa_state_t;
@@ -403,6 +412,10 @@ determine_timeouts(void) dprintf(DEBUG_tcg, "TCGBIOS: Return code from TPM_GetCapability(Timeouts)" " = 0x%08x\n", returnCode);
+#ifdef CONFIG_TPM_DIAGNOSTICS + tcpa_state.get_timeouts_error = rc ? rc : returnCode; +#endif + if (rc || returnCode) goto err_exit;
@@ -416,6 +429,10 @@ determine_timeouts(void) dprintf(DEBUG_tcg, "TCGBIOS: Return code from TPM_GetCapability(Durations)" " = 0x%08x\n", returnCode);
+#ifdef CONFIG_TPM_DIAGNOSTICS + tcpa_state.get_durations_error = rc ? rc : returnCode; +#endif + if (rc || returnCode) goto err_exit;
@@ -471,6 +488,10 @@ tcpa_startup(void) dprintf(DEBUG_tcg, "Return code from TPM_Startup = 0x%08x\n", returnCode);
+#ifdef CONFIG_TPM_DIAGNOSTICS + tcpa_state.startup_error = rc ? rc : returnCode; +#endif + if (rc || returnCode) goto err_exit;
@@ -480,6 +501,10 @@ tcpa_startup(void) dprintf(DEBUG_tcg, "Return code from TPM_SelfTestFull = 0x%08x\n", returnCode);
+#ifdef CONFIG_TPM_DIAGNOSTICS + tcpa_state.selftest_error = rc ? rc : returnCode; +#endif + if (rc || returnCode) goto err_exit;
@@ -515,6 +540,11 @@ tcpa_leave_bios(void) PhysicalPresence_CMD_ENABLE, sizeof(PhysicalPresence_CMD_ENABLE), NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT); + +#ifdef CONFIG_TPM_DIAGNOSTICS + tcpa_state.physpresence_enable_error = rc ? rc : returnCode; +#endif + if (rc || returnCode) goto err_exit;
@@ -522,6 +552,11 @@ tcpa_leave_bios(void) PhysicalPresence_NOT_PRESENT_LOCK, sizeof(PhysicalPresence_NOT_PRESENT_LOCK), NULL, 10, &returnCode, TPM_DURATION_TYPE_SHORT); + +#ifdef CONFIG_TPM_DIAGNOSTICS + tcpa_state.physpresence_lock_error = rc ? rc : returnCode; +#endif + if (rc || returnCode) goto err_exit;
@@ -628,6 +663,10 @@ tpm_sha1_calc(const u8 *data, u32 length, u8 *hash) &returnCode, &data[offset], rest, TPM_DURATION_TYPE_SHORT);
+#ifdef CONFIG_TPM_DIAGNOSTICS + tcpa_state.sha1_error = rc ? rc : returnCode; +#endif + if (rc || returnCode) goto err_exit;
@@ -1929,6 +1968,131 @@ tcpa_process_cfg(const tpm_bios_cfg_t *cfg, int verbose) return rc; }
+#ifdef CONFIG_TPM_DIAGNOSTICS +static void +wait_for_any_key(void) +{ + printf("Press any key to continue.\n"); + while (get_keystroke(1000) < 0) + ; +} + +static void +tcpa_display_timeouts(void) +{ + struct tpm_driver *td; + int i; + + td = &tpm_drivers[tcpa_state.tpm_driver_to_use]; + + printf("\nTIS Timeouts : "); + + if (td->timeouts == NULL) { + printf("not known"); + } else { + for (i = 0; i < 4 ; i++) + printf("%d ", td->timeouts[i]); + } + + printf("\nTPM Durations: "); + + if (td->durations == NULL) { + printf("not known"); + } else { + for (i = 0; i < 3 ; i++) + printf("%d ", td->durations[i]); + } + printf("\n\n"); + + wait_for_any_key(); +} + +static void +tcpa_display_tpm_version(void) +{ + struct tpm_driver *td; + u16 did, vid, rid; + + td = &tpm_drivers[tcpa_state.tpm_driver_to_use]; + + td->get_vers_data(&did, &vid, &rid); + + printf("\nDevice ID : 0x%x\n", did); + printf("Vendor ID : 0x%x\n", vid); + printf("Revision ID : 0x%x\n\n", rid); + wait_for_any_key(); +} + +static void +tcpa_display_errors(void) +{ + printf("\ntpm probed: %d\n", tcpa_state.tpm_probed); + printf("tpm working: %d\n", tcpa_state.tpm_working); + printf("tpm found: %d\n", tcpa_state.tpm_found); + printf("startup error: 0x%x\n", tcpa_state.startup_error); + printf("selftest error: 0x%x\n", tcpa_state.selftest_error); + printf("get timeouts error : 0x%x\n", + tcpa_state.get_timeouts_error); + printf("get durations error: 0x%x\n", + tcpa_state.get_durations_error); + printf("get sha1 error: 0x%x\n", + tcpa_state.sha1_error); + printf("phys. presence enable error: 0x%x\n", + tcpa_state.physpresence_enable_error); + printf("phys. presence lock error : 0x%x\n\n", + tcpa_state.physpresence_lock_error); + wait_for_any_key(); +} + +static void +tcpa_display_perm_flags(void) +{ + struct tpm_permanent_flags pf; + int rc; + + rc = read_permanent_flags((char *)&pf, sizeof(pf)); + if (rc) + return; + + printf("phys. presence lifetime lock: %d\n", + pf.flags[PERM_FLAG_IDX_PHYSICAL_PRESENCE_LIFETIME_LOCK]); + printf("phys. presence hw enable: %d\n", + pf.flags[PERM_FLAG_IDX_PHYSICAL_PRESENCE_HW_ENABLE]); + printf("phys. presence cmd enable: %d\n\n", + pf.flags[PERM_FLAG_IDX_PHYSICAL_PRESENCE_CMD_ENABLE]); + wait_for_any_key(); +} + +static void +tcpa_display_stclear_flags(void) +{ + struct tpm_stclear_flags stcf; + int rc; + + rc = read_stclear_flags((char *)&stcf, sizeof(stcf)); + if (rc) + return; + + printf("phys. presence: %d\n", + stcf.flags[STCLEAR_FLAG_IDX_PHYSICAL_PRESENCE]); + printf("phys. presence lock: %d\n", + stcf.flags[STCLEAR_FLAG_IDX_PHYSICAL_PRESENCE_LOCK]); + printf("global lock: %d\n\n", + stcf.flags[STCLEAR_FLAG_IDX_GLOBAL_LOCK]); + wait_for_any_key(); +} + +static void +tcpa_display_diagnostics(void) +{ + tcpa_display_tpm_version(); + tcpa_display_errors(); + tcpa_display_timeouts(); + tcpa_display_perm_flags(); + tcpa_display_stclear_flags(); +} +#endif + void tcpa_menu(void) { @@ -1955,9 +2119,12 @@ tcpa_menu(void) "6. Allow installation of owner\n" "7. Prevent installation of owner\n"); } else { - printf("TPM is not working correctly.\n"); + printf("TPM is not working correctly.\n\n"); }
+#ifdef CONFIG_TPM_DIAGNOSTICS + printf("d. TPM Diagnostics\n"); +#endif printf("Escape for previous menu.\n");
if (has_working_tpm()) { @@ -1976,6 +2143,11 @@ tcpa_menu(void) case 2 ... 8: cfg.op = scan_code - 1; break; +#ifdef CONFIG_TPM_DIAGNOSTICS + case 32: + tcpa_display_diagnostics(); + continue; +#endif default: continue; } diff --git a/src/tcgbios.h b/src/tcgbios.h index b01300a..3807b2f 100644 --- a/src/tcgbios.h +++ b/src/tcgbios.h @@ -87,6 +87,10 @@ #define TPM_ST_DEACTIVATED 0x3
+/* TPM command error codes */ +#define TPM_INVALID_POSTINIT 0x26 + + /* interrupt identifiers (al register) */ enum irq_ids { TCG_StatusCheck = 0, @@ -313,6 +317,7 @@ enum permFlagsIndex { PERM_FLAG_IDX_ALLOW_MAINTENANCE, PERM_FLAG_IDX_PHYSICAL_PRESENCE_LIFETIME_LOCK, PERM_FLAG_IDX_PHYSICAL_PRESENCE_HW_ENABLE, + PERM_FLAG_IDX_PHYSICAL_PRESENCE_CMD_ENABLE, };
Make the key for reaching the TPM menu configurable since the Chromebooks don't have F11.
Allow the TPM to be initialized by other firmware, so disregard certain TPM error codes.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com --- src/boot.c | 3 ++- src/tcgbios.c | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/src/boot.c b/src/boot.c index 5533529..c5b9d1d 100644 --- a/src/boot.c +++ b/src/boot.c @@ -461,6 +461,7 @@ interactive_bootmenu(void)
char *bootmsg = romfile_loadfile("etc/boot-menu-message", NULL); int menukey = romfile_loadint("etc/boot-menu-key", 0x86); + int menukey_tpm = romfile_loadint("etc/boot-menu-key-tpm", 0x85); again: printf("%s", bootmsg ?: "\nPress F12 for boot menu.\n"); if (detected_tpm()) @@ -471,7 +472,7 @@ again: enable_bootsplash(); int scan_code = get_keystroke(menutime); disable_bootsplash(); - if (detected_tpm() && scan_code == 0x85) { + if (detected_tpm() && scan_code == menukey_tpm) { tcpa_menu(); goto again; } diff --git a/src/tcgbios.c b/src/tcgbios.c index bdeedb8..3c1d514 100644 --- a/src/tcgbios.c +++ b/src/tcgbios.c @@ -492,7 +492,15 @@ tcpa_startup(void) tcpa_state.startup_error = rc ? rc : returnCode; #endif
- if (rc || returnCode) +#ifdef CONFIG_COREBOOT + /* with other firmware on the system the TPM may already have been + * initialized + */ + if (returnCode == TPM_INVALID_POSTINIT) + returnCode = 0; +#endif + + if (rc || returnCode) goto err_exit;
rc = build_and_send_cmd(TPM_ORD_SelfTestFull, NULL, 0,
On Wed, Jul 02, 2014 at 11:38:44AM -0400, Stefan Berger wrote:
This is a repost of a series of patches providing TPM support to SeaBIOS.
As an addition, this patch series now works on the Acer C720 Chromebook with limitations (S3 not getting invoked; no logging into TCPA table).
The patch series cleanly applies to a checkout of tags/rel-1.7.5.
The following set of patches add TPM and Trusted Computing support to SeaBIOS.
Thanks Stefan. Just to make sure I understand - at a very high-level - the goal of the tcg bios is to take "measurements" of the firmware so that an OS (or app) can verify that it isn't being run in a malicious sandbox (or at least, is running in the same environment that it was originally installed in)? That is, the OS can verify using cryptographic hashes that the same chain of system boot software is in use and thus no new malicious boot loader, option rom, etc. could be running. This is all assuming the BIOS itself is not attacked (because if the "S-CRTM" is compromised then an attacker could replay bogus measurements that an OS would be unable to distinguish). Am I correct?
-Kevin
On 07/02/2014 11:51 AM, Kevin O'Connor wrote:
On Wed, Jul 02, 2014 at 11:38:44AM -0400, Stefan Berger wrote:
This is a repost of a series of patches providing TPM support to SeaBIOS.
As an addition, this patch series now works on the Acer C720 Chromebook with limitations (S3 not getting invoked; no logging into TCPA table).
The patch series cleanly applies to a checkout of tags/rel-1.7.5.
The following set of patches add TPM and Trusted Computing support to SeaBIOS.
Thanks Stefan. Just to make sure I understand - at a very high-level
- the goal of the tcg bios is to take "measurements" of the firmware
so that an OS (or app) can verify that it isn't being run in a malicious sandbox (or at least, is running in the same environment that it was originally installed in)? That is, the OS can verify using cryptographic hashes that the same chain of system boot software is in use and thus no new malicious boot loader, option rom, etc. could be running. This is all assuming the BIOS itself is not attacked (because if the "S-CRTM" is compromised then an attacker could replay bogus measurements that an OS would be unable to distinguish). Am I correct?
Yes, that's the idea.
Stefan
On 07/02/2014 11:51 AM, Kevin O'Connor wrote:
On Wed, Jul 02, 2014 at 11:38:44AM -0400, Stefan Berger wrote:
This is a repost of a series of patches providing TPM support to SeaBIOS.
As an addition, this patch series now works on the Acer C720 Chromebook with limitations (S3 not getting invoked; no logging into TCPA table).
The patch series cleanly applies to a checkout of tags/rel-1.7.5.
The following set of patches add TPM and Trusted Computing support to SeaBIOS.
Kevin,
do you have comments about the patches? I have to say that in the meantime I did some more minor work on them (fixed a bug or two), but the general structure of the code is still the same.
We now have ACPI support in QEMU as well. In SeaBIOS the TPM patches add the TPM ACPI tables (SSDT, TCPA) if SeaBIOS is building ACPI tables. I suppose this is still the correct thing to do.
Regards, Stefan
On Mon, Aug 25, 2014 at 05:18:49PM -0400, Stefan Berger wrote:
On 07/02/2014 11:51 AM, Kevin O'Connor wrote:
On Wed, Jul 02, 2014 at 11:38:44AM -0400, Stefan Berger wrote:
This is a repost of a series of patches providing TPM support to SeaBIOS.
As an addition, this patch series now works on the Acer C720 Chromebook with limitations (S3 not getting invoked; no logging into TCPA table).
The patch series cleanly applies to a checkout of tags/rel-1.7.5.
The following set of patches add TPM and Trusted Computing support to SeaBIOS.
Kevin,
do you have comments about the patches? I have to say that in the meantime I did some more minor work on them (fixed a bug or two), but the general structure of the code is still the same.
Hi Stefan,
Sorry for not responding earlier.
As we discussed in the past, the main concern I have is the addition of the TPM boot menu. The problem with the menu, is that I suspect the number of people who will find utility in it is extremely small. (Most people wont even know what a TPM is.) However, many more users are likely to see the prompt, click through it, and then get very confused with the available options and the implications of choosing them. So, I think it is a poor trade off of complexity for gain. Which leads to my second major concern with bios menus - those users that would find gain are also likely to be the users that need to make a change to hundreds of machines. Those users don't want to go around connecting keyboards (or the VM equivalent) to all those machines.
I have a couple of other minor comments on the patch series - I'll send some notes out. However, my other comments are minor and I think finding a solution to the menu is the most important next step.
We now have ACPI support in QEMU as well. In SeaBIOS the TPM patches add the TPM ACPI tables (SSDT, TCPA) if SeaBIOS is building ACPI tables. I suppose this is still the correct thing to do.
I'd prefer to keep the ACPI code unchanged. New users that want TPM functionality on QEMU can use a newer machine type, and users that want to use an older machine type for compatibility reasons should not see changes to the ACPI definitions.
-Kevin
On 08/26/2014 10:01 AM, Kevin O'Connor wrote:
On Mon, Aug 25, 2014 at 05:18:49PM -0400, Stefan Berger wrote:
On 07/02/2014 11:51 AM, Kevin O'Connor wrote:
On Wed, Jul 02, 2014 at 11:38:44AM -0400, Stefan Berger wrote:
This is a repost of a series of patches providing TPM support to SeaBIOS.
As an addition, this patch series now works on the Acer C720 Chromebook with limitations (S3 not getting invoked; no logging into TCPA table).
The patch series cleanly applies to a checkout of tags/rel-1.7.5.
The following set of patches add TPM and Trusted Computing support to SeaBIOS.
Kevin,
do you have comments about the patches? I have to say that in the meantime I did some more minor work on them (fixed a bug or two), but the general structure of the code is still the same.
Hi Stefan,
Sorry for not responding earlier.
As we discussed in the past, the main concern I have is the addition of the TPM boot menu. The problem with the menu, is that I suspect the number of people who will find utility in it is extremely small. (Most people wont even know what a TPM is.) However, many more users are likely to see the prompt, click through it, and then get very confused with the available options and the implications of choosing them. So, I think it is a poor trade off of complexity for gain.
Here's the justification for the menu: A TPM can have an owner who identifies himself via owner password. In the case that the owner forgets the password, there is no way for the owner to give up ownership of the TPM unless there is a BIOS menu that allows him to give up ownership under physical presence (1). Physical presence is only assumed while the BIOS is active and a user for example pressed a key when the machine initialized to indicate physical presence. (I would think pressing F11 to enter the BIOS menu would be enough for indicate physical presence). I don't know of another way of doing this.
(1): TPM_ForceClear is to be sent by the BIOS. Page 41 in the pdf from http://www.trustedcomputinggroup.org/files/static_page_files/72C33D71-1A4B-B...
Menus exist today in BIOSes and UEFI. Those users that care about them, will look at them, others I would think will disregard them.
Which leads to my second major concern with bios menus - those users that would find gain are also likely to be the users that need to make a change to hundreds of machines. Those users don't want to go around connecting keyboards (or the VM equivalent) to all those machines.
I have a couple of other minor comments on the patch series - I'll send some notes out. However, my other comments are minor and I think finding a solution to the menu is the most important next step.
We now have ACPI support in QEMU as well. In SeaBIOS the TPM patches add the TPM ACPI tables (SSDT, TCPA) if SeaBIOS is building ACPI tables. I suppose this is still the correct thing to do.
I'd prefer to keep the ACPI code unchanged. New users that want TPM functionality on QEMU can use a newer machine type, and users that want to use an older machine type for compatibility reasons should not see changes to the ACPI definitions.
So there is no existing use case where SeaBIOS is the only firmware on a platform (other than on QEMU)? I know coreboot can run SeaBIOS as a payload and therefore I would agree that in this case coreboot should handle the ACPI tables.
Stefan
On Tue, Aug 26, 2014 at 10:42:31AM -0400, Stefan Berger wrote:
On 08/26/2014 10:01 AM, Kevin O'Connor wrote:
On Mon, Aug 25, 2014 at 05:18:49PM -0400, Stefan Berger wrote:
We now have ACPI support in QEMU as well. In SeaBIOS the TPM patches add the TPM ACPI tables (SSDT, TCPA) if SeaBIOS is building ACPI tables. I suppose this is still the correct thing to do.
I'd prefer to keep the ACPI code unchanged. New users that want TPM functionality on QEMU can use a newer machine type, and users that want to use an older machine type for compatibility reasons should not see changes to the ACPI definitions.
So there is no existing use case where SeaBIOS is the only firmware on a platform (other than on QEMU)? I know coreboot can run SeaBIOS as a payload and therefore I would agree that in this case coreboot should handle the ACPI tables.
The acpi code in SeaBIOS was only ever used by QEMU. Other users (coreboot and CSM) always created the acpi tables and passed them to SeaBIOS.
-Kevin
On Tue, Aug 26, 2014 at 10:42:31AM -0400, Stefan Berger wrote:
As we discussed in the past, the main concern I have is the addition of the TPM boot menu. The problem with the menu, is that I suspect the number of people who will find utility in it is extremely small. (Most people wont even know what a TPM is.) However, many more users are likely to see the prompt, click through it, and then get very confused with the available options and the implications of choosing them. So, I think it is a poor trade off of complexity for gain.
Here's the justification for the menu: A TPM can have an owner who identifies himself via owner password. In the case that the owner forgets the password, there is no way for the owner to give up ownership of the TPM unless there is a BIOS menu that allows him to give up ownership under physical presence (1). Physical presence is only assumed while the BIOS is active and a user for example pressed a key when the machine initialized to indicate physical presence. (I would think pressing F11 to enter the BIOS menu would be enough for indicate physical presence). I don't know of another way of doing this.
If I understand the intent of the above, the goal is to prevent malicious software on the guest from reprogramming the TPM without the users' knowledge. (The malicious software sees that the TPM is password locked, so it unlocks the TPM, clears the password, and continues.)
If this is the intent, can't we just pass a flag (via fw_cfg) from QEMU command line to SeaBIOS to force a clear? That is, the guest software can't manipulate the QEMU command line (or its fw_cfg entries) and so the ability to set a flag there is proof of physical presence. (Access to the virtual machine disk images and virtual machine command line is as close to "physical" as one can get.)
On coreboot, a similar solution could be accomplished by setting a flag in CBFS (the flash). Granted, one doesn't need to be physically present to reprogram the flash, but if one can reprogram the flash, they could just as easily reprogram SeaBIOS anyway.
-Kevin
"Kevin O'Connor" kevin@koconnor.net wrote on 08/26/2014 11:19:14 AM:
On Tue, Aug 26, 2014 at 10:42:31AM -0400, Stefan Berger wrote:
As we discussed in the past, the main concern I have is the addition of the TPM boot menu. The problem with the menu, is that I suspect the number of people who will find utility in it is extremely small. (Most people wont even know what a TPM is.) However, many more users are likely to see the prompt, click through it, and then get very confused with the available options and the implications of choosing them. So, I think it is a poor trade off of complexity for gain.
Here's the justification for the menu: A TPM can have an owner who identifies himself via owner password. In the case that the owner
forgets
the password, there is no way for the owner to give up ownership of
the TPM
unless there is a BIOS menu that allows him to give up ownership under physical presence (1). Physical presence is only assumed while the
BIOS is
active and a user for example pressed a key when the machine
initialized to
indicate physical presence. (I would think pressing F11 to enter the
BIOS
menu would be enough for indicate physical presence). I don't know of another way of doing this.
If I understand the intent of the above, the goal is to prevent malicious software on the guest from reprogramming the TPM without the users' knowledge. (The malicious software sees that the TPM is password locked, so it unlocks the TPM, clears the password, and continues.)
Yes. This should not be possible under normal circumstances where the BIOS gives up physical presence once it goes into the boot loader.
If this is the intent, can't we just pass a flag (via fw_cfg) from QEMU command line to SeaBIOS to force a clear? That is, the guest software can't manipulate the QEMU command line (or its fw_cfg entries) and so the ability to set a flag there is proof of physical presence. (Access to the virtual machine disk images and virtual machine command line is as close to "physical" as one can get.)
One would need at least a flag to indicate that the BIOS automatically give up ownership of the TPM. Giving up ownership also means that the device automatically becomes disabled and deactivated. The BIOS would then presumably automatically have to enabled and activate the TPM again without user interaction.
The other aspect is that this extension propagates all the way into higher layers: libvirt would need an API and command line tool extension just to set this flag and presumably use the QEMU monitor with a new command to indicate it. You want to be able to do this in a cloud environment, you need another API and/or GUI support in your cloud stack for doing just this... I doesn't seem to become a lot easier this way.
On coreboot, a similar solution could be accomplished by setting a flag in CBFS (the flash). Granted, one doesn't need to be physically present to reprogram the flash, but if one can reprogram the flash, they could just as easily reprogram SeaBIOS anyway.
I am not so familiar with how CBFS is handled. Is it at least access-restricted to root? I guess one would need a tool to write the above flag(s) into the flash at the right position.
Stefan
Hi,
If this is the intent, can't we just pass a flag (via fw_cfg) from QEMU command line to SeaBIOS to force a clear? That is, the guest software can't manipulate the QEMU command line (or its fw_cfg entries) and so the ability to set a flag there is proof of physical presence.
I'm not so sure moving this to fw_cfg is the right answer here.
We use fw_cfg for alot of configuration bits, because it is easier that way. We don't need a setup menu in seabios. We don't need persistent storage for config options.
There are exceptions though. We have a boot menu, which strictly speaking would not be needed as you can set the boot order via fw_cfg. But it is very useful that you can change the boot order interactively if needed (for a guest reinstall for example), without having to touch the virtual machine configuration.
Same applies here. IMO it should be possible to manage the TPM without having to touch the virtual machine configuration. Persistent storage isn't an issue in that case, the tpm device provides that.
We could add a fw_cfg file to enable/disable the tpm menu, simliar to the etc/show-boot-menu file for the boot menu. That way the menu would be off by default, avoiding user confusion.
For the qemu case it would not be needed IMHO as the tpm menu shows only up in case tpm hardware is present, and why should you add a tpm to your VM if you don't want to use it?
When running (via coreboot) on physical hardware it is more useful as you can't simply rip off the tpm chip to disable the menu ;)
cheers, Gerd
On Wed, Aug 27, 2014 at 09:51:02AM +0200, Gerd Hoffmann wrote:
Hi,
If this is the intent, can't we just pass a flag (via fw_cfg) from QEMU command line to SeaBIOS to force a clear? That is, the guest software can't manipulate the QEMU command line (or its fw_cfg entries) and so the ability to set a flag there is proof of physical presence.
I'm not so sure moving this to fw_cfg is the right answer here.
I appreciate the additional perspective.
We use fw_cfg for alot of configuration bits, because it is easier that way. We don't need a setup menu in seabios. We don't need persistent storage for config options.
There are exceptions though. We have a boot menu, which strictly speaking would not be needed as you can set the boot order via fw_cfg. But it is very useful that you can change the boot order interactively if needed (for a guest reinstall for example), without having to touch the virtual machine configuration.
Same applies here. IMO it should be possible to manage the TPM without having to touch the virtual machine configuration. Persistent storage isn't an issue in that case, the tpm device provides that.
Realistically, though, who is ever going to manage their TPM? Perhaps I'm missing an important use case. If so, Stefan, maybe you can describe some real-world scenarios.
For QEMU, I would have thought there were only two interesting states - TPM chip not present (and thus not enabled, not activated, and not ownable) - or TPM chip present and enabled, activated, and ownable. What's the value in the other states, and in what common situations would one want to change between states during the lifetime of a VM?
For coreboot, where a TPM chip is present, I imagine there would also be two common states - the user wishes to use the chip and thus it is enabled, activated, and ownable - or the user doesn't wish to use the chip and thus it is disabled and deactivated.
We could add a fw_cfg file to enable/disable the tpm menu, simliar to the etc/show-boot-menu file for the boot menu. That way the menu would be off by default, avoiding user confusion.
For the qemu case it would not be needed IMHO as the tpm menu shows only up in case tpm hardware is present, and why should you add a tpm to your VM if you don't want to use it?
When running (via coreboot) on physical hardware it is more useful as you can't simply rip off the tpm chip to disable the menu ;)
If adding "tpm-bios-running-state" to fw_cfg isn't viable, then another possible way forward would be to add a setup program to SeaBIOS. The way I would envision this is there would be a program stored in flash (or fw_cfg) and upon user request (eg, "Press F1 to enter setup") SeaBIOS would launch this program instead of using its normal boot process. This setup program could enable all sorts of useful functionality (eg, configuring time, default bootorder, passwords, tpm, reflash firmware, etc).
This would be a great feature to have. Unfortunately, it is not an easy solution.
-Kevin
On 08/27/2014 12:26 PM, Kevin O'Connor wrote:
On Wed, Aug 27, 2014 at 09:51:02AM +0200, Gerd Hoffmann wrote:
Hi,
If this is the intent, can't we just pass a flag (via fw_cfg) from QEMU command line to SeaBIOS to force a clear? That is, the guest software can't manipulate the QEMU command line (or its fw_cfg entries) and so the ability to set a flag there is proof of physical presence.
I'm not so sure moving this to fw_cfg is the right answer here.
I appreciate the additional perspective.
We use fw_cfg for alot of configuration bits, because it is easier that way. We don't need a setup menu in seabios. We don't need persistent storage for config options.
There are exceptions though. We have a boot menu, which strictly speaking would not be needed as you can set the boot order via fw_cfg. But it is very useful that you can change the boot order interactively if needed (for a guest reinstall for example), without having to touch the virtual machine configuration.
Same applies here. IMO it should be possible to manage the TPM without having to touch the virtual machine configuration. Persistent storage isn't an issue in that case, the tpm device provides that.
Realistically, though, who is ever going to manage their TPM? Perhaps I'm missing an important use case. If so, Stefan, maybe you can describe some real-world scenarios.
The above scenario with a user having forgotten the password or a machine being handed off to another user where the previous user did not give up ownership of the TPM. In these cases the user would manage the TPM via the BIOS in so far as he would have to give up the TPM ownership (under physical presence) which is typically done in a TPM BIOS menu item. There's typically also another option in the BIOS and that is one to activate / enable the device, maybe as one menu item or as two. Some people may want to leave it off, others may want to turn it on and the BIOS menu gives them this option.
A TPM menu in the BIOS is probably implemented in all of today's machines where users expect to adjust those parameters. I would say it's almost expected to find the possibility to do this in the BIOS and those who are used to it would probably get more confused if it wasn't implemented this way. This of course doesn't preclude additional ways to support TPM management.
Stefan
For QEMU, I would have thought there were only two interesting states
- TPM chip not present (and thus not enabled, not activated, and not
ownable) - or TPM chip present and enabled, activated, and ownable. What's the value in the other states, and in what common situations would one want to change between states during the lifetime of a VM?
For coreboot, where a TPM chip is present, I imagine there would also be two common states - the user wishes to use the chip and thus it is enabled, activated, and ownable - or the user doesn't wish to use the chip and thus it is disabled and deactivated.
We could add a fw_cfg file to enable/disable the tpm menu, simliar to the etc/show-boot-menu file for the boot menu. That way the menu would be off by default, avoiding user confusion.
For the qemu case it would not be needed IMHO as the tpm menu shows only up in case tpm hardware is present, and why should you add a tpm to your VM if you don't want to use it?
When running (via coreboot) on physical hardware it is more useful as you can't simply rip off the tpm chip to disable the menu ;)
If adding "tpm-bios-running-state" to fw_cfg isn't viable, then another possible way forward would be to add a setup program to SeaBIOS. The way I would envision this is there would be a program stored in flash (or fw_cfg) and upon user request (eg, "Press F1 to enter setup") SeaBIOS would launch this program instead of using its normal boot process. This setup program could enable all sorts of useful functionality (eg, configuring time, default bootorder, passwords, tpm, reflash firmware, etc).
This would be a great feature to have. Unfortunately, it is not an easy solution.
-Kevin
On Wed, Aug 27, 2014 at 05:04:51PM -0400, Stefan Berger wrote:
On 08/27/2014 12:26 PM, Kevin O'Connor wrote:
On Wed, Aug 27, 2014 at 09:51:02AM +0200, Gerd Hoffmann wrote:
Same applies here. IMO it should be possible to manage the TPM without having to touch the virtual machine configuration. Persistent storage isn't an issue in that case, the tpm device provides that.
Realistically, though, who is ever going to manage their TPM? Perhaps I'm missing an important use case. If so, Stefan, maybe you can describe some real-world scenarios.
The above scenario with a user having forgotten the password or a machine being handed off to another user where the previous user did not give up ownership of the TPM. In these cases the user would manage the TPM via the BIOS in so far as he would have to give up the TPM ownership (under physical presence) which is typically done in a TPM BIOS menu item. There's typically also another option in the BIOS and that is one to activate / enable the device, maybe as one menu item or as two. Some people may want to leave it off, others may want to turn it on and the BIOS menu gives them this option.
You seem to be describing two use cases: 1 - the tpm password must be reset (eg, because it was forgotten or the machine was given away/sold), and 2 - some people have a TPM chip but don't want it being used. I understand these two use cases and agree it would be worthwhile to provide a solution for them.
However, I don't think an additional SeaBIOS boot menu is a good solution for the above two use cases. By my estimation, in real world use, the above actions would likely come up only once every few years (if ever) for a given machine. Prompting the user on every boot for such a rare case is not a good complexity vs utility trade off.
Are there other use cases to consider, or am I misunderstanding the frequency of the above cases?
To expand on my earlier proposal, lets take the second use case - a user that does not want the tpm chip being used. This decision is a machine hardware decision and I think it is something that fits well in fw_cfg/cbfs. I could see a setting "etc/tpm-state" with a value of 0 = TPM is forced to disabled, deactivated, and unowned, and a value of 1 = TPM is forced to enabled, activated, and ownable. This allows the user to set a control policy (for the next and subsequent boots) via either their virt manager (qemu) or flash tools (coreboot). This method also indirectly addresses the first use case (password reset) by the two part sequence of disabling the tpm, rebooting, then reenabling the tpm (if desired) and rebooting. Sure, it's slightly more work for the user to reset the password, but for an action that happens so rarely I don't see it as a problem.
A TPM menu in the BIOS is probably implemented in all of today's machines where users expect to adjust those parameters. I would say it's almost expected to find the possibility to do this in the BIOS and those who are used to it would probably get more confused if it wasn't implemented this way. This of course doesn't preclude additional ways to support TPM management.
SeaBIOS doesn't have a system configuration menu though. On QEMU the system config menu is in the virt manager and on coreboot it's in various OS tools.
-Kevin
"Kevin O'Connor" kevin@koconnor.net wrote on 08/27/2014 07:00:23 PM:
On Wed, Aug 27, 2014 at 05:04:51PM -0400, Stefan Berger wrote:
On 08/27/2014 12:26 PM, Kevin O'Connor wrote:
On Wed, Aug 27, 2014 at 09:51:02AM +0200, Gerd Hoffmann wrote:
Same applies here. IMO it should be possible to manage the TPM
without
having to touch the virtual machine configuration. Persistent
storage
isn't an issue in that case, the tpm device provides that.
Realistically, though, who is ever going to manage their TPM? Perhaps I'm missing an important use case. If so, Stefan, maybe you can describe some real-world scenarios.
The above scenario with a user having forgotten the password or a
machine
being handed off to another user where the previous user did not give
up
ownership of the TPM. In these cases the user would manage the TPM via
the
BIOS in so far as he would have to give up the TPM ownership (under
physical
presence) which is typically done in a TPM BIOS menu item.
There'stypically
also another option in the BIOS and that is one to activate / enable
the
device, maybe as one menu item or as two. Some people may want to
leave it
off, others may want to turn it on and the BIOS menu gives them this
option.
You seem to be describing two use cases: 1 - the tpm password must be reset (eg, because it was forgotten or the machine was given away/sold), and 2 - some people have a TPM chip but don't want it being used. I understand these two use cases and agree it would be worthwhile to provide a solution for them.
However, I don't think an additional SeaBIOS boot menu is a good solution for the above two use cases. By my estimation, in real world use, the above actions would likely come up only once every few years (if ever) for a given machine. Prompting the user on every boot for such a rare case is not a good complexity vs utility trade off.
Are there other use cases to consider, or am I misunderstanding the frequency of the above cases?
These are probably rare events.
To expand on my earlier proposal, lets take the second use case - a user that does not want the tpm chip being used. This decision is a machine hardware decision and I think it is something that fits well in fw_cfg/cbfs. I could see a setting "etc/tpm-state" with a value of 0 = TPM is forced to disabled, deactivated, and unowned, and a value of 1 = TPM is forced to enabled, activated, and ownable. This allows the user to set a control policy (for the next and subsequent boots) via either their virt manager (qemu) or flash tools (coreboot). This method also indirectly addresses the first use case (password reset) by the two part sequence of disabling the tpm, rebooting, then reenabling the tpm (if desired) and rebooting. Sure, it's slightly more work for the user to reset the password, but for an action that happens so rarely I don't see it as a problem.
:-/ Once we push these operations to the outside of the VM, we'll need to propagate it through all the management software (if we decided to support it there then). It runs into QEMU monitor, libvirt API and virsh management tool, virt-manager, and every cloud stack that is out there needs support in its cloud controller and on the cloud mgmt. software on the host where the VM is located (and where libvirt is) and in its API, GUI, and its own CLI tools where users will control the VM's TPM from home. All this effort would be required for an operation that is rare but not 'contained' anymore, though should be there for correctness and completeness reasons.
A TPM menu in the BIOS is probably implemented in all of today's
machines
where users expect to adjust those parameters. I would say it's almost expected to find the possibility to do this in the BIOS and those
who are used
to it would probably get more confused if it wasn't implemented
this way. This
of course doesn't preclude additional ways to support TPM management.
SeaBIOS doesn't have a system configuration menu though. On QEMU the system config menu is in the virt manager and on coreboot it's in various OS tools.
There's another spec form TCG that requires the implementation of ACPI support with the possibility to send messages to the ACPI interface indicating what to do upon reboot. It seems like this ACPI offers an API and could write these messages (operations) into the TPM's NVRAM (ACPI programs the memory mapped IO of the TPM TIS interface as it seems) that the BIOS could then look at and act upon. Maybe that would be the spec to follow and 'contain' the problem to the inside of the VM. I did use some of those ideas already when the user chosen menu item was translate in an 'operation code'.
The reserved NVRAM index of the TPM for this purpose is 0x50010000. It would be easy to read the message (1 byte) from that position from the TPM's NVRAM via the BIOS. Access is basically by sending a TPM request to the TIS interface for reading data from this special NVRAM location. The more challenging part is the ACPI program that now needs to write a similar packet to the TIS interface to write the 1 byte operation code into that NVRAM location
http://www.trustedcomputinggroup.org/files/resource_files/353F514C-1A4B-B294...
Stefan