Closed biergaizi closed 2 years ago
Documentation: https://github.com/qemu/qemu/blob/master/docs/specs/tpm.txt
The TPM device is defined with ACPI ID "PNP0C31". QEMU builds a SSDT and passes it into the guest through the fw_cfg device. The device description contains the base address of the TIS interface 0xfed40000 and the size of the MMIO area (0x5000). In case a TPM2 is used by QEMU, a TPM2 ACPI table is also provided. The device is described to be used in polling mode rather than interrupt mode primarily because no unused IRQ could be found.
The swtpm program behaves like a hardware TPM and therefore needs to be initialized by the firmware running inside the QEMU virtual machine. One necessary step for initializing the device is to send the TPM_Startup command to it. SeaBIOS, for example, has been instrumented to initialize a TPM 1.2 or TPM 2 device using this command.
Apparently the TPM was not detected because the hardware was never initialized. I patched the source code of coreboot to select the LPC_TPM driver for Q35 board in coreboot, and enabled TPM and measured boot. But the TPM still fails to work.
TPM: pcr 3 measure 00090000 @ 4224: 61dda497607f8802039a5c8d4654a25040f21a9f
lpc_tpm: Read reg 0x18 returns 0xff
lpc_tpm: Read reg 0x19 returns 0xffff
lpc_tpm: Write reg 0x18 with 0x0
lpc_tpm: Write reg 0x18 with 0xc1
lpc_tpm: Write reg 0x18 with 0x0
lpc_tpm: Write reg 0x18 with 0x0
lpc_tpm: Write reg 0x18 with 0x0
lpc_tpm: Write reg 0x18 with 0x22
lpc_tpm: Write reg 0x18 with 0x0
lpc_tpm: Write reg 0x18 with 0x0
lpc_tpm: Write reg 0x18 with 0x0
lpc_tpm: Write reg 0x18 with 0x14
lpc_tpm: Write reg 0x18 with 0x0
lpc_tpm: Write reg 0x18 with 0x0
lpc_tpm: Write reg 0x18 with 0x0
lpc_tpm: Write reg 0x18 with 0x3
lpc_tpm: Write reg 0x18 with 0x61
lpc_tpm: Write reg 0x18 with 0xdd
lpc_tpm: Write reg 0x18 with 0xa4
lpc_tpm: Write reg 0x18 with 0x97
lpc_tpm: Write reg 0x18 with 0x60
lpc_tpm: Write reg 0x18 with 0x7f
lpc_tpm: Write reg 0x18 with 0x88
lpc_tpm: Write reg 0x18 with 0x2
lpc_tpm: Write reg 0x18 with 0x3
lpc_tpm: Write reg 0x18 with 0x9a
lpc_tpm: Write reg 0x18 with 0x5c
lpc_tpm: Write reg 0x18 with 0x8d
lpc_tpm: Write reg 0x18 with 0x46
lpc_tpm: Write reg 0x18 with 0x54
lpc_tpm: Write reg 0x18 with 0xa2
lpc_tpm: Write reg 0x18 with 0x50
lpc_tpm: Write reg 0x18 with 0x40
lpc_tpm: Write reg 0x18 with 0xf2
lpc_tpm: Write reg 0x18 with 0x1a
lpc_tpm: Read reg 0x18 returns 0xff
lpc_tpm: Read reg 0x18 returns 0xff
lpc_tpm: Read reg 0x19 returns 0xffff
lpc_tpm: Write reg 0x18 with 0x9f
lpc_tpm: Read reg 0x18 returns 0xff
lpc_tpm: Read reg 0x18 returns 0xff
lpc_tpm: Read reg 0x18 returns 0xff
src/drivers/pc80/tpm/tpm.c:515 unexpected TPM status 0xff
src/drivers/pc80/tpm/tpm.c:703 failed sending data to TPM
TPM: command 0x14 send/receive failed: 0x10000001
Any tip on how to initialize the TPM?
Logs from swtpm.
Ctrl Cmd: length 4
00 00 00 10
Ctrl Rsp: length 4
00 00 00 00
SWTPM_IO_Read: length 10
80 01 00 00 00 0A 00 00 01 81
SWTPM_IO_Write: length 10
00 C4 00 00 00 0A 00 00 00 09
SWTPM_IO_Read: length 10
00 C1 00 00 00 0A 00 00 00 F1
SWTPM_IO_Write: length 10
00 C4 00 00 00 0A 00 00 00 09
Ctrl Cmd: length 4
00 00 00 01
Ctrl Rsp: length 8
00 00 00 00 00 00 3F FF
Ctrl Cmd: length 8
00 00 00 02 00 00 00 00
TPM_MainInit: Initialize the TPM to host interface
TPM_MainInit: Initialize the TPM crypto support
TPM_Crypto_Init: FreeBL library
TPM_Crypto_Init: pHashConst f7 7d 67 a7
TPM_MainInit: Initialize the TPM NVRAM
TPM_MainInit: Run common limited self tests
TPM_MainInit: Initializing global TPM 0
TPM_Global_Init: TPMs 1
TPM_Global_Init: Initializing TPM_PERMANENT_FLAGS
TPM_Global_Init: Initializing TPM_STCLEAR_FLAGS
TPM_Global_Init: Initializing TPM_STANY_FLAGS
TPM_Global_Init: Initializing TPM_PERMANENT_DATA
TPM_Global_Init: Initializing TPM_STCLEAR_DATA
TPM_StclearData_Init: Initializing PCR's
TPM_Global_Init: Initializing TPM_STANY_DATA
TPM_Global_Init: Initializing TPM_KEY_HANDLE_LIST
TPM_Global_Init: Initializing TPM_NV_INDEX_ENTRIES
TPM_MainInit: Creating global TPM instance 0
TPM_MainInit: Run limited self tests on TPM 0
Ctrl Rsp: length 4
00 00 00 00
Ctrl Cmd: length 4
00 00 00 03
TPM_StclearData_Init: Initializing PCR's
Ctrl Rsp: length 4
00 00 00 00
It looks like coreboot never initialized the TPM but tried to send measurements to the TPM and fails. Do I need to change the devicetree?
Adding the TPM device to the devicetree doesn't work. Now it's clear that TPM relies on ACPI for autodetection, but coreboot only has an incomplete, almost-nonexistance implementation for QEMU, causing the Linux kernel to disable ACPI therefore TPM is not detected. SeaBIOS implemented most the these ACPI functionality on QEMU so it works.
Booting the kernel with tpm_tis.force=1
forces the kernel to probe the TPM device and tpm0
is found. :+1: However it seems the kernel fails to initialize the TPM, and it doesn't provide any function at all, and there's no /dev/tpm0
. :rage: At least we are making progress.
I'll continue to debug the problem on the next weekend.
So it turned out, that the tpm_tis interface simulated by QEMU was identified by Linux kernel, and the kernel even called tpm_chip_register
to register the chip, but as soon as the chip is registered, something happens and tpm_chip_unregister
is called to unload the chip, therefore nothing is detected in the last. I'm not a TPM or kernel developer so identifying the problem is beyond my capabilities. If you have experience with TPM, you may want to continue debugging.
Making TPM working with full coreboot + QEMU emulation seems to be a waste of time, meanwhile one can just run the HEADS image w/o coreboot...
qemu-system-x86_64 -machine q35 -display sdl \
-bios bios-256k.bin -boot menu=on \
-chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \
-tpmdev emulator,id=tpm0,chardev=chrtpm \
-device tpm-tis,tpmdev=tpm0 \
-kernel bzImage -initrd initrd.cpio.xz
Now the kernel successfully finds /dev/tpm0, but most TPM commands and tpm-reset script doesn't work, because the TPM was in the weird state. It looks like the source of the issue is some serious problems in swtpm.
It's probably the time to collect some data and report it to stefanberger/swtpm...
@zaolin?
@tlaurion No, I was building swtpm and QEMU by myself, manually, without this automation. But I think this script does the same thing, and it's unlikely to have a difference.
@biergaizi : that was coreboot < 4.8, right?
@tlaurion Without coreboot at all, because it doesn't work with coreboot. Instead, it partially works by just booting straight into the HEADS filesystem image with SeaBIOS, Linux kernel can detect TPM without problem, but it is still unusable - somehow, the TPM was being put in a strange state, as I reported. My conclusion was: only a swtpm developer has the knowledge to investigate it.
@biergaizi Were you successful in your investigation?
@Thrilleratplay, I think this is what you are looking for
@biergaizi important breakthrough in code on the safeboot side:
@tlaurion Perfect. I'll retest it on the new code base and close the bug report if the TPM works.
Since QEMU 2.11, TPM-related code has been merged and it's now possible to simulate a TPM hardware with swtpm and QEMU.
On SeaBIOS, the TPM is detected and usable under Linux-based system, with kernel module
tpm_tis
, shown as/dev/tpm0
.However, for mysterious reasons, the TPM is only detectable on SeaBIOS, but not HEADS. There's no TPM device under
/dev
, nor any TPM-related information in dmesg. We need to investigate the cause of the issue is an incomplete TPM implemenation in QEMU/swtpm or something else.Having a working TPM-simulation would ease the development significantly.