aboutsummaryrefslogtreecommitdiff
path: root/docs/specs/tpm.rst
blob: 535912a92b81a2ba8c39ae6489b98033ecb1f01d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
===============
QEMU TPM Device
===============

Guest-side hardware interface
=============================

TIS 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. (see the `TIS specification`_, or a later version
of it).

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_common.c``
 - ``hw/tpm/tpm_tis_isa.c``
 - ``hw/tpm/tpm_tis_sysbus.c``
 - ``hw/tpm/tpm_tis.h``

Both an ISA device and a sysbus device are available. The former is
used with pc/q35 machine while the latter can be instantiated in the
Arm virt machine.

CRB interface
-------------

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. (see the `CRB specification`_, or a later version of it)

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``

SPAPR interface
---------------

pSeries (ppc64) machines offer a tpm-spapr device model.

QEMU files related to the SPAPR interface:
 - ``hw/tpm/tpm_spapr.c``

fw_cfg interface
================

The bios/firmware may read the ``"etc/tpm/config"`` fw_cfg entry for
configuring the guest appropriately.

The entry of 6 bytes has the following content, in little-endian:

.. code-block:: c

    #define TPM_VERSION_UNSPEC          0
    #define TPM_VERSION_1_2             1
    #define TPM_VERSION_2_0             2

    #define TPM_PPI_VERSION_NONE        0
    #define TPM_PPI_VERSION_1_30        1

    struct FwCfgTPMConfig {
        uint32_t tpmppi_address;         /* PPI memory location */
        uint8_t tpm_version;             /* TPM version */
        uint8_t tpmppi_version;          /* PPI version */
    };

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. (see the `ACPI specification`_, or a later version
of it)

ACPI PPI Interface
------------------

QEMU supports the Physical Presence Interface (PPI) for TPM 1.2 and
TPM 2. This interface requires ACPI and firmware support. (see the
`PPI specification`_)

PPI enables a system administrator (root) to request a modification to
the TPM upon reboot. The PPI specification defines the operation
requests and the actions the firmware has to take. The system
administrator passes the operation request number to the firmware
through an ACPI interface which writes this number to a memory
location that the firmware knows. Upon reboot, the firmware finds the
number and sends commands to the TPM. The firmware writes the TPM
result code and the operation request number to a memory location that
ACPI can read from and pass the result on to the administrator.

The PPI specification defines a set of mandatory and optional
operations for the firmware to implement. The ACPI interface also
allows an administrator to list the supported operations. In QEMU the
ACPI code is generated by QEMU, yet the firmware needs to implement
support on a per-operations basis, and different firmwares may support
a different subset. Therefore, QEMU introduces the virtual memory
device for PPI where the firmware can indicate which operations it
supports and ACPI can enable the ones that are supported and disable
all others. This interface lies in main memory and has the following
layout:

 +-------------+--------+--------+-------------------------------------------+
 |  Field      | Length | Offset | Description                               |
 +=============+========+========+===========================================+
 | ``func``    |  0x100 |  0x000 | Firmware sets values for each supported   |
 |             |        |        | operation. See defined values below.      |
 +-------------+--------+--------+-------------------------------------------+
 | ``ppin``    |   0x1  |  0x100 | SMI interrupt to use. Set by firmware.    |
 |             |        |        | Not supported.                            |
 +-------------+--------+--------+-------------------------------------------+
 | ``ppip``    |   0x4  |  0x101 | ACPI function index to pass to SMM code.  |
 |             |        |        | Set by ACPI. Not supported.               |
 +-------------+--------+--------+-------------------------------------------+
 | ``pprp``    |   0x4  |  0x105 | Result of last executed operation. Set by |
 |             |        |        | firmware. See function index 5 for values.|
 +-------------+--------+--------+-------------------------------------------+
 | ``pprq``    |   0x4  |  0x109 | Operation request number to execute. See  |
 |             |        |        | 'Physical Presence Interface Operation    |
 |             |        |        | Summary' tables in specs. Set by ACPI.    |
 +-------------+--------+--------+-------------------------------------------+
 | ``pprm``    |   0x4  |  0x10d | Operation request optional parameter.     |
 |             |        |        | Values depend on operation. Set by ACPI.  |
 +-------------+--------+--------+-------------------------------------------+
 | ``lppr``    |   0x4  |  0x111 | Last executed operation request number.   |
 |             |        |        | Copied from pprq field by firmware.       |
 +-------------+--------+--------+-------------------------------------------+
 | ``fret``    |   0x4  |  0x115 | Result code from SMM function.            |
 |             |        |        | Not supported.                            |
 +-------------+--------+--------+-------------------------------------------+
 | ``res1``    |  0x40  |  0x119 | Reserved for future use                   |
 +-------------+--------+--------+-------------------------------------------+
 |``next_step``|   0x1  |  0x159 | Operation to execute after reboot by      |
 |             |        |        | firmware. Used by firmware.               |
 +-------------+--------+--------+-------------------------------------------+
 | ``movv``    |   0x1  |  0x15a | Memory overwrite variable                 |
 +-------------+--------+--------+-------------------------------------------+

The following values are supported for the ``func`` field. They
correspond to the values used by ACPI function index 8.

 +----------+-------------------------------------------------------------+
 | Value    | Description                                                 |
 +==========+=============================================================+
 | 0        | Operation is not implemented.                               |
 +----------+-------------------------------------------------------------+
 | 1        | Operation is only accessible through firmware.              |
 +----------+-------------------------------------------------------------+
 | 2        | Operation is blocked for OS by firmware configuration.      |
 +----------+-------------------------------------------------------------+
 | 3        | Operation is allowed and physically present user required.  |
 +----------+-------------------------------------------------------------+
 | 4        | Operation is allowed and physically present user is not     |
 |          | required.                                                   |
 +----------+-------------------------------------------------------------+

The location of the table is given by the fw_cfg ``tpmppi_address``
field.  The PPI memory region size is 0x400 (``TPM_PPI_ADDR_SIZE``) to
leave enough room for future updates.

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 swtpm backend.

QEMU files related to TPM backends:
 - ``backends/tpm.c``
 - ``include/sysemu/tpm.h``
 - ``include/sysemu/tpm_backend.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:
 - ``backends/tpm/tpm_passthrough.c``
 - ``backends/tpm/tpm_util.c``
 - ``include/sysemu/tpm_util.h``


Command line to start QEMU with the TPM passthrough device using the host's
hardware TPM ``/dev/tpm0``:

.. code-block:: console

  qemu-system-x86_64 -display sdl -accel kvm \
  -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 (assuming a TPM 2 is passed through):

.. code-block:: console

  # dmesg | grep -i tpm
  [    0.012560] ACPI: TPM2 0x000000000BFFD1900 00004C (v04 BOCHS  \
      BXPC     0000001 BXPC 00000001)

  # ls -l /dev/tpm*
  crw-rw----. 1 tss root  10,   224 Sep  6 12:36 /dev/tpm0
  crw-rw----. 1 tss rss  253, 65536 Sep  6 12:36 /dev/tpmrm0

  Starting with Linux 5.12 there are PCR entries for TPM 2 in sysfs:
  # find /sys/devices/ -type f | grep pcr-sha
  ...
  /sys/devices/LNXSYSTEM:00/LNXSYBUS:00/MSFT0101:00/tpm/tpm0/pcr-sha256/1
  ...
  /sys/devices/LNXSYSTEM:00/LNXSYBUS:00/MSFT0101:00/tpm/tpm0/pcr-sha256/9
  ...

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. (see the `SWTPM protocol`_ specification)

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:
 - ``backends/tpm/tpm_emulator.c``
 - ``backends/tpm/tpm_util.c``
 - ``include/sysemu/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.

.. code-block:: console

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

Command line to start QEMU with the TPM emulator device communicating
with the swtpm (x86):

.. code-block:: console

  qemu-system-x86_64 -display sdl -accel kvm \
    -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 a pSeries machine is emulated, use the following command line:

.. code-block:: console

  qemu-system-ppc64 -display sdl -machine pseries,accel=kvm \
    -m 1024 -bios slof.bin -boot menu=on \
    -nodefaults -device VGA -device pci-ohci -device usb-kbd \
    -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \
    -tpmdev emulator,id=tpm0,chardev=chrtpm \
    -device tpm-spapr,tpmdev=tpm0 \
    -device spapr-vscsi,id=scsi0,reg=0x00002000 \
    -device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x3,drive=drive-virtio-disk0,id=virtio-disk0 \
    -drive file=test.img,format=raw,if=none,id=drive-virtio-disk0

In case an Arm virt machine is emulated, use the following command line:

.. code-block:: console

  qemu-system-aarch64 -machine virt,gic-version=3,accel=kvm \
    -cpu host -m 4G \
    -nographic -no-acpi \
    -chardev socket,id=chrtpm,path=/tmp/mytpm1/swtpm-sock \
    -tpmdev emulator,id=tpm0,chardev=chrtpm \
    -device tpm-tis-device,tpmdev=tpm0 \
    -device virtio-blk-pci,drive=drv0 \
    -drive format=qcow2,file=hda.qcow2,if=none,id=drv0 \
    -drive if=pflash,format=raw,file=flash0.img,readonly=on \
    -drive if=pflash,format=raw,file=flash1.img

In case SeaBIOS is used as firmware, it should show the TPM menu item
after entering the menu with 'ESC'.

.. code-block:: console

  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:

.. code-block:: console

  # dmesg | grep -i tpm
  [    0.012560] ACPI: TPM2 0x000000000BFFD1900 00004C (v04 BOCHS  \
      BXPC     0000001 BXPC 00000001)

  # ls -l /dev/tpm*
  crw-rw----. 1 tss root  10,   224 Sep  6 12:36 /dev/tpm0
  crw-rw----. 1 tss rss  253, 65536 Sep  6 12:36 /dev/tpmrm0

  Starting with Linux 5.12 there are PCR entries for TPM 2 in sysfs:
  # find /sys/devices/ -type f | grep pcr-sha
  ...
  /sys/devices/LNXSYSTEM:00/LNXSYBUS:00/MSFT0101:00/tpm/tpm0/pcr-sha256/1
  ...
  /sys/devices/LNXSYSTEM:00/LNXSYBUS:00/MSFT0101:00/tpm/tpm0/pcr-sha256/9
  ...

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:

.. code-block:: console

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

In a 2nd terminal start the VM:

.. code-block:: console

  qemu-system-x86_64 -display sdl -accel kvm \
    -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:

.. code-block:: console

  (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:

.. code-block:: console

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

In the 2nd terminal restore the state of the VM using the additional
'-incoming' option.

.. code-block:: console

  qemu-system-x86_64 -display sdl -accel kvm \
    -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 ... )


.. _TIS specification:
   https://trustedcomputinggroup.org/pc-client-work-group-pc-client-specific-tpm-interface-specification-tis/

.. _CRB specification:
   https://trustedcomputinggroup.org/resource/pc-client-platform-tpm-profile-ptp-specification/


.. _ACPI specification:
   https://trustedcomputinggroup.org/tcg-acpi-specification/

.. _PPI specification:
   https://trustedcomputinggroup.org/resource/tcg-physical-presence-interface-specification/

.. _SWTPM protocol:
   https://github.com/stefanberger/swtpm/blob/master/man/man3/swtpm_ioctls.pod