qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Stefan Berger <stefanb@linux.vnet.ibm.com>
To: qemu-devel@nongnu.org
Cc: safford@watson.ibm.com, Kevin O'Connor <kevin@koconnor.net>,
	Stefan Berger <stefanb@linux.vnet.ibm.com>,
	quan.xu@intel.com, mst@redhat.com
Subject: [Qemu-devel] [PATCH 2/5] Support Physical Presence Interface Spec
Date: Wed, 15 Apr 2015 18:38:45 -0400	[thread overview]
Message-ID: <1429137528-1069064-3-git-send-email-stefanb@linux.vnet.ibm.com> (raw)
In-Reply-To: <1429137528-1069064-1-git-send-email-stefanb@linux.vnet.ibm.com>

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 <stefanb@linux.vnet.ibm.com>
Cc: Michael Tsirkin <mst@redhat.com>
Cc: Kevin O'Connor <kevin@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

  parent reply	other threads:[~2015-04-15 22:39 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-04-15 22:38 [Qemu-devel] [PATCH 0/5] Extend TPM support with a QEMU-external TPM Stefan Berger
2015-04-15 22:38 ` [Qemu-devel] [PATCH 1/5] Provide support for the CUSE TPM Stefan Berger
2015-04-15 22:38 ` Stefan Berger [this message]
2015-04-15 22:38 ` [Qemu-devel] [PATCH 3/5] Introduce condition to notifiy waiters of completed command Stefan Berger
2015-04-15 22:38 ` [Qemu-devel] [PATCH 4/5] Introduce condition in TPM backend for notification Stefan Berger
2015-04-15 22:38 ` [Qemu-devel] [PATCH 5/5] Add support for VM suspend/resume for TPM TIS Stefan Berger
2015-04-16 13:35 ` [Qemu-devel] [PATCH 0/5] Extend TPM support with a QEMU-external TPM Igor Mammedov
2015-04-16 14:05   ` Stefan Berger
2015-04-22  7:00     ` Igor Mammedov
2015-04-22 18:18       ` Stefan Berger
2015-04-29  9:06         ` Igor Mammedov
2015-04-29 16:42           ` Stefan Berger
2015-05-04  9:16             ` Igor Mammedov
2015-05-04 15:22               ` Stefan Berger
2015-05-04 16:16                 ` Kevin O'Connor
2015-05-04 18:39                   ` Stefan Berger
2015-05-04 21:41                     ` Igor Mammedov
2015-05-05  2:50                       ` Kevin O'Connor
2015-05-05 17:42                         ` Stefan Berger
2015-04-16 18:55   ` Michael S. Tsirkin
2015-04-16 19:21     ` Stefan Berger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1429137528-1069064-3-git-send-email-stefanb@linux.vnet.ibm.com \
    --to=stefanb@linux.vnet.ibm.com \
    --cc=kevin@koconnor.net \
    --cc=mst@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=quan.xu@intel.com \
    --cc=safford@watson.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).