linuxboot / heads

A minimal Linux that runs as a coreboot or LinuxBoot ROM payload to provide a secure, flexible boot environment for laptops, workstations and servers.
https://osresearch.net/
GNU General Public License v2.0
1.42k stars 188 forks source link

Various Problems of TPM Simulation on QEMU #354

Closed biergaizi closed 2 years ago

biergaizi commented 6 years ago

Since QEMU 2.11, TPM-related code has been merged and it's now possible to simulate a TPM hardware with swtpm and QEMU.

swtpm socket --tpmstate dir=/tmp/mytpm1 \
             --ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock  \
             --log level=20 &

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 \
                    alpine-standard-3.7.0-x86_64.iso

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.

biergaizi commented 6 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?

biergaizi commented 6 years ago

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?

biergaizi commented 6 years ago

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.

biergaizi commented 6 years ago

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_unregisteris 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.

biergaizi commented 6 years ago

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.

biergaizi commented 6 years ago

It's probably the time to collect some data and report it to stefanberger/swtpm...

tlaurion commented 5 years ago

@zaolin?

tlaurion commented 5 years ago

@biergaizi : have you came across this?

biergaizi commented 5 years ago

@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.

tlaurion commented 5 years ago

@biergaizi : that was coreboot < 4.8, right?

biergaizi commented 5 years ago

@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.

tlaurion commented 4 years ago

516 and #701 related.

tlaurion commented 4 years ago

@biergaizi Were you successful in your investigation?

tlaurion commented 4 years ago

@Thrilleratplay, I think this is what you are looking for

tlaurion commented 4 years ago

@biergaizi important breakthrough in code on the safeboot side:

swtpm support in safeboot relative commit

biergaizi commented 3 years ago

@tlaurion Perfect. I'll retest it on the new code base and close the bug report if the TPM works.