Patchwork [V4,2/8] Provide ACPI SSDT table for TPM device + S3 resume support

login
register
mail settings
Submitter Stefan Berger
Date April 12, 2011, 1:32 p.m.
Message ID <20110412133250.471022795@linux.vnet.ibm.com>
Download mbox | patch
Permalink /patch/90799/
State New
Headers show

Comments

Stefan Berger - April 12, 2011, 1:32 p.m.
This patch provides ACPI support for the TPM device. It probes for the TPM
device and only if a TPM device is found then the TPM's SSDT and TCPA table
are created. This patch also connects them to the RSDT.

Since the logging area in the TCPA table requires 64kb, the memory reserved
for ACPI tables (config.h) is increased to 96kb.

This patch requires the subsequent patch for it to compile and work.

The IRQ description in the TPM's SSDT is commented since it will be
'safer' to run the TPM in polling mode - the Linux TPM TIS driver for example
has too many issues when run in interrupt mode.

The description of the TCPA (client) table can be found here:

http://www.trustedcomputinggroup.org/resources/server_work_group_acpi_general_specification_version_10

The compiled SSDT description is also part of this patch.

v2:
  - Increasing the CONFIG_MAX_HIGHTABLE to 96kb
  - Adding cut-down tcgbios.c|h to keep SeaBIOS compiling
  - Build tpm_drivers.c and tcgbios.c
    -> TPM's SSDT and TCPA tables are now visible in Linux

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>

---
 Makefile              |   11 ++++++-
 src/acpi-tpm-ssdt.dsl |   22 +++++++++++++++
 src/acpi-tpm-ssdt.hex |   26 ++++++++++++++++++
 src/acpi.c            |   41 ++++++++++++++++++++++++++++
 src/acpi.h            |   20 +++++++++++++
 src/config.h          |    2 -
 src/tcgbios.c         |   72 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/tcgbios.h         |   57 +++++++++++++++++++++++++++++++++++++++
 8 files changed, 248 insertions(+), 3 deletions(-)
Stefan Berger - May 18, 2011, 7:49 p.m.
On 04/12/2011 09:32 AM, Stefan Berger wrote:
> +
> +static u32 add_tpm_device(void **tpm_addr, void **tcpa_addr)
> +{
> +    struct tcpa_descriptor_rev2 *tcpa;
> +
> +    *tpm_addr = NULL;
> +    *tcpa_addr = NULL;
> +
> +    if (has_working_tpm()) {
> +        u32 laml = 64 * 1024;
Kevin,

   the above line prepares 64 kb to be allocated for an ACPI table. It 
works fine if booting a VM from an image. However, when passing the 
kernel, initrd and command line parameters to Qemu directly (-kernel, 
-append, ..), the 64kb above seem to be too much -- I see errors in the 
Linux kernel's dmesg when Linux tries to access the ACPI tables. 
Lowering the above to 48kb (happens to) makes it work. I am wondering 
whether something is copying into the ACPI area or what else may be the 
reason - do you know?

> +        *tpm_addr = malloc_high(sizeof(AmlCode_TPM));
> +
> +        tcpa = malloc_high(sizeof(*tcpa) + laml);
> +        if (!tcpa || !*tpm_addr) {
> +            warn_noalloc();
> +            return 1;
> +        }
Regards,
    Stefan
Kevin O'Connor - May 18, 2011, 11:44 p.m.
On Wed, May 18, 2011 at 03:49:25PM -0400, Stefan Berger wrote:
> On 04/12/2011 09:32 AM, Stefan Berger wrote:
> >+
> >+static u32 add_tpm_device(void **tpm_addr, void **tcpa_addr)
> >+{
> >+    struct tcpa_descriptor_rev2 *tcpa;
> >+
> >+    *tpm_addr = NULL;
> >+    *tcpa_addr = NULL;
> >+
> >+    if (has_working_tpm()) {
> >+        u32 laml = 64 * 1024;
> Kevin,
> 
>   the above line prepares 64 kb to be allocated for an ACPI table.
> It works fine if booting a VM from an image. However, when passing
> the kernel, initrd and command line parameters to Qemu directly
> (-kernel, -append, ..), the 64kb above seem to be too much -- I see
> errors in the Linux kernel's dmesg when Linux tries to access the
> ACPI tables. Lowering the above to 48kb (happens to) makes it work.
> I am wondering whether something is copying into the ACPI area or
> what else may be the reason - do you know?

I don't know of any reason why that would fail.  You can print out the
address and compare it to the e820 map that SeaBIOS outputs and verify
it is properly reserved.

Does increasing the amount of guest system ram help?

BTW, is this just a buffer or could the size be dynamically
calculated?

-Kevin
Stefan Berger - May 19, 2011, 1:18 a.m.
On 05/18/2011 07:44 PM, Kevin O'Connor wrote:
> On Wed, May 18, 2011 at 03:49:25PM -0400, Stefan Berger wrote:
>> On 04/12/2011 09:32 AM, Stefan Berger wrote:
>>> +
>>> +static u32 add_tpm_device(void **tpm_addr, void **tcpa_addr)
>>> +{
>>> +    struct tcpa_descriptor_rev2 *tcpa;
>>> +
>>> +    *tpm_addr = NULL;
>>> +    *tcpa_addr = NULL;
>>> +
>>> +    if (has_working_tpm()) {
>>> +        u32 laml = 64 * 1024;
>> Kevin,
>>
>>    the above line prepares 64 kb to be allocated for an ACPI table.
>> It works fine if booting a VM from an image. However, when passing
>> the kernel, initrd and command line parameters to Qemu directly
>> (-kernel, -append, ..), the 64kb above seem to be too much -- I see
>> errors in the Linux kernel's dmesg when Linux tries to access the
>> ACPI tables. Lowering the above to 48kb (happens to) makes it work.
>> I am wondering whether something is copying into the ACPI area or
>> what else may be the reason - do you know?
> I don't know of any reason why that would fail.  You can print out the
> address and compare it to the e820 map that SeaBIOS outputs and verify
> it is properly reserved.
>
> Does increasing the amount of guest system ram help?
It helped. At some point the initrd and ACPI tables weren't overlapping 
anymore... I forgot to increase the size reserved for ACPI tables inside 
the Qemu code...

> BTW, is this just a buffer or could the size be dynamically
> calculated?
>
Following specs it's supposed to be 64kb. The BIOS writes logs into this 
area.

    Stefan
> -Kevin

Patch

Index: seabios/src/acpi-tpm-ssdt.dsl
===================================================================
--- /dev/null
+++ seabios/src/acpi-tpm-ssdt.dsl
@@ -0,0 +1,22 @@ 
+DefinitionBlock (
+    "acpi-tpm-ssdt.aml",// Output Filename
+    "SSDT",             // Signature
+    0x01,               // SSDT Compliance Revision
+    "BXPC",             // OEMID
+    "BXSSDT",           // TABLE ID
+    0x1                 // OEM Revision
+    )
+{
+    /* TPM with emulated TPM TIS interface */
+    Device (TPM) {
+        Name (_HID, EisaID ("PNP0C31"))
+        Name (_CRS, ResourceTemplate ()
+        {
+                Memory32Fixed (ReadWrite, 0xFED40000, 0x00005000)
+                //IRQNoFlags () {11}
+        })
+        Method (_STA, 0, NotSerialized) {
+            Return (0x0F)
+        }
+    }
+}
Index: seabios/src/acpi-tpm-ssdt.hex
===================================================================
--- /dev/null
+++ seabios/src/acpi-tpm-ssdt.hex
@@ -0,0 +1,26 @@ 
+/*
+ * 
+ * Intel ACPI Component Architecture
+ * ASL Optimizing Compiler version 20101013-64 [Nov 21 2010]
+ * Copyright (c) 2000 - 2010 Intel Corporation
+ * 
+ * Compilation of "out/.dsl.i" - Mon Apr  4 15:44:33 2011
+ * 
+ * C source code output
+ * AML code block contains 0x56 bytes
+ *
+ */
+unsigned char AmlCode_TPM[] =
+{
+    0x53,0x53,0x44,0x54,0x56,0x00,0x00,0x00,  /* 00000000    "SSDTV..." */
+    0x01,0x13,0x42,0x58,0x50,0x43,0x00,0x00,  /* 00000008    "..BXPC.." */
+    0x42,0x58,0x53,0x53,0x44,0x54,0x00,0x00,  /* 00000010    "BXSSDT.." */
+    0x01,0x00,0x00,0x00,0x49,0x4E,0x54,0x4C,  /* 00000018    "....INTL" */
+    0x13,0x10,0x10,0x20,0x5B,0x82,0x30,0x54,  /* 00000020    "... [.0T" */
+    0x50,0x4D,0x5F,0x08,0x5F,0x48,0x49,0x44,  /* 00000028    "PM_._HID" */
+    0x0C,0x41,0xD0,0x0C,0x31,0x08,0x5F,0x43,  /* 00000030    ".A..1._C" */
+    0x52,0x53,0x11,0x11,0x0A,0x0E,0x86,0x09,  /* 00000038    "RS......" */
+    0x00,0x01,0x00,0x00,0xD4,0xFE,0x00,0x50,  /* 00000040    ".......P" */
+    0x00,0x00,0x79,0x00,0x14,0x09,0x5F,0x53,  /* 00000048    "..y..._S" */
+    0x54,0x41,0x00,0xA4,0x0A,0x0F             /* 00000050    "TA...."   */
+};
Index: seabios/Makefile
===================================================================
--- seabios.orig/Makefile
+++ seabios/Makefile
@@ -20,7 +20,7 @@  SRC16=$(SRCBOTH) system.c disk.c font.c
 SRC32FLAT=$(SRCBOTH) post.c shadow.c memmap.c coreboot.c boot.c \
       acpi.c smm.c mptable.c smbios.c pciinit.c optionroms.c mtrr.c \
       lzmadecode.c bootsplash.c jpeg.c usb-hub.c paravirt.c dev-i440fx.c \
-      pci_region.c
+      pci_region.c tcgbios.c tpm_drivers.c
 SRC32SEG=util.c output.c pci.c pcibios.c apm.c stacks.c
 
 cc-option = $(shell if test -z "`$(1) $(2) -S -o /dev/null -xc \
@@ -192,13 +192,20 @@  $(OUT)vgabios.bin: $(OUT)vgabios.bin.raw
 	$(Q)./tools/buildrom.py $< $@
 
 ####### dsdt build rules
+src/acpi-tpm-ssdt.hex: src/acpi-tpm-ssdt.dsl
+	@echo "Compiling TPM SSDT"
+	$(Q)cpp -P $< > $(OUT)$*.dsl.i
+	$(Q)iasl -tc -p $(OUT)$* $(OUT)$*.dsl.i
+	$(Q)cp $(OUT)$*.hex $@
+	$(Q)sed -i 's/AmlCode/AmlCode_TPM/' $@
+
 src/%.hex: src/%.dsl
 	@echo "Compiling DSDT"
 	$(Q)cpp -P $< > $(OUT)$*.dsl.i
 	$(Q)iasl -tc -p $(OUT)$* $(OUT)$*.dsl.i
 	$(Q)cp $(OUT)$*.hex $@
 
-$(OUT)ccode32flat.o: src/acpi-dsdt.hex
+$(OUT)ccode32flat.o: src/acpi-dsdt.hex src/acpi-tpm-ssdt.hex
 
 ####### Kconfig rules
 export HOSTCC             := $(CC)
Index: seabios/src/acpi.c
===================================================================
--- seabios.orig/src/acpi.c
+++ seabios/src/acpi.c
@@ -13,6 +13,8 @@ 
 #include "pci_regs.h" // PCI_INTERRUPT_LINE
 #include "paravirt.h"
 #include "dev-i440fx.h" // piix4_fadt_init
+#include "acpi-tpm-ssdt.hex"
+#include "tcgbios.h" // has_working_tpm
 
 /****************************************************/
 /* ACPI tables init */
@@ -586,6 +588,39 @@  static const struct pci_device_id acpi_f
     PCI_DEVICE_END,
 };
 
+
+static u32 add_tpm_device(void **tpm_addr, void **tcpa_addr)
+{
+    struct tcpa_descriptor_rev2 *tcpa;
+
+    *tpm_addr = NULL;
+    *tcpa_addr = NULL;
+
+    if (has_working_tpm()) {
+        u32 laml = 64 * 1024;
+        *tpm_addr = malloc_high(sizeof(AmlCode_TPM));
+
+        tcpa = malloc_high(sizeof(*tcpa) + laml);
+        if (!tcpa || !*tpm_addr) {
+            warn_noalloc();
+            return 1;
+        }
+
+        if (*tpm_addr)
+            memcpy(*tpm_addr, AmlCode_TPM, sizeof(AmlCode_TPM));
+
+        memset(tcpa, 0x0, sizeof(*tcpa) + laml);
+        u64 lasa = (u32)tcpa + sizeof(*tcpa);
+
+        tcpa->laml = laml;
+        tcpa->lasa = lasa;
+        build_header((void*)tcpa, TCPA_SIGNATURE, sizeof(*tcpa), 2);
+
+        *tcpa_addr = tcpa;
+    }
+    return 0;
+}
+
 struct rsdp_descriptor *RsdpAddr;
 
 #define MAX_ACPI_TABLES 20
@@ -642,6 +677,12 @@  acpi_bios_init(void)
         }
     }
 
+    void *tcpa, *tpm;
+    if (add_tpm_device(&tpm, &tcpa))
+        return;
+    ACPI_INIT_TABLE(tpm);
+    ACPI_INIT_TABLE(tcpa);
+
     struct rsdt_descriptor_rev1 *rsdt;
     size_t rsdt_len = sizeof(*rsdt) + sizeof(u32) * tbl_idx;
     rsdt = malloc_high(rsdt_len);
Index: seabios/src/acpi.h
===================================================================
--- seabios.orig/src/acpi.h
+++ seabios/src/acpi.h
@@ -98,4 +98,24 @@  struct fadt_descriptor_rev1
 #endif
 } PACKED;
 
+
+struct rsdt_descriptor {
+    ACPI_TABLE_HEADER_DEF
+    u32 entry[1];
+} PACKED;
+
+#define TCPA_SIGNATURE 0x41504354
+struct tcpa_descriptor_rev2
+{
+    ACPI_TABLE_HEADER_DEF
+    u16  platform_class;
+    u32  laml;
+    u64  lasa;
+} PACKED;
+
+/* TCPA ACPI definitions */
+#define TCPA_ACPI_CLASS_CLIENT          0
+#define TCPA_ACPI_CLASS_SERVER          1
+
+
 #endif // acpi.h
Index: seabios/src/config.h
===================================================================
--- seabios.orig/src/config.h
+++ seabios/src/config.h
@@ -26,7 +26,7 @@ 
 // Space to reserve in f-segment for dynamic allocations
 #define CONFIG_MAX_BIOSTABLE 2048
 // Space to reserve in high-memory for tables
-#define CONFIG_MAX_HIGHTABLE (64*1024)
+#define CONFIG_MAX_HIGHTABLE (96*1024)
 // Largest supported externaly facing drive id
 #define CONFIG_MAX_EXTDRIVE 16
 
Index: seabios/src/tcgbios.c
===================================================================
--- /dev/null
+++ seabios/src/tcgbios.c
@@ -0,0 +1,72 @@ 
+//  Implementation of the TCG BIOS extension according to the specification
+//  described in
+//  https://www.trustedcomputinggroup.org/specs/PCClient/TCG_PCClientImplementationforBIOS_1-20_1-00.pdf
+//
+//  Copyright (C) 2006-2011 IBM Corporation
+//  Copyright (C) 2006-2011 Stefan Berger <stefanb@us.ibm.com>
+//
+// This file may be distributed under the terms of the GNU LGPLv3 license.
+
+
+#include "config.h"
+
+#include "types.h"
+#include "tpm_drivers.h" // tpm_drivers[]
+
+
+typedef struct {
+    u8            tpm_probed:1;
+    u8            tpm_found:1;
+    u8            tpm_working:1;
+    u8            if_shutdown:1;
+    u8            tpm_driver_to_use:4;
+} tcpa_state_t;
+
+
+static tcpa_state_t tcpa_state = {
+    .tpm_driver_to_use = TPM_INVALID_DRIVER,
+};
+
+extern struct tpm_driver tpm_drivers[];
+
+
+/********************************************************
+  Extensions for TCG-enabled BIOS
+ *******************************************************/
+
+
+static u32
+is_tpm_present(void)
+{
+    u32 rc = 0;
+    unsigned int i;
+
+    for (i = 0; i < TPM_NUM_DRIVERS; i++) {
+        struct tpm_driver *td = &tpm_drivers[i];
+        if (td->probe() != 0) {
+            td->init();
+            tcpa_state.tpm_driver_to_use = i;
+            rc = 1;
+            break;
+        }
+    }
+
+    return rc;
+}
+
+
+int
+has_working_tpm(void)
+{
+    if (!tcpa_state.tpm_probed) {
+        tcpa_state.tpm_probed = 1;
+        tcpa_state.tpm_found = (is_tpm_present() != 0);
+        tcpa_state.tpm_working = 1;
+    }
+    if (!tcpa_state.tpm_working)
+        return 0;
+
+    return tcpa_state.tpm_found;
+}
+
+
Index: seabios/src/tcgbios.h
===================================================================
--- /dev/null
+++ seabios/src/tcgbios.h
@@ -0,0 +1,57 @@ 
+#ifndef TCGBIOS_H
+#define TCGBIOS_H
+
+
+#define TPM_OK                          0x0
+#define TPM_RET_BASE                    0x1
+#define TCG_GENERAL_ERROR               (TPM_RET_BASE + 0x0)
+#define TCG_TPM_IS_LOCKED               (TPM_RET_BASE + 0x1)
+#define TCG_NO_RESPONSE                 (TPM_RET_BASE + 0x2)
+#define TCG_INVALID_RESPONSE            (TPM_RET_BASE + 0x3)
+#define TCG_INVALID_ACCESS_REQUEST      (TPM_RET_BASE + 0x4)
+#define TCG_FIRMWARE_ERROR              (TPM_RET_BASE + 0x5)
+#define TCG_INTEGRITY_CHECK_FAILED      (TPM_RET_BASE + 0x6)
+#define TCG_INVALID_DEVICE_ID           (TPM_RET_BASE + 0x7)
+#define TCG_INVALID_VENDOR_ID           (TPM_RET_BASE + 0x8)
+#define TCG_UNABLE_TO_OPEN              (TPM_RET_BASE + 0x9)
+#define TCG_UNABLE_TO_CLOSE             (TPM_RET_BASE + 0xa)
+#define TCG_RESPONSE_TIMEOUT            (TPM_RET_BASE + 0xb)
+#define TCG_INVALID_COM_REQUEST         (TPM_RET_BASE + 0xc)
+#define TCG_INVALID_ADR_REQUEST         (TPM_RET_BASE + 0xd)
+#define TCG_WRITE_BYTE_ERROR            (TPM_RET_BASE + 0xe)
+#define TCG_READ_BYTE_ERROR             (TPM_RET_BASE + 0xf)
+#define TCG_BLOCK_WRITE_TIMEOUT         (TPM_RET_BASE + 0x10)
+#define TCG_CHAR_WRITE_TIMEOUT          (TPM_RET_BASE + 0x11)
+#define TCG_CHAR_READ_TIMEOUT           (TPM_RET_BASE + 0x12)
+#define TCG_BLOCK_READ_TIMEOUT          (TPM_RET_BASE + 0x13)
+#define TCG_TRANSFER_ABORT              (TPM_RET_BASE + 0x14)
+#define TCG_INVALID_DRV_FUNCTION        (TPM_RET_BASE + 0x15)
+#define TCG_OUTPUT_BUFFER_TOO_SHORT     (TPM_RET_BASE + 0x16)
+#define TCG_FATAL_COM_ERROR             (TPM_RET_BASE + 0x17)
+#define TCG_INVALID_INPUT_PARA          (TPM_RET_BASE + 0x18)
+#define TCG_TCG_COMMAND_ERROR           (TPM_RET_BASE + 0x19)
+#define TCG_INTERFACE_SHUTDOWN          (TPM_RET_BASE + 0x20)
+//define TCG_PC_UNSUPPORTED             (TPM_RET_BASE + 0x21)
+#define TCG_PC_TPM_NOT_PRESENT          (TPM_RET_BASE + 0x22)
+#define TCG_PC_TPM_DEACTIVATED          (TPM_RET_BASE + 0x23)
+
+
+#define TPM_INVALID_ADR_REQUEST          TCG_INVALID_ADR_REQUEST
+#define TPM_IS_LOCKED                    TCG_TPM_IS_LOCKED
+#define TPM_INVALID_DEVICE_ID            TCG_INVALID_DEVICE_ID
+#define TPM_INVALID_VENDOR_ID            TCG_INVALID_VENDOR_ID
+//define TPM_RESERVED_REG_INVALID
+#define TPM_FIRMWARE_ERROR               TCG_FIRMWARE_ERROR
+#define TPM_UNABLE_TO_OPEN               TCG_UNABLE_TO_OPEN
+#define TPM_UNABLE_TO_CLOSE              TCG_UNABLE_TO_CLOSE
+#define TPM_INVALID_RESPONSE             TCG_INVALID_RESPONSE
+#define TPM_RESPONSE_TIMEOUT             TCG_RESPONSE_TIMEOUT
+#define TPM_INVALID_ACCESS_REQUEST       TCG_INVALID_ACCESS_REQUEST
+#define TPM_TRANSFER_ABORT               TCG_TRANSFER_ABORT
+#define TPM_GENERAL_ERROR                TCG_GENERAL_ERROR
+
+
+int has_working_tpm(void);
+
+
+#endif /* TCGBIOS_H */