Commit 78b62d37 authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/stefanberger/tags/pull-tpm-2017-10-04-3' into staging



Merge tpm 2017/10/04 v3

# gpg: Signature made Fri 13 Oct 2017 12:37:07 BST
# gpg:                using RSA key 0x75AD65802A0B4211
# gpg: Good signature from "Stefan Berger <stefanb@linux.vnet.ibm.com>"
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: B818 B9CA DF90 89C2 D5CE  C66B 75AD 6580 2A0B 4211

* remotes/stefanberger/tags/pull-tpm-2017-10-04-3:
  specs: Describe the TPM support in QEMU
  tpm: Move tpm_cleanup() to right place
  tpm: Added support for TPM emulator
  tpm-passthrough: move reusable code to utils
  tpm-backend: Move realloc_buffer() implementation to tpm-tis model
  tpm-backend: Add new API to read backend TpmInfo
  tpm-backend: Made few interface methods optional
  tpm-backend: Initialize and free data members in it's own methods
  tpm-backend: Move thread handling inside TPMBackend
  tpm-backend: Remove unneeded member variable from backend class
  tpm: Use EMSGSIZE instead of EBADMSG to compile on OpenBSD

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents 40a1e8ac 8dc67017
Loading
Loading
Loading
Loading
+69 −46
Original line number Diff line number Diff line
@@ -18,27 +18,30 @@
#include "qapi/qmp/qerror.h"
#include "sysemu/tpm.h"
#include "qemu/thread.h"
#include "sysemu/tpm_backend_int.h"

enum TpmType tpm_backend_get_type(TPMBackend *s)
static void tpm_backend_worker_thread(gpointer data, gpointer user_data)
{
    TPMBackend *s = TPM_BACKEND(user_data);
    TPMBackendClass *k  = TPM_BACKEND_GET_CLASS(s);

    return k->ops->type;
    assert(k->handle_request != NULL);
    k->handle_request(s, (TPMBackendCmd)data);
}

const char *tpm_backend_get_desc(TPMBackend *s)
static void tpm_backend_thread_end(TPMBackend *s)
{
    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);

    return k->ops->desc();
    if (s->thread_pool) {
        g_thread_pool_push(s->thread_pool, (gpointer)TPM_BACKEND_CMD_END, NULL);
        g_thread_pool_free(s->thread_pool, FALSE, TRUE);
        s->thread_pool = NULL;
    }
}

void tpm_backend_destroy(TPMBackend *s)
enum TpmType tpm_backend_get_type(TPMBackend *s)
{
    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);

    k->ops->destroy(s);
    return k->ops->type;
}

int tpm_backend_init(TPMBackend *s, TPMState *state,
@@ -46,48 +49,62 @@ int tpm_backend_init(TPMBackend *s, TPMState *state,
{
    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);

    return k->ops->init(s, state, datacb);
    s->tpm_state = state;
    s->recv_data_callback = datacb;
    s->had_startup_error = false;

    return k->ops->init ? k->ops->init(s) : 0;
}

int tpm_backend_startup_tpm(TPMBackend *s)
{
    int res = 0;
    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);

    return k->ops->startup_tpm(s);
}
    /* terminate a running TPM */
    tpm_backend_thread_end(s);

bool tpm_backend_had_startup_error(TPMBackend *s)
{
    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
    s->thread_pool = g_thread_pool_new(tpm_backend_worker_thread, s, 1, TRUE,
                                       NULL);
    g_thread_pool_push(s->thread_pool, (gpointer)TPM_BACKEND_CMD_INIT, NULL);

    res = k->ops->startup_tpm ? k->ops->startup_tpm(s) : 0;

    return k->ops->had_startup_error(s);
    s->had_startup_error = (res != 0);

    return res;
}

size_t tpm_backend_realloc_buffer(TPMBackend *s, TPMSizedBuffer *sb)
bool tpm_backend_had_startup_error(TPMBackend *s)
{
    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);

    return k->ops->realloc_buffer(sb);
    return s->had_startup_error;
}

void tpm_backend_deliver_request(TPMBackend *s)
{
    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);

    k->ops->deliver_request(s);
    g_thread_pool_push(s->thread_pool, (gpointer)TPM_BACKEND_CMD_PROCESS_CMD,
                       NULL);
}

void tpm_backend_reset(TPMBackend *s)
{
    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);

    if (k->ops->reset) {
        k->ops->reset(s);
    }

    tpm_backend_thread_end(s);

    s->had_startup_error = false;
}

void tpm_backend_cancel_cmd(TPMBackend *s)
{
    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);

    assert(k->ops->cancel_cmd);

    k->ops->cancel_cmd(s);
}

@@ -95,23 +112,40 @@ bool tpm_backend_get_tpm_established_flag(TPMBackend *s)
{
    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);

    return k->ops->get_tpm_established_flag(s);
    return k->ops->get_tpm_established_flag ?
           k->ops->get_tpm_established_flag(s) : false;
}

int tpm_backend_reset_tpm_established_flag(TPMBackend *s, uint8_t locty)
{
    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);

    return k->ops->reset_tpm_established_flag(s, locty);
    return k->ops->reset_tpm_established_flag ?
           k->ops->reset_tpm_established_flag(s, locty) : 0;
}

TPMVersion tpm_backend_get_tpm_version(TPMBackend *s)
{
    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);

    assert(k->ops->get_tpm_version);

    return k->ops->get_tpm_version(s);
}

TPMInfo *tpm_backend_query_tpm(TPMBackend *s)
{
    TPMInfo *info = g_new0(TPMInfo, 1);
    TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);

    info->id = g_strdup(s->id);
    info->model = s->fe_model;
    info->options = k->ops->get_tpm_options ?
                    k->ops->get_tpm_options(s) : NULL;

    return info;
}

static bool tpm_backend_prop_get_opened(Object *obj, Error **errp)
{
    TPMBackend *s = TPM_BACKEND(obj);
@@ -152,33 +186,21 @@ static void tpm_backend_prop_set_opened(Object *obj, bool value, Error **errp)

static void tpm_backend_instance_init(Object *obj)
{
    TPMBackend *s = TPM_BACKEND(obj);

    object_property_add_bool(obj, "opened",
                             tpm_backend_prop_get_opened,
                             tpm_backend_prop_set_opened,
                             NULL);
    s->fe_model = -1;
}

void tpm_backend_thread_deliver_request(TPMBackendThread *tbt)
{
   g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_PROCESS_CMD, NULL);
}

void tpm_backend_thread_create(TPMBackendThread *tbt,
                               GFunc func, gpointer user_data)
static void tpm_backend_instance_finalize(Object *obj)
{
    if (!tbt->pool) {
        tbt->pool = g_thread_pool_new(func, user_data, 1, TRUE, NULL);
        g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_INIT, NULL);
    }
}
    TPMBackend *s = TPM_BACKEND(obj);

void tpm_backend_thread_end(TPMBackendThread *tbt)
{
    if (tbt->pool) {
        g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_END, NULL);
        g_thread_pool_free(tbt->pool, FALSE, TRUE);
        tbt->pool = NULL;
    }
    g_free(s->id);
    tpm_backend_thread_end(s);
}

static const TypeInfo tpm_backend_info = {
@@ -186,6 +208,7 @@ static const TypeInfo tpm_backend_info = {
    .parent = TYPE_OBJECT,
    .instance_size = sizeof(TPMBackend),
    .instance_init = tpm_backend_instance_init,
    .instance_finalize = tpm_backend_instance_finalize,
    .class_size = sizeof(TPMBackendClass),
    .abstract = true,
};
+12 −1
Original line number Diff line number Diff line
@@ -3495,6 +3495,12 @@ else
  tpm_passthrough=no
fi

# TPM emulator is for all posix systems
if test "$mingw32" != "yes"; then
  tpm_emulator=$tpm
else
  tpm_emulator=no
fi
##########################################
# attr probe

@@ -5412,6 +5418,7 @@ echo "gcov enabled $gcov"
echo "TPM support       $tpm"
echo "libssh2 support   $libssh2"
echo "TPM passthrough   $tpm_passthrough"
echo "TPM emulator      $tpm_emulator"
echo "QOM debugging     $qom_cast_debug"
echo "Live block migration $live_block_migration"
echo "lzo support       $lzo"
@@ -6011,12 +6018,16 @@ if test "$live_block_migration" = "yes" ; then
  echo "CONFIG_LIVE_BLOCK_MIGRATION=y" >> $config_host_mak
fi

# TPM passthrough support?
if test "$tpm" = "yes"; then
  echo 'CONFIG_TPM=$(CONFIG_SOFTMMU)' >> $config_host_mak
  # TPM passthrough support?
  if test "$tpm_passthrough" = "yes"; then
    echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak
  fi
  # TPM emulator support?
  if test "$tpm_emulator" = "yes"; then
    echo "CONFIG_TPM_EMULATOR=y" >> $config_host_mak
  fi
fi

echo "TRACE_BACKENDS=$trace_backends" >> $config_host_mak

docs/specs/tpm.txt

0 → 100644
+123 −0
Original line number Diff line number Diff line
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


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

qemu-system-x86_64 -display sdl -enable-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:

#> 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 ...
+5 −0
Original line number Diff line number Diff line
@@ -1000,6 +1000,7 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
    Error *err = NULL;
    unsigned int c = 0;
    TPMPassthroughOptions *tpo;
    TPMEmulatorOptions *teo;

    info_list = qmp_query_tpm(&err);
    if (err) {
@@ -1029,6 +1030,10 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict)
                           tpo->has_cancel_path ? ",cancel-path=" : "",
                           tpo->has_cancel_path ? tpo->cancel_path : "");
            break;
        case TPM_TYPE_OPTIONS_KIND_EMULATOR:
            teo = ti->options->u.emulator.data;
            monitor_printf(mon, ",chardev=%s", teo->chardev);
            break;
        case TPM_TYPE_OPTIONS_KIND__MAX:
            break;
        }
+1 −0
Original line number Diff line number Diff line
common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o
common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o tpm_util.o
common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o tpm_util.o
Loading