2017-10-04 20:12:09 +03:00
|
|
|
QEMU TPM Device
|
|
|
|
===============
|
|
|
|
|
|
|
|
= Guest-side Hardware Interface =
|
|
|
|
|
|
|
|
The QEMU TPM emulation implements a TPM TIS hardware interface following the
|
|
|
|
Trusted Computing Group's specification "TCG PC Client Specific TPM Interface
|
|
|
|
Specification (TIS)", Specification Version 1.3, 21 March 2013. This
|
|
|
|
specification, or a later version of it, can be accessed from the following
|
|
|
|
URL:
|
|
|
|
|
|
|
|
https://trustedcomputinggroup.org/pc-client-work-group-pc-client-specific-tpm-interface-specification-tis/
|
|
|
|
|
|
|
|
The TIS interface makes a memory mapped IO region in the area 0xfed40000 -
|
|
|
|
0xfed44fff available to the guest operating system.
|
|
|
|
|
|
|
|
|
|
|
|
QEMU files related to TPM TIS interface:
|
|
|
|
- hw/tpm/tpm_tis.c
|
|
|
|
- hw/tpm/tpm_tis.h
|
|
|
|
|
|
|
|
|
2018-10-31 00:33:53 +03:00
|
|
|
QEMU also implements a TPM CRB interface following the Trusted Computing
|
|
|
|
Group's specification "TCG PC Client Platform TPM Profile (PTP)
|
|
|
|
Specification", Family "2.0", Level 00 Revision 01.03 v22, May 22, 2017.
|
|
|
|
This specification, or a later version of it, can be accessed from the
|
|
|
|
following URL:
|
|
|
|
|
|
|
|
https://trustedcomputinggroup.org/resource/pc-client-platform-tpm-profile-ptp-specification/
|
|
|
|
|
|
|
|
The CRB interface makes a memory mapped IO region in the area 0xfed40000 -
|
|
|
|
0xfed40fff (1 locality) available to the guest operating system.
|
|
|
|
|
|
|
|
QEMU files related to TPM CRB interface:
|
|
|
|
- hw/tpm/tpm_crb.c
|
|
|
|
|
|
|
|
|
2017-10-04 20:12:09 +03:00
|
|
|
= ACPI Interface =
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
To support measurement logs to be written by the firmware, e.g. SeaBIOS, a TCPA
|
|
|
|
table is implemented. This table provides a 64kb buffer where the firmware can
|
|
|
|
write its log into. For TPM 2 only a more recent version of the TPM2 table
|
|
|
|
provides support for measurements logs and a TCPA table does not need to be
|
|
|
|
created.
|
|
|
|
|
|
|
|
The TCPA and TPM2 ACPI tables follow the Trusted Computing Group specification
|
|
|
|
"TCG ACPI Specification" Family "1.2" and "2.0", Level 00 Revision 00.37. This
|
|
|
|
specification, or a later version of it, can be accessed from the following
|
|
|
|
URL:
|
|
|
|
|
|
|
|
https://trustedcomputinggroup.org/tcg-acpi-specification/
|
|
|
|
|
|
|
|
|
|
|
|
QEMU files related to TPM ACPI tables:
|
|
|
|
- hw/i386/acpi-build.c
|
|
|
|
- include/hw/acpi/tpm.h
|
|
|
|
|
|
|
|
|
|
|
|
= TPM backend devices =
|
|
|
|
|
|
|
|
The TPM implementation is split into two parts, frontend and backend. The
|
|
|
|
frontend part is the hardware interface, such as the TPM TIS interface
|
|
|
|
described earlier, and the other part is the TPM backend interface. The backend
|
|
|
|
interfaces implement the interaction with a TPM device, which may be a physical
|
|
|
|
or an emulated device. The split between the front- and backend devices allows
|
|
|
|
a frontend to be connected with any available backend. This enables the TIS
|
|
|
|
interface to be used with the passthrough backend or the (future) swtpm backend.
|
|
|
|
|
|
|
|
|
|
|
|
QEMU files related to TPM backends:
|
|
|
|
- backends/tpm.c
|
|
|
|
- include/sysemu/tpm_backend.h
|
|
|
|
- include/sysemu/tpm_backend_int.h
|
|
|
|
|
|
|
|
|
|
|
|
== The QEMU TPM passthrough device ==
|
|
|
|
|
|
|
|
In case QEMU is run on Linux as the host operating system it is possible to
|
|
|
|
make the hardware TPM device available to a single QEMU guest. In this case the
|
|
|
|
user must make sure that no other program is using the device, e.g., /dev/tpm0,
|
|
|
|
before trying to start QEMU with it.
|
|
|
|
|
|
|
|
The passthrough driver uses the host's TPM device for sending TPM commands
|
|
|
|
and receiving responses from. Besides that it accesses the TPM device's sysfs
|
|
|
|
entry for support of command cancellation. Since none of the state of a
|
|
|
|
hardware TPM can be migrated between hosts, virtual machine migration is
|
|
|
|
disabled when the TPM passthrough driver is used.
|
|
|
|
|
|
|
|
Since the host's TPM device will already be initialized by the host's firmware,
|
|
|
|
certain commands, e.g. TPM_Startup(), sent by the virtual firmware for device
|
|
|
|
initialization, will fail. In this case the firmware should not use the TPM.
|
|
|
|
|
|
|
|
Sharing the device with the host is generally not a recommended usage scenario
|
|
|
|
for a TPM device. The primary reason for this is that two operating systems can
|
|
|
|
then access the device's single set of resources, such as platform configuration
|
|
|
|
registers (PCRs). Applications or kernel security subsystems, such as the
|
|
|
|
Linux Integrity Measurement Architecture (IMA), are not expecting to share PCRs.
|
|
|
|
|
|
|
|
|
|
|
|
QEMU files related to the TPM passthrough device:
|
|
|
|
- hw/tpm/tpm_passthrough.c
|
|
|
|
- hw/tpm/tpm_util.c
|
|
|
|
- hw/tpm/tpm_util.h
|
|
|
|
|
|
|
|
|
|
|
|
Command line to start QEMU with the TPM passthrough device using the host's
|
|
|
|
hardware TPM /dev/tpm0:
|
|
|
|
|
2018-06-13 08:05:19 +03:00
|
|
|
qemu-system-x86_64 -display sdl -accel kvm \
|
2017-10-04 20:12:09 +03:00
|
|
|
-m 1024 -boot d -bios bios-256k.bin -boot menu=on \
|
|
|
|
-tpmdev passthrough,id=tpm0,path=/dev/tpm0 \
|
|
|
|
-device tpm-tis,tpmdev=tpm0 test.img
|
|
|
|
|
|
|
|
The following commands should result in similar output inside the VM with a
|
|
|
|
Linux kernel that either has the TPM TIS driver built-in or available as a
|
|
|
|
module:
|
|
|
|
|
|
|
|
#> dmesg | grep -i tpm
|
|
|
|
[ 0.711310] tpm_tis 00:06: 1.2 TPM (device=id 0x1, rev-id 1)
|
|
|
|
|
|
|
|
#> dmesg | grep TCPA
|
|
|
|
[ 0.000000] ACPI: TCPA 0x0000000003FFD191C 000032 (v02 BOCHS \
|
|
|
|
BXPCTCPA 0000001 BXPC 00000001)
|
|
|
|
|
|
|
|
#> ls -l /dev/tpm*
|
|
|
|
crw-------. 1 root root 10, 224 Jul 11 10:11 /dev/tpm0
|
|
|
|
|
|
|
|
#> find /sys/devices/ | grep pcrs$ | xargs cat
|
|
|
|
PCR-00: 35 4E 3B CE 23 9F 38 59 ...
|
|
|
|
...
|
|
|
|
PCR-23: 00 00 00 00 00 00 00 00 ...
|
2017-10-05 19:47:27 +03:00
|
|
|
|
|
|
|
|
|
|
|
== The QEMU TPM emulator device ==
|
|
|
|
|
|
|
|
The TPM emulator device uses an external TPM emulator called 'swtpm' for
|
|
|
|
sending TPM commands to and receiving responses from. The swtpm program
|
|
|
|
must have been started before trying to access it through the TPM emulator
|
|
|
|
with QEMU.
|
|
|
|
|
|
|
|
The TPM emulator implements a command channel for transferring TPM commands
|
|
|
|
and responses as well as a control channel over which control commands can
|
|
|
|
be sent. The specification for the control channel can be found here:
|
|
|
|
|
|
|
|
https://github.com/stefanberger/swtpm/blob/master/man/man3/swtpm_ioctls.pod
|
|
|
|
|
|
|
|
|
|
|
|
The control channel serves the purpose of resetting, initializing, and
|
|
|
|
migrating the TPM state, among other things.
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
|
|
|
|
QEMU files related to the TPM emulator device:
|
|
|
|
- hw/tpm/tpm_emulator.c
|
|
|
|
- hw/tpm/tpm_util.c
|
|
|
|
- hw/tpm/tpm_util.h
|
|
|
|
|
|
|
|
|
|
|
|
The following commands start the swtpm with a UnixIO control channel over
|
|
|
|
a socket interface. They do not need to be run as root.
|
|
|
|
|
|
|
|
mkdir /tmp/mytpm1
|
|
|
|
swtpm socket --tpmstate dir=/tmp/mytpm1 \
|
|
|
|
--ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \
|
|
|
|
--log level=20
|
|
|
|
|
|
|
|
Command line to start QEMU with the TPM emulator device communicating with
|
|
|
|
the swtpm:
|
|
|
|
|
2018-06-13 08:05:19 +03:00
|
|
|
qemu-system-x86_64 -display sdl -accel kvm \
|
2017-10-05 19:47:27 +03:00
|
|
|
-m 1024 -boot d -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 test.img
|
|
|
|
|
|
|
|
|
|
|
|
In case SeaBIOS is used as firmware, it should show the TPM menu item
|
|
|
|
after entering the menu with 'ESC'.
|
|
|
|
|
|
|
|
Select boot device:
|
|
|
|
1. DVD/CD [ata1-0: QEMU DVD-ROM ATAPI-4 DVD/CD]
|
|
|
|
[...]
|
|
|
|
5. Legacy option rom
|
|
|
|
|
|
|
|
t. TPM Configuration
|
|
|
|
|
|
|
|
|
|
|
|
The following commands should result in similar output inside the VM with a
|
|
|
|
Linux kernel that either has the TPM TIS driver built-in or available as a
|
|
|
|
module:
|
|
|
|
|
|
|
|
#> dmesg | grep -i tpm
|
|
|
|
[ 0.711310] tpm_tis 00:06: 1.2 TPM (device=id 0x1, rev-id 1)
|
|
|
|
|
|
|
|
#> dmesg | grep TCPA
|
|
|
|
[ 0.000000] ACPI: TCPA 0x0000000003FFD191C 000032 (v02 BOCHS \
|
|
|
|
BXPCTCPA 0000001 BXPC 00000001)
|
|
|
|
|
|
|
|
#> ls -l /dev/tpm*
|
|
|
|
crw-------. 1 root root 10, 224 Jul 11 10:11 /dev/tpm0
|
|
|
|
|
|
|
|
#> find /sys/devices/ | grep pcrs$ | xargs cat
|
|
|
|
PCR-00: 35 4E 3B CE 23 9F 38 59 ...
|
|
|
|
...
|
|
|
|
PCR-23: 00 00 00 00 00 00 00 00 ...
|
2018-03-06 01:10:01 +03:00
|
|
|
|
|
|
|
|
|
|
|
=== Migration with the TPM emulator ===
|
|
|
|
|
|
|
|
The TPM emulator supports the following types of virtual machine migration:
|
|
|
|
|
|
|
|
- VM save / restore (migration into a file)
|
|
|
|
- Network migration
|
|
|
|
- Snapshotting (migration into storage like QoW2 or QED)
|
|
|
|
|
|
|
|
The following command sequences can be used to test VM save / restore.
|
|
|
|
|
|
|
|
|
|
|
|
In a 1st terminal start an instance of a swtpm using the following command:
|
|
|
|
|
|
|
|
mkdir /tmp/mytpm1
|
|
|
|
swtpm socket --tpmstate dir=/tmp/mytpm1 \
|
|
|
|
--ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \
|
|
|
|
--log level=20 --tpm2
|
|
|
|
|
|
|
|
In a 2nd terminal start the VM:
|
|
|
|
|
2018-06-13 08:05:19 +03:00
|
|
|
qemu-system-x86_64 -display sdl -accel kvm \
|
2018-03-06 01:10:01 +03:00
|
|
|
-m 1024 -boot d -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 \
|
|
|
|
-monitor stdio \
|
|
|
|
test.img
|
|
|
|
|
|
|
|
Verify that the attached TPM is working as expected using applications inside
|
|
|
|
the VM.
|
|
|
|
|
|
|
|
To store the state of the VM use the following command in the QEMU monitor in
|
|
|
|
the 2nd terminal:
|
|
|
|
|
|
|
|
(qemu) migrate "exec:cat > testvm.bin"
|
|
|
|
(qemu) quit
|
|
|
|
|
|
|
|
At this point a file called 'testvm.bin' should exists and the swtpm and QEMU
|
|
|
|
processes should have ended.
|
|
|
|
|
|
|
|
To test 'VM restore' you have to start the swtpm with the same parameters
|
|
|
|
as before. If previously a TPM 2 [--tpm2] was saved, --tpm2 must now be
|
|
|
|
passed again on the command line.
|
|
|
|
|
|
|
|
In the 1st terminal restart the swtpm with the same command line as before:
|
|
|
|
|
|
|
|
swtpm socket --tpmstate dir=/tmp/mytpm1 \
|
|
|
|
--ctrl type=unixio,path=/tmp/mytpm1/swtpm-sock \
|
|
|
|
--log level=20 --tpm2
|
|
|
|
|
2018-06-12 09:51:50 +03:00
|
|
|
In the 2nd terminal restore the state of the VM using the additional
|
2018-03-06 01:10:01 +03:00
|
|
|
'-incoming' option.
|
|
|
|
|
2018-06-13 08:05:19 +03:00
|
|
|
qemu-system-x86_64 -display sdl -accel kvm \
|
2018-03-06 01:10:01 +03:00
|
|
|
-m 1024 -boot d -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 \
|
|
|
|
-incoming "exec:cat < testvm.bin" \
|
|
|
|
test.img
|
|
|
|
|
|
|
|
|
|
|
|
Troubleshooting migration:
|
|
|
|
|
|
|
|
There are several reasons why migration may fail. In case of problems,
|
|
|
|
please ensure that the command lines adhere to the following rules and,
|
|
|
|
if possible, that identical versions of QEMU and swtpm are used at all
|
|
|
|
times.
|
|
|
|
|
|
|
|
VM save and restore:
|
|
|
|
- QEMU command line parameters should be identical apart from the
|
|
|
|
'-incoming' option on VM restore
|
|
|
|
- swtpm command line parameters should be identical
|
|
|
|
|
|
|
|
VM migration to 'localhost':
|
|
|
|
- QEMU command line parameters should be identical apart from the
|
|
|
|
'-incoming' option on the destination side
|
|
|
|
- swtpm command line parameters should point to two different
|
|
|
|
directories on the source and destination swtpm (--tpmstate dir=...)
|
|
|
|
(especially if different versions of libtpms were to be used on the
|
|
|
|
same machine).
|
|
|
|
|
|
|
|
VM migration across the network:
|
|
|
|
- QEMU command line parameters should be identical apart from the
|
|
|
|
'-incoming' option on the destination side
|
|
|
|
- swtpm command line parameters should be identical
|
|
|
|
|
|
|
|
VM Snapshotting:
|
|
|
|
- QEMU command line parameters should be identical
|
|
|
|
- swtpm command line parameters should be identical
|
|
|
|
|
|
|
|
|
|
|
|
Besides that, migration failure reasons on the swtpm level may include
|
|
|
|
the following:
|
|
|
|
|
|
|
|
- the versions of the swtpm on the source and destination sides are
|
|
|
|
incompatible
|
|
|
|
- downgrading of TPM state may not be supported
|
|
|
|
- the source and destination libtpms were compiled with different
|
|
|
|
compile-time options and the destination side refuses to accept the
|
|
|
|
state
|
|
|
|
- different migration keys are used on the source and destination side
|
|
|
|
and the destination side cannot decrypt the migrated state
|
|
|
|
(swtpm ... --migration-key ... )
|