diff --git a/docs/specs/tpm.txt b/docs/specs/tpm.txt index 914daaceeafa4ad4010f8837364d283c6a67d20b..d1d71571e982ddd921766270a5fe3501967e7e23 100644 --- a/docs/specs/tpm.txt +++ b/docs/specs/tpm.txt @@ -121,3 +121,82 @@ crw-------. 1 root root 10, 224 Jul 11 10:11 /dev/tpm0 PCR-00: 35 4E 3B CE 23 9F 38 59 ... ... PCR-23: 00 00 00 00 00 00 00 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: + +qemu-system-x86_64 -display sdl -enable-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 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 ... diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c index 9aaec8e3efb79cea5c89b6c1c76ca1bda373fff0..e1a68104d6ff5e178c9545fe6dda4fe006d3608a 100644 --- a/hw/tpm/tpm_emulator.c +++ b/hw/tpm/tpm_emulator.c @@ -71,15 +71,21 @@ typedef struct TPMEmulator { ptm_cap caps; /* capabilities of the TPM */ uint8_t cur_locty_number; /* last set locality */ Error *migration_blocker; + + QemuMutex mutex; } TPMEmulator; -static int tpm_emulator_ctrlcmd(CharBackend *dev, unsigned long cmd, void *msg, +static int tpm_emulator_ctrlcmd(TPMEmulator *tpm, unsigned long cmd, void *msg, size_t msg_len_in, size_t msg_len_out) { + CharBackend *dev = &tpm->ctrl_chr; uint32_t cmd_no = cpu_to_be32(cmd); ssize_t n = sizeof(uint32_t) + msg_len_in; uint8_t *buf = NULL; + int ret = -1; + + qemu_mutex_lock(&tpm->mutex); buf = g_alloca(n); memcpy(buf, &cmd_no, sizeof(cmd_no)); @@ -87,17 +93,21 @@ static int tpm_emulator_ctrlcmd(CharBackend *dev, unsigned long cmd, void *msg, n = qemu_chr_fe_write_all(dev, buf, n); if (n <= 0) { - return -1; + goto end; } if (msg_len_out != 0) { n = qemu_chr_fe_read_all(dev, msg, msg_len_out); if (n <= 0) { - return -1; + goto end; } } - return 0; + ret = 0; + +end: + qemu_mutex_unlock(&tpm->mutex); + return ret; } static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu, @@ -154,7 +164,7 @@ static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number, DPRINTF("setting locality : 0x%x", locty_number); loc.u.req.loc = locty_number; - if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SET_LOCALITY, &loc, + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_LOCALITY, &loc, sizeof(loc), sizeof(loc)) < 0) { error_setg(errp, "tpm-emulator: could not set locality : %s", strerror(errno)); @@ -202,8 +212,8 @@ error: static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu) { DPRINTF("%s", __func__); - if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_GET_CAPABILITY, - &tpm_emu->caps, 0, sizeof(tpm_emu->caps)) < 0) { + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_CAPABILITY, + &tpm_emu->caps, 0, sizeof(tpm_emu->caps)) < 0) { error_report("tpm-emulator: probing failed : %s", strerror(errno)); return -1; } @@ -254,8 +264,8 @@ static int tpm_emulator_startup_tpm(TPMBackend *tb) ptm_res res; DPRINTF("%s", __func__); - if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_INIT, &init, sizeof(init), - sizeof(init)) < 0) { + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init), + sizeof(init)) < 0) { error_report("tpm-emulator: could not send INIT: %s", strerror(errno)); goto err_exit; @@ -278,7 +288,7 @@ static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb) ptm_est est; DPRINTF("%s", __func__); - if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_GET_TPMESTABLISHED, &est, + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_TPMESTABLISHED, &est, 0, sizeof(est)) < 0) { error_report("tpm-emulator: Could not get the TPM established flag: %s", strerror(errno)); @@ -302,7 +312,7 @@ static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb, } reset_est.u.req.loc = tpm_emu->cur_locty_number; - if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_RESET_TPMESTABLISHED, + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_RESET_TPMESTABLISHED, &reset_est, sizeof(reset_est), sizeof(reset_est)) < 0) { error_report("tpm-emulator: Could not reset the establishment bit: %s", @@ -330,7 +340,7 @@ static void tpm_emulator_cancel_cmd(TPMBackend *tb) return; } - if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_CANCEL_TPM_CMD, &res, 0, + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_CANCEL_TPM_CMD, &res, 0, sizeof(res)) < 0) { error_report("tpm-emulator: Could not cancel command: %s", strerror(errno)); @@ -378,8 +388,8 @@ static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu) qemu_chr_fe_set_msgfds(&tpm_emu->ctrl_chr, fds + 1, 1); - if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SET_DATAFD, &res, 0, - sizeof(res)) || res != 0) { + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_DATAFD, &res, 0, + sizeof(res)) < 0 || res != 0) { error_report("tpm-emulator: Failed to send CMD_SET_DATAFD: %s", strerror(errno)); goto err_exit; @@ -501,6 +511,7 @@ static void tpm_emulator_inst_init(Object *obj) DPRINTF("%s", __func__); tpm_emu->options = g_new0(TPMEmulatorOptions, 1); tpm_emu->cur_locty_number = ~0; + qemu_mutex_init(&tpm_emu->mutex); } /* @@ -510,8 +521,7 @@ static void tpm_emulator_shutdown(TPMEmulator *tpm_emu) { ptm_res res; - if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SHUTDOWN, &res, 0, - sizeof(res)) < 0) { + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SHUTDOWN, &res, 0, sizeof(res)) < 0) { error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s", strerror(errno)); } else if (res != 0) { @@ -536,6 +546,8 @@ static void tpm_emulator_inst_finalize(Object *obj) migrate_del_blocker(tpm_emu->migration_blocker); error_free(tpm_emu->migration_blocker); } + + qemu_mutex_destroy(&tpm_emu->mutex); } static void tpm_emulator_class_init(ObjectClass *klass, void *data) diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c index 7402528b256ce69d6125fa8082f985b351c7a734..42d647d363a9eedffa4efd061008bb9e0e01c134 100644 --- a/hw/tpm/tpm_tis.c +++ b/hw/tpm/tpm_tis.c @@ -545,7 +545,7 @@ static uint64_t tpm_tis_mmio_read(void *opaque, hwaddr addr, uint8_t v; if (tpm_backend_had_startup_error(s->be_driver)) { - return val; + return 0; } switch (offset) { @@ -1008,6 +1008,10 @@ TPMVersion tpm_tis_get_tpm_version(Object *obj) { TPMState *s = TPM(obj); + if (tpm_backend_had_startup_error(s->be_driver)) { + return TPM_VERSION_UNSPEC; + } + return tpm_backend_get_tpm_version(s->be_driver); }