For automated management of a TPM device, implement the TCG Physical Presence Interface Specification that allows a root user on Linux (for example) to set an opcode for a sequence of TPM operations that the BIOS is supposed to execute upon reboot of the physical or virtual machine. A sequence of operations may for example involve giving up ownership of the TPM and activating and enabling the device.
The sequences of operations are defined in table 2 in the specs to be found at the following link: http://www.trustedcomputinggroup.org/resources/tcg_physical_presence_interface_specification As an example, in recent versions of Linux the opcode (5) can be set as follows: cd /sys/devices/pnp0/00\:04/ppi echo 5 > request This ACPI implementation assumes that the underlying firmware (SeaBIOS) has 'thrown an anchor' into the f-segment. The anchor is identified by two signatures (TCG_MAGIC) surrounding a 64bit pointer. The structure in the f-segment is write-protected and holds a pointer to a structure in high memmory area where the ACPI code writes the opcode into and where it can read the last response from the BIOS. The supported opcodes are 1-11, 14, and 21-22. (see table 2 in spec) Also '0' is supported to 'clear' an intention. Signed-off-by: Stefan Berger <stef...@linux.vnet.ibm.com> Cc: Michael Tsirkin <m...@redhat.com> Cc: Kevin O'Connor <ke...@koconnor.net> --- hw/i386/acpi-tpm-core.dsl | 277 ++++++++++++++++++++++++++++++++++++++++++++++ hw/i386/acpi-tpm2.dsl | 27 +++++ hw/i386/q35-acpi-dsdt.dsl | 1 + hw/i386/ssdt-tpm.dsl | 12 +- 4 files changed, 306 insertions(+), 11 deletions(-) create mode 100644 hw/i386/acpi-tpm-core.dsl create mode 100644 hw/i386/acpi-tpm2.dsl diff --git a/hw/i386/acpi-tpm-core.dsl b/hw/i386/acpi-tpm-core.dsl new file mode 100644 index 0000000..20fdee4 --- /dev/null +++ b/hw/i386/acpi-tpm-core.dsl @@ -0,0 +1,277 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Note: This implementation follows the TCG specification + * 'TCG Physical Presence Interface Specification' of which the latest + * version can be found here: + * http://www.trustedcomputinggroup.org/resources/tcg_physical_presence_interface_specification + */ + +#include "hw/acpi/tpm.h" + +#define TCG_MAGIC 0x41504354 + +/* + * common code for TPM SSDT and TPM2 + */ +Name (_HID, EisaID ("PNP0C31")) +Name (_CRS, ResourceTemplate () +{ + Memory32Fixed (ReadWrite, TPM_TIS_ADDR_BASE, TPM_TIS_ADDR_SIZE) + // older Linux tpm_tis drivers do not work with IRQ + //IRQNoFlags () {TPM_TIS_IRQ} +}) + +Method (_STA, 0, NotSerialized) { + Return (0x0F) +} + +OperationRegion (TTIS, SystemMemory, + TPM_TIS_ADDR_BASE, TPM_TIS_ADDR_SIZE) + +// Define TPM Debug register +Field(TTIS, AnyAcc, NoLock, Preserve) { + Offset (0xf90), + TDBG, 32 // QEMU TIS Debug +} + +// Last accepted opcode +NAME(OP, Zero) + +// Field to access f-segment +OperationRegion (FSEG, SystemMemory, 0xf0000, 0x10000) +Field(FSEG, WordAcc, NoLock, Preserve) { + FDAT, 0x80000 +} + +// Read a 32bit word from memory; this function +// is used for accessing the bitfield in FDAT +Method (RD32, 2, NotSerialized) { + Store(DerefOf(Index(Arg0, Arg1)), Local0) + Increment(Arg1) + Store(DerefOf(Index(Arg0, Arg1)), Local1) + Increment(Arg1) + Store(DerefOf(Index(Arg0, Arg1)), Local2) + Increment(Arg1) + Store(DerefOf(Index(Arg0, Arg1)), Local3) + Or(Local0, ShiftLeft(Local1, 8), Local0) + Or(Local0, ShiftLeft(Local2, 16), Local0) + Or(Local0, ShiftLeft(Local3, 24), Local0) + Return (Local0) +} + +// The address in high memory where we exchange +// info with the BIOS +Name(ADDR, 0x0) + +// Scan the f-segment for our anchor with +// signature TCG_MAGIC +Method (SCAN, 0, NotSerialized) { + Store( Zero, Local1) + While (LLess(Local1, 0x10000)) { + Store(DerefOf(Index(FDAT, Local1)), Local0) + // first scan for single byte + If (LEqual(Local0, 0x54)) { + // Check two two 4-byte signatures + If (LAnd( + LEqual(RD32(FDAT, Local1 ), TCG_MAGIC), + LEqual(RD32(FDAT, Add(Local1, 8)), TCG_MAGIC) + ) + ) { + //Store ( RD32(FDAT, Add(Local1, 4)), TDBG) + Store ( RD32(FDAT, Add(Local1, 4)), ADDR) + Break + } + } + Add(Local1, 16, Local1) + } +} + +// Write given opcode into RAM +Method (WRAM, 1, NotSerialized) { + If (LEqual(ADDR, 0x0)) { + // find high memory reserved by BIOS; this will + // set ADDR + SCAN( ) + } + If (LNotEqual(ADDR, 0x0)) { + // Write to high memory pointed to by ADDR + OperationRegion (HIGH, SystemMemory, ADDR, 0x10) + Field(HIGH, AnyAcc, NoLock, Preserve) { + SIG1, 32, + SIZE, 16, + CODE, 8 + } + If (LAnd( + LEqual(SIG1, TCG_MAGIC), + LGreaterEqual(SIZE, 1)) + ) { + // Remember last opcode in CODE + Store(Arg0, CODE) + // Write opcode for BIOS to find + Store(Arg0, OP) + Return ( 0 ) + } + } + Return ( 1 ) +} + + +Method (RRAM, 0, NotSerialized) { + Name (OPRE, Package(3) { 1, 0, 0}) + If (LEqual(ADDR, 0x0)) { + // find high memory reserved by BIOS; this will + // set ADDR + SCAN( ) + } + If (LNotEqual(ADDR, 0x0)) { + // Read from memory pointed to by ADDR + OperationRegion (HIGH, SystemMemory, ADDR, 0x10) + Field(HIGH, AnyAcc, NoLock, Preserve) { + SIG1, 32, + SIZE, 16, + CODE, 8, + SUCC, 8, + CODO, 8, + RESP, 32 + } + // Check signature and sufficient space + If (LAnd( + LEqual(SIG1, TCG_MAGIC), + LGreaterEqual(SIZE, 7) + )) { + Store(SUCC, Index(OPRE, 0)) + Store(CODO, Index(OPRE, 1)) + Store(RESP, Index(OPRE, 2)) + } + } + return (OPRE) +} + +Method (_DSM, 4, NotSerialized) { + If (LEqual (Arg0, ToUUID("3DDDFAA6-361B-4EB4-A424-8D10089D1653"))) { + + // only supporting API revision 1 + If (LNotEqual (Arg1, 1)) { + Return (Buffer (1) {0}) + } + + Store(ToInteger(Arg2), Local0) + // standard DSM query function + If (LEqual (Local0, 0)) { + Return (Buffer () {0xFF, 0x01}) + } + + // interface version + If (LEqual (Local0, 1)) { + Return ("1.2") + } + + // submit TPM operation + If (LEqual (Local0, 2)) { + // get opcode from package + Store(DerefOf(Index(Arg3, 0)), Local0) + // check for supported opcode + // supported opcodes: 0, 1-11, 14, 21-22 + If (LOr( + LOr( + LAnd(LGreaterEqual(Local0, 0), + LLessEqual(Local0, 11)), + LEqual(Local0, 14) + ), + LAnd(LGreaterEqual(Local0, 21), + LLessEqual(Local0, 22)) + ) + ) { + // Write the OP into TPM NVRAM + Store(WRAM ( Local0 ), Local1) + return (Local1) + } else { + Return (1) + } + } + + // get pending TPM operation + If (LEqual (Local0, 3)) { + NAME(PEOP, Package(2) { 0, 0 }) + + Store ( 0 , Index(PEOP, 0)) + Store ( OP, Index(PEOP, 1)) + + Return (PEOP) + } + + // action to transition to pre-OS env. + If (LEqual (Local0, 4)) { + return (2) // Requiring reboot + } + + // get pre-OS TPM operation response + If (LEqual (Local0, 5)) { + Store (RRAM(), Local0) + return ( Local0 ) + } + + // preferred user language + If (LEqual (Local0, 6)) { + return (3) // Not implemented + } + + // submit TPM operation v2 + If (LEqual (Local0, 7)) { + Store(DerefOf(Index(Arg3, 0)), Local0) + // supported opcodes: 0, 1-11, 14, 21-22 + If (LOr( + LOr( + LAnd(LGreaterEqual(Local0, 0), + LLessEqual(Local0, 11)), + LEqual(Local0, 14) + ), + LAnd(LGreaterEqual(Local0, 21), + LLessEqual(Local0, 22)) + ) + ) { + // Write the OP into TPM NVRAM + Store(WRAM ( Local0 ), Local1) + return (Local1) + } else { + Return (1) + } + } + + // get user confirmation status + If (LEqual (Local0, 8)) { + Store(DerefOf(Index(Arg3,0)), Local0) + // supported opcodes: 0, 1-11, 14, 21-22 + If (LOr( + LOr( + LAnd(LGreaterEqual(Local0, 0), + LLessEqual(Local0, 11)), + LEqual(Local0, 14) + ), + LAnd(LGreaterEqual(Local0, 21), + LLessEqual(Local0, 22)) + ) + ) { + Return (4) // allowed, no user required + } else { + Return (0) // not implemented + } + } + } + return (Buffer() { 0x0 }) +} + diff --git a/hw/i386/acpi-tpm2.dsl b/hw/i386/acpi-tpm2.dsl new file mode 100644 index 0000000..3d31125 --- /dev/null +++ b/hw/i386/acpi-tpm2.dsl @@ -0,0 +1,27 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Note: This implementation follows the TCG specification + * 'TCG Physical Presence Interface Specification' of which the latest + * version can be found here: + * http://www.trustedcomputinggroup.org/resources/tcg_physical_presence_interface_specification + */ + +Scope(\_SB) { + Device(TPM2) { +#include "acpi-tpm-core.dsl" + } +} diff --git a/hw/i386/q35-acpi-dsdt.dsl b/hw/i386/q35-acpi-dsdt.dsl index 16eaca3..a73035d 100644 --- a/hw/i386/q35-acpi-dsdt.dsl +++ b/hw/i386/q35-acpi-dsdt.dsl @@ -115,6 +115,7 @@ DefinitionBlock ( } #include "acpi-dsdt-hpet.dsl" +#include "acpi-tpm2.dsl" /**************************************************************** diff --git a/hw/i386/ssdt-tpm.dsl b/hw/i386/ssdt-tpm.dsl index 75d9691..32d268c 100644 --- a/hw/i386/ssdt-tpm.dsl +++ b/hw/i386/ssdt-tpm.dsl @@ -12,7 +12,6 @@ * You should have received a copy of the GNU General Public License along * with this program; if not, see <http://www.gnu.org/licenses/>. */ -#include "hw/acpi/tpm.h" ACPI_EXTRACT_ALL_CODE ssdt_tpm_aml @@ -28,16 +27,7 @@ DefinitionBlock ( Scope(\_SB) { /* TPM with emulated TPM TIS interface */ Device (TPM) { - Name (_HID, EisaID ("PNP0C31")) - Name (_CRS, ResourceTemplate () - { - Memory32Fixed (ReadWrite, TPM_TIS_ADDR_BASE, TPM_TIS_ADDR_SIZE) - // older Linux tpm_tis drivers do not work with IRQ - //IRQNoFlags () {TPM_TIS_IRQ} - }) - Method (_STA, 0, NotSerialized) { - Return (0x0F) - } +#include "acpi-tpm-core.dsl" } } } -- 1.9.3