From: Stefan Berger stefanb@linux.vnet.ibm.com
To (try to) avoid SeaBIOS from having to implement a menu for managing the various states of a TPM device, implement the TCG Physical Presence Interface Specification that allows a root user on Linux (for example) to set the 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 operation 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_interfa...
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 > ppi
This ACPI implementation writes the opcode into the TPM's NVRAM at index 0x5001 0000 (to be adapted), which SeaBIOS can then read and react to. An alernative would be to have ACPI write the byte into a memory area that SeaBIOS would have to be able to find upon reboot.
More than a single byte of persistent memory will be needed once SeaBIOS supports this since it must store a status byte about the last operation, the opcode that was acted on, and the return code that the TPM gave (see ACPI function 5).
Only a subset of the defined opcodes will be supported. Tentatively opcodes 1-11, 14 will be supported.
Signed-off-by: Stefan Berger stefanb@linux.vnet.ibm.com --- hw/i386/ssdt-tpm.dsl | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+)
diff --git a/hw/i386/ssdt-tpm.dsl b/hw/i386/ssdt-tpm.dsl index 75d9691..678b212 100644 --- a/hw/i386/ssdt-tpm.dsl +++ b/hw/i386/ssdt-tpm.dsl @@ -12,6 +12,14 @@ * 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_interfa... + */ + #include "hw/acpi/tpm.h"
ACPI_EXTRACT_ALL_CODE ssdt_tpm_aml @@ -38,6 +46,206 @@ DefinitionBlock ( Method (_STA, 0, NotSerialized) { Return (0x0F) } + // Last accepted opcode + NAME(OP, Zero) + OperationRegion (TTIS, SystemMemory, + TPM_TIS_ADDR_BASE, TPM_TIS_ADDR_SIZE) + + // Define TIS registers + Field(TTIS, AnyAcc, NoLock, Preserve) { + Offset (0x00), + TACC, 8, // Access register + Offset (0x18), + TSTS, 8, // Status register + Offset (0x24), + TDAT, 8, // Data register + Offset (0xf90), + TDBG, 32 // QEMU TIS Debug + } + // TPM_NV_WriteValue for writing 1 byte to a NVRAM index + Name (NVWR, Package() { + 0x00, 0xc1, + 0x00, 0x00, 0x00, 0x17, // total length + 0x00, 0x00, 0x00, 0xcd, // opcode + 0x50, 0x01, 0x00, 0x00, // index + 0x00, 0x00, 0x00, 0x00, // offset + 0x00, 0x00, 0x00, 0x01, // payload length + 0x00 + }) + // Buffer to read TPM responses into + Name (BUFF, Buffer(0xa){}) + // Last TPM return code from writing to TPM NVRAM + Name (WRET, 0) + + // Write into NVRAM of TPM + Method ( _WNV, 1, NotSerialized) { + // Any early return will indicate a failed execution + Store(0xffffffff, WRET) + + // Request access to locty 0 + Store(0x02, TACC) + + // Make sure we are active + Store(TACC, Local0) + If (LNotEqual(And(Local0, 0xa0), 0xa0)) { + return (16) // EBUSY + } + + // Set the Command Ready Flag + Store(0x40, TSTS) + + // Make sure we are ready + Store(TSTS, Local0) + If (LNotEqual(And(Local0, 0x40), 0x40)) { + return (14) // EFAULT + } + + // Write the user-requested op-code into the NVRAM command + Store(Arg0, Index(NVWR, 0x16)) + + // Write the TPM_NV_WriteValue into the data buffer + Store(0x0, Local0) + While (LLess(Local0, SizeOf(NVWR))) { + Store(DerefOf(Index(NVWR, Local0)), TDAT) + Increment(Local0) + } + + // Go: have TPM process the command + Store(0x20, TSTS) + + // Wait for response to be ready + Store(0, Local0) + While (LLess(local0, 0x30)) { + Store(TSTS, Local1) + if (LEqual(And(Local1, 0x10), 0x10)) { + break + } + Sleep(10) + Increment(Local0) + } + + // Read the result into BUFF + Store(0, Local0) + While (LLess(Local0, SizeOf(BUFF))) { + Store(TDAT, Index(BUFF, Local0)) + Increment(Local0) + } + + // Extract return code from response + Mid(BUFF, 6, 4, WRET) + + Return (0) + } + 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: 1-11, 14 + If (LOr(LAnd(LGreaterEqual(Local0, 1), LLessEqual(Local0, 11)), LEqual(Local0, 14))) { + // Write the OP into TPM NVRAM + Store(_WNV ( Local0 ), Local1) + if (LNotEqual(Local1, 0)) { + return (Local1) + } + // Check for error during NVRAM write + if (LEqual(WRET, 0)) { + Store(Local0, OP) + Return (0) + } else { + Return (1) + } + } else { + Return (1) + } + } + + // get pending TPM operation + If (LEqual (Local0, 3)) { + NAME(PEOP, Package(2) { 0, 0 }) + + // Check for error during NVRAM write + if (LEqual(WRET, 0)) { + Store ( 0, Index(PEOP, 0)) + } else { + Store ( 1, 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)) { + // missing BIOS support + Name (OPRE, Package(3) { 1, 0, 0}) + return (OPRE) + } + + // 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: 1-11, 14 + If (LOr(LAnd(LGreaterEqual(Local0, 1), LLessEqual(Local0, 11)), LEqual(Local0, 14))) { + // Write the OP into TPM NVRAM + Store(_WNV ( Local0 ), Local1) + if (LNotEqual(Local1, 0)) { + return (Local1) + } + // Check for error during NVRAM write + if (LEqual(WRET, 0)) { + // Remember opcode locally + Store(Local0, OP) + Return (0) + } else { + Return (1) + } + } else { + Return (1) + } + } + + // get user confirmation status + If (LEqual (Local0, 8)) { + Store(DerefOf(Index(Arg3,0)), Local0) + If (LLessEqual(Local0, 22)) { + Return (4) // allowed, no user required + } else { + Return (0) // not implemented + } + } + } + return (Buffer() { 0x0 }) + } } } }
On 09/02/2014 10:27 AM, Stefan Berger wrote:
From: Stefan Berger stefanb@linux.vnet.ibm.com
To (try to) avoid SeaBIOS from having to implement a menu for managing the various states of a TPM device, implement the TCG Physical Presence Interface Specification that allows a root user on Linux (for example) to set the 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 operation 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_interfa...
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 > ppi
This ACPI implementation writes the opcode into the TPM's NVRAM at index 0x5001 0000 (to be adapted), which SeaBIOS can then read and react to. An alernative would be to have ACPI write the byte into a memory area that SeaBIOS would have to be able to find upon reboot.
More than a single byte of persistent memory will be needed once SeaBIOS supports this since it must store a status byte about the last operation, the opcode that was acted on, and the return code that the TPM gave (see ACPI function 5).
Only a subset of the defined opcodes will be supported. Tentatively opcodes 1-11, 14 will be supported.
So here now for my own comments:
The above referenced spec defines the NVRAM area 0x5001 0000 to be used for "BIOS TPM Management flags" (page 5/35). The particular problem with this NVRAM area is that this particular area would have to be defined during TPM manufacturing due to bit 28 set in this index, which has a particular meaning. Defining it in a software TPM is easy, but doesn't address TPMs out there in the field and the TPMs I looked at do not define this area. We could use NVRAM area 0x4001 0000 instead, which could be created by the BIOS if not found. The next question then is about the choice of protection flags to be used for this area. In general 4 different types of flags exist for allowing writing to an NVRAM area: writing only under physical presence, writing with NVRAM area authentication, writing with authentication using the owner password, or 'WRITEDEFINE'. This is described in the TPM specs in section 20.1 action 9d. (http://www.trustedcomputinggroup.org/resources/tpm_main_specification -- part 3 - commands). For ACPI anything with authentication seems too complicated since a session would have to be opened and usage of hashing is required. Only allowing writing to the area during physical presence is also not possible when the OS is running (BIOS gives up physical presence). So what remains is 'WRITEDEFINE', which allows to lock the area permanently once '0' bytes are written to it. This also seems impractical since anyone could now stop the mechanism from working.
So, to implement this spec., the only option seems to be to have a reserved memory area allocated by the BIOS for the ACPI code to write data into. The BIOS would then have to know the address and find the data on reboot. SeaBIOS could try to reserve such an area, find the SSDT for the TPM and overwrite a special marker (address) left in the TPM's SSDT, such as an address like 0x12345678, which would presumably be visible after compilation.
Thoughts?
Regards, Stefan