[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [qemu-xen master] Merge remote-tracking branch 'remotes/maxreitz/tags/pull-block-2020-06-24' into staging
=== This changeset includes merge from high-traffic branch === Commits on that branch are not reported individually. commit 4abf70a661a5df3886ac9d7c19c3617fa92b922a Merge: 6651620b92bc08cde07cb500e9a43dba7bd9b2b7 24b861c0386a17ea31eb824310c21118fb7be883 Author: Peter Maydell <peter.maydell@xxxxxxxxxx> AuthorDate: Fri Jul 3 15:34:44 2020 +0100 Commit: Peter Maydell <peter.maydell@xxxxxxxxxx> CommitDate: Fri Jul 3 15:34:45 2020 +0100 Merge remote-tracking branch 'remotes/maxreitz/tags/pull-block-2020-06-24' into staging Block patches: - Two iotest fixes # gpg: Signature made Wed 24 Jun 2020 09:00:51 BST # gpg: using RSA key 91BEB60A30DB3E8857D11829F407DB0061D5CF40 # gpg: issuer "mreitz@xxxxxxxxxx" # gpg: Good signature from "Max Reitz <mreitz@xxxxxxxxxx>" [full] # Primary key fingerprint: 91BE B60A 30DB 3E88 57D1 1829 F407 DB00 61D5 CF40 * remotes/maxreitz/tags/pull-block-2020-06-24: iotests: don't test qcow2.py inside 291 iotests: Fix 051 output after qdev_init_nofail() removal Signed-off-by: Peter Maydell <peter.maydell@xxxxxxxxxx> MAINTAINERS | 16 +- Makefile | 12 +- Makefile.objs | 1 + accel/kvm/kvm-all.c | 21 +- accel/tcg/translate-all.c | 6 +- backends/Kconfig | 1 + backends/Makefile.objs | 2 +- backends/tpm.c | 208 ---- backends/tpm/Kconfig | 14 + backends/tpm/Makefile.objs | 4 + backends/tpm/tpm_backend.c | 208 ++++ backends/tpm/tpm_emulator.c | 997 +++++++++++++++ backends/tpm/tpm_int.h | 88 ++ backends/tpm/tpm_ioctl.h | 271 +++++ backends/tpm/tpm_passthrough.c | 405 ++++++ backends/tpm/tpm_util.c | 380 ++++++ backends/tpm/trace-events | 33 + block/nvme.c | 218 +++- block/trace-events | 2 +- blockdev.c | 27 +- chardev/char-socket.c | 9 +- configure | 108 +- cpus.c | 15 +- docs/index.html.in | 4 +- docs/nvdimm.txt | 10 + docs/qdev-device-use.txt | 17 +- docs/specs/tpm.rst | 16 +- docs/system/deprecated.rst | 71 +- exec.c | 54 +- fpu/softfloat.c | 87 +- hw/9pfs/9p.c | 6 +- hw/acpi/aml-build.c | 51 +- hw/acpi/generic_event_device.c | 29 + hw/acpi/pcihp.c | 3 +- hw/acpi/piix4.c | 23 +- hw/arm/Kconfig | 8 +- hw/arm/armsse.c | 61 +- hw/arm/armv7m.c | 7 +- hw/arm/aspeed.c | 62 +- hw/arm/aspeed_ast2600.c | 35 +- hw/arm/aspeed_soc.c | 29 +- hw/arm/bcm2835_peripherals.c | 12 +- hw/arm/cubieboard.c | 2 +- hw/arm/exynos4210.c | 2 +- hw/arm/fsl-imx25.c | 12 +- hw/arm/fsl-imx6.c | 12 +- hw/arm/imx25_pdk.c | 2 +- hw/arm/mcimx6ul-evk.c | 2 +- hw/arm/mcimx7d-sabre.c | 2 +- hw/arm/mps2-tz.c | 23 +- hw/arm/mps2.c | 67 +- hw/arm/msf2-som.c | 4 +- hw/arm/nrf51_soc.c | 6 +- hw/arm/nseries.c | 4 +- hw/arm/orangepi.c | 2 +- hw/arm/raspi.c | 2 +- hw/arm/realview.c | 3 +- hw/arm/sabrelite.c | 6 +- hw/arm/stm32f205_soc.c | 2 +- hw/arm/stm32f405_soc.c | 2 +- hw/arm/versatilepb.c | 3 +- hw/arm/vexpress.c | 6 +- hw/arm/virt-acpi-build.c | 34 + hw/arm/virt.c | 124 +- hw/arm/xilinx_zynq.c | 7 +- hw/arm/xlnx-versal-virt.c | 2 +- hw/arm/xlnx-zcu102.c | 10 +- hw/block/fdc.c | 193 ++- hw/block/nand.c | 2 +- hw/block/pflash_cfi01.c | 6 +- hw/block/pflash_cfi02.c | 2 +- hw/char/ibex_uart.c | 2 +- hw/char/virtio-serial-bus.c | 4 +- hw/core/bus.c | 8 +- hw/core/machine.c | 8 +- hw/core/numa.c | 7 + hw/core/qdev-properties-system.c | 151 ++- hw/core/qdev-properties.c | 17 + hw/display/ati.c | 92 +- hw/display/ati_dbg.c | 1 + hw/display/ati_regs.h | 1 + hw/display/sm501.c | 157 ++- hw/display/trace-events | 12 + hw/display/virtio-gpu-pci.c | 2 +- hw/display/virtio-vga.c | 2 +- hw/dma/sparc32_dma.c | 6 +- hw/dma/xilinx_axidma.c | 12 +- hw/hyperv/vmbus.c | 3 +- hw/i2c/core.c | 18 +- hw/i2c/versatile_i2c.c | 38 +- hw/i386/acpi-build.c | 214 +--- hw/i386/amd_iommu.c | 6 +- hw/i386/fw_cfg.c | 28 + hw/i386/fw_cfg.h | 1 + hw/i386/pc.c | 34 +- hw/i386/pc_piix.c | 1 + hw/i386/pc_q35.c | 1 + hw/i386/x86.c | 8 +- hw/ide/qdev.c | 4 +- hw/input/adb-kbd.c | 42 +- hw/input/adb-mouse.c | 65 +- hw/input/adb.c | 210 +++- hw/input/pckbd.c | 31 + hw/input/trace-events | 27 +- hw/intc/Kconfig | 3 + hw/intc/Makefile.objs | 1 + hw/intc/loongson_liointc.c | 242 ++++ hw/isa/isa-superio.c | 18 +- hw/m68k/q800.c | 3 +- hw/microblaze/petalogix_ml605_mmu.c | 5 +- hw/mips/cps.c | 35 +- hw/misc/mac_via.c | 411 ++++--- hw/misc/macio/cuda.c | 60 +- hw/misc/macio/macio.c | 3 +- hw/misc/macio/pmu.c | 47 +- hw/misc/pca9552.c | 217 +++- hw/misc/trace-events | 7 + hw/net/virtio-net.c | 2 +- hw/net/xilinx_axienet.c | 12 +- hw/pci/pci.c | 3 - hw/pci/pcie.c | 2 +- hw/pci/shpc.c | 2 +- hw/ppc/mac_newworld.c | 2 - hw/ppc/pnv.c | 12 +- hw/ppc/spapr.c | 10 +- hw/ppc/spapr_caps.c | 28 +- hw/ppc/spapr_drc.c | 4 +- hw/ppc/spapr_pci.c | 4 +- hw/ppc/spapr_vio.c | 6 +- hw/riscv/riscv_hart.c | 14 +- hw/riscv/sifive_u.c | 12 +- hw/s390x/ap-bridge.c | 2 +- hw/s390x/css-bridge.c | 2 +- hw/s390x/s390-pci-bus.c | 14 +- hw/scsi/megasas.c | 36 +- hw/scsi/scsi-bus.c | 4 +- hw/scsi/virtio-scsi.c | 4 +- hw/scsi/vmw_pvscsi.c | 2 +- hw/sd/milkymist-memcard.c | 7 +- hw/sd/pxa2xx_mmci.c | 15 +- hw/sd/sd.c | 2 +- hw/sd/ssi-sd.c | 3 +- hw/sparc64/sun4u.c | 9 +- hw/tpm/Kconfig | 21 +- hw/tpm/Makefile.objs | 3 - hw/tpm/tpm_crb.c | 4 +- hw/tpm/tpm_emulator.c | 997 --------------- hw/tpm/tpm_int.h | 75 -- hw/tpm/tpm_ioctl.h | 271 ----- hw/tpm/tpm_passthrough.c | 405 ------ hw/tpm/tpm_ppi.c | 1 + hw/tpm/tpm_ppi.h | 1 - hw/tpm/tpm_prop.h | 31 + hw/tpm/tpm_spapr.c | 4 +- hw/tpm/tpm_tis.h | 1 - hw/tpm/tpm_tis_common.c | 11 +- hw/tpm/tpm_tis_isa.c | 3 +- hw/tpm/tpm_tis_sysbus.c | 3 +- hw/tpm/tpm_util.c | 377 ------ hw/tpm/tpm_util.h | 85 -- hw/tpm/trace-events | 34 +- hw/usb/bus.c | 2 +- hw/usb/dev-mtp.c | 2 - hw/usb/dev-smartcard-reader.c | 2 +- hw/usb/hcd-xhci.h | 2 +- hw/virtio/vhost-user.c | 12 +- hw/virtio/virtio-iommu-pci.c | 4 +- hw/watchdog/cmsdk-apb-watchdog.c | 1 + hw/watchdog/trace-events | 1 + hw/xen/Makefile.objs | 2 +- hw/xen/xen-bus.c | 2 +- hw/xen/xen-legacy-backend.c | 2 +- hw/xtensa/xtfpga.c | 3 +- include/block/block.h | 4 +- include/exec/cpu-all.h | 8 +- include/exec/cpu-defs.h | 7 +- include/fpu/softfloat.h | 3 + include/hw/acpi/acpi-defs.h | 18 - include/hw/arm/aspeed.h | 12 +- include/hw/block/fdc.h | 5 +- include/hw/hyperv/vmbus-bridge.h | 3 +- include/hw/i2c/arm_sbcon_i2c.h | 35 + include/hw/i2c/i2c.h | 2 + include/hw/i386/pc.h | 1 - include/hw/input/adb.h | 26 +- include/hw/misc/mac_via.h | 2 +- include/hw/misc/macio/cuda.h | 4 - include/hw/misc/macio/pmu.h | 4 - include/hw/misc/pca9552.h | 16 +- include/hw/ppc/xive_regs.h | 2 +- include/hw/qdev-core.h | 5 +- include/hw/qdev-properties.h | 18 +- include/qemu/coroutine_int.h | 5 + include/qemu/osdep.h | 57 +- include/sysemu/blockdev.h | 2 + include/sysemu/tpm_util.h | 72 ++ memory.c | 29 +- migration/qemu-file.c | 2 +- migration/rdma.c | 19 +- pc-bios/bios-256k.bin | Bin 262144 -> 262144 bytes pc-bios/bios.bin | Bin 131072 -> 131072 bytes pc-bios/vgabios-ati.bin | Bin 39424 -> 39424 bytes pc-bios/vgabios-bochs-display.bin | Bin 28672 -> 28672 bytes pc-bios/vgabios-cirrus.bin | Bin 38912 -> 38912 bytes pc-bios/vgabios-qxl.bin | Bin 39424 -> 39424 bytes pc-bios/vgabios-ramfb.bin | Bin 28672 -> 28672 bytes pc-bios/vgabios-stdvga.bin | Bin 39424 -> 39424 bytes pc-bios/vgabios-virtio.bin | Bin 39424 -> 39424 bytes pc-bios/vgabios-vmware.bin | Bin 39424 -> 39424 bytes pc-bios/vgabios.bin | Bin 38912 -> 38912 bytes qemu-options.hx | 9 +- qga/commands-posix.c | 1 + replay/replay.c | 2 +- roms/config.seabios-128k | 3 + roms/seabios | 2 +- scripts/minikconf.py | 6 +- scripts/performance/topN_callgrind.py | 140 +++ scripts/performance/topN_perf.py | 149 +++ scripts/tracetool.py | 2 +- scripts/tracetool/__init__.py | 2 +- scripts/tracetool/backend/__init__.py | 2 +- scripts/tracetool/backend/dtrace.py | 2 +- scripts/tracetool/backend/log.py | 2 +- scripts/tracetool/backend/simple.py | 2 +- scripts/tracetool/backend/ust.py | 2 +- scripts/tracetool/format/__init__.py | 2 +- scripts/tracetool/format/c.py | 2 +- scripts/tracetool/format/d.py | 2 +- scripts/tracetool/format/h.py | 2 +- scripts/tracetool/format/stap.py | 2 +- scripts/tracetool/format/tcg_h.py | 2 +- scripts/tracetool/format/tcg_helper_c.py | 2 +- scripts/tracetool/format/tcg_helper_h.py | 2 +- scripts/tracetool/format/tcg_helper_wrapper_h.py | 2 +- scripts/tracetool/transform.py | 2 +- scripts/tracetool/vcpu.py | 2 +- softmmu/vl.c | 8 + stubs/Makefile.objs | 1 + stubs/cmos.c | 7 + target/arm/Makefile.objs | 1 + target/arm/cpu.c | 83 +- target/arm/cpu.h | 52 +- target/arm/cpu64.c | 15 +- target/arm/helper-a64.c | 96 +- target/arm/helper-a64.h | 16 + target/arm/helper-sve.h | 488 ++++++++ target/arm/helper.c | 421 +++++-- target/arm/helper.h | 2 + target/arm/internals.h | 153 ++- target/arm/kvm.c | 4 +- target/arm/kvm64.c | 14 +- target/arm/kvm_arm.h | 21 +- target/arm/m_helper.c | 11 +- target/arm/mte_helper.c | 906 ++++++++++++++ target/arm/neon-dp.decode | 106 ++ target/arm/op_helper.c | 16 + target/arm/sve_helper.c | 614 ++++++++-- target/arm/tlb_helper.c | 13 +- target/arm/translate-a64.c | 677 +++++++++-- target/arm/translate-a64.h | 5 + target/arm/translate-neon.inc.c | 1191 +++++++++++++++++- target/arm/translate-sve.c | 1420 ++++++++++++++-------- target/arm/translate-vfp.inc.c | 9 +- target/arm/translate.c | 1080 +--------------- target/arm/translate.h | 31 +- target/i386/cpu.c | 9 + target/i386/fpu_helper.c | 1396 +++++++++++++++++++-- target/i386/kvm.c | 46 +- target/m68k/softfloat.c | 83 -- target/m68k/softfloat.h | 1 - target/mips/kvm.c | 26 + target/mips/kvm_mips.h | 11 + target/ppc/translate_init.inc.c | 5 - target/xtensa/cpu.h | 1 + target/xtensa/overlay_tool.h | 8 +- target/xtensa/translate.c | 60 +- tests/check-block-qdict.c | 24 +- tests/check-block.sh | 12 +- tests/check-qobject.c | 5 +- tests/check-qom-proplist.c | 14 +- tests/data/acpi/pc/DSDT | Bin 5014 -> 4934 bytes tests/data/acpi/pc/DSDT.acpihmat | Bin 6338 -> 6258 bytes tests/data/acpi/pc/DSDT.bridge | Bin 6873 -> 6793 bytes tests/data/acpi/pc/DSDT.cphp | Bin 5477 -> 5397 bytes tests/data/acpi/pc/DSDT.dimmpxm | Bin 6667 -> 6587 bytes tests/data/acpi/pc/DSDT.ipmikcs | Bin 5086 -> 5006 bytes tests/data/acpi/pc/DSDT.memhp | Bin 6373 -> 6293 bytes tests/data/acpi/pc/DSDT.numamem | Bin 5020 -> 4940 bytes tests/data/acpi/q35/DSDT | Bin 7752 -> 7678 bytes tests/data/acpi/q35/DSDT.acpihmat | Bin 9076 -> 9002 bytes tests/data/acpi/q35/DSDT.bridge | Bin 7769 -> 7695 bytes tests/data/acpi/q35/DSDT.cphp | Bin 8215 -> 8141 bytes tests/data/acpi/q35/DSDT.dimmpxm | Bin 9405 -> 9331 bytes tests/data/acpi/q35/DSDT.ipmibt | Bin 7827 -> 7753 bytes tests/data/acpi/q35/DSDT.memhp | Bin 9111 -> 9037 bytes tests/data/acpi/q35/DSDT.mmio64 | Bin 8882 -> 8808 bytes tests/data/acpi/q35/DSDT.numamem | Bin 7758 -> 7684 bytes tests/data/acpi/q35/DSDT.tis | Bin 8357 -> 8283 bytes tests/qemu-iotests/172 | 27 +- tests/qemu-iotests/172.out | 642 +++++++++- tests/qtest/arm-cpu-features.c | 38 +- tests/qtest/bios-tables-test-allowed-diff.h | 18 + tests/qtest/bios-tables-test.c | 2 +- tests/qtest/libqos/pci-pc.c | 2 +- tests/qtest/test-x86-cpuid-compat.c | 4 +- tests/qtest/tpm-emu.c | 2 +- tests/qtest/usb-hcd-ehci-test.c | 2 +- tests/tcg/i386/test-i386-f2xm1.c | 1140 +++++++++++++++++ tests/tcg/i386/test-i386-fpatan.c | 1071 ++++++++++++++++ tests/tcg/i386/test-i386-fyl2x.c | 1161 ++++++++++++++++++ tests/tcg/i386/test-i386-fyl2xp1.c | 1156 ++++++++++++++++++ tests/test-base64.c | 3 +- tests/test-bdrv-graph-mod.c | 4 +- tests/test-block-iothread.c | 3 +- tests/test-crypto-cipher.c | 8 +- tests/test-io-task.c | 4 +- tests/test-logging.c | 12 +- tests/test-qemu-opts.c | 22 +- tests/test-replication.c | 109 +- tests/test-string-input-visitor.c | 33 +- tests/test-string-output-visitor.c | 16 +- tests/test-util-filemonitor.c | 1 + trace/simple.c | 20 +- trace/simple.h | 2 +- ui/vnc.c | 6 +- util/coroutine-sigaltstack.c | 4 + util/coroutine-ucontext.c | 28 + util/getauxval.c | 10 + util/oslib-posix.c | 15 + util/qemu-timer.c | 32 +- 330 files changed, 17977 insertions(+), 6506 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 51a45703b8..dec252f38b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -842,6 +842,7 @@ M: Peter Maydell <peter.maydell@xxxxxxxxxx> L: qemu-arm@xxxxxxxxxx S: Maintained F: hw/*/versatile* +F: include/hw/i2c/arm_sbcon_i2c.h F: hw/misc/arm_sysctl.c F: docs/system/arm/versatile.rst @@ -1095,6 +1096,12 @@ F: hw/isa/vt82c686.c F: hw/pci-host/bonito.c F: include/hw/isa/vt82c686.h +Loongson-3 virtual platforms +M: Huacai Chen <chenhc@xxxxxxxxxx> +R: Jiaxun Yang <jiaxun.yang@xxxxxxxxxxx> +S: Maintained +F: hw/intc/loongson_liointc.c + Boston M: Paul Burton <pburton@xxxxxxxxxxxx> R: Aleksandar Rikalo <aleksandar.rikalo@xxxxxxxxxx> @@ -2430,7 +2437,7 @@ F: hw/tpm/* F: include/hw/acpi/tpm.h F: include/sysemu/tpm* F: qapi/tpm.json -F: backends/tpm.c +F: backends/tpm/ F: tests/qtest/*tpm* T: git https://github.com/stefanberger/qemu-tpm.git tpm-next @@ -3018,3 +3025,10 @@ M: Peter Maydell <peter.maydell@xxxxxxxxxx> S: Maintained F: docs/conf.py F: docs/*/conf.py + +Miscellaneous +------------- +Performance Tools and Tests +M: Ahmed Karaman <ahmedkhaledkaraman@xxxxxxxxx> +S: Maintained +F: scripts/performance/ diff --git a/Makefile b/Makefile index 48f23aa978..b1b8a5a6d0 100644 --- a/Makefile +++ b/Makefile @@ -418,7 +418,7 @@ MINIKCONF_ARGS = \ CONFIG_LINUX=$(CONFIG_LINUX) \ CONFIG_PVRDMA=$(CONFIG_PVRDMA) -MINIKCONF_INPUTS = $(SRC_PATH)/Kconfig.host $(SRC_PATH)/hw/Kconfig +MINIKCONF_INPUTS = $(SRC_PATH)/Kconfig.host $(SRC_PATH)/backends/Kconfig $(SRC_PATH)/hw/Kconfig MINIKCONF_DEPS = $(MINIKCONF_INPUTS) $(wildcard $(SRC_PATH)/hw/*/Kconfig) MINIKCONF = $(PYTHON) $(SRC_PATH)/scripts/minikconf.py \ @@ -873,8 +873,9 @@ install-sphinxdocs: sphinxdocs install-doc: $(DOCS) install-sphinxdocs $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)" $(INSTALL_DATA) $(MANUAL_BUILDDIR)/index.html "$(DESTDIR)$(qemu_docdir)" - $(INSTALL_DATA) docs/interop/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)" - $(INSTALL_DATA) docs/interop/qemu-qmp-ref.txt "$(DESTDIR)$(qemu_docdir)" + $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)/interop" + $(INSTALL_DATA) docs/interop/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)/interop" + $(INSTALL_DATA) docs/interop/qemu-qmp-ref.txt "$(DESTDIR)$(qemu_docdir)/interop" ifdef CONFIG_POSIX $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1" $(INSTALL_DATA) $(MANUAL_BUILDDIR)/system/qemu.1 "$(DESTDIR)$(mandir)/man1" @@ -892,8 +893,9 @@ ifdef CONFIG_TRACE_SYSTEMTAP endif ifneq (,$(findstring qemu-ga,$(TOOLS))) $(INSTALL_DATA) $(MANUAL_BUILDDIR)/interop/qemu-ga.8 "$(DESTDIR)$(mandir)/man8" - $(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)" - $(INSTALL_DATA) docs/interop/qemu-ga-ref.txt "$(DESTDIR)$(qemu_docdir)" + $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)/interop" + $(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)/interop" + $(INSTALL_DATA) docs/interop/qemu-ga-ref.txt "$(DESTDIR)$(qemu_docdir)/interop" $(INSTALL_DATA) docs/interop/qemu-ga-ref.7 "$(DESTDIR)$(mandir)/man7" endif endif diff --git a/Makefile.objs b/Makefile.objs index 7ce2588b89..98383972ee 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -125,6 +125,7 @@ trace-events-subdirs = trace-events-subdirs += accel/kvm trace-events-subdirs += accel/tcg trace-events-subdirs += backends +trace-events-subdirs += backends/tpm trace-events-subdirs += crypto trace-events-subdirs += monitor ifeq ($(CONFIG_USER_ONLY),y) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index f24d7da783..d54a8701d8 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -101,7 +101,7 @@ struct KVMState bool kernel_irqchip_required; OnOffAuto kernel_irqchip_split; bool sync_mmu; - bool manual_dirty_log_protect; + uint64_t manual_dirty_log_protect; /* The man page (and posix) say ioctl numbers are signed int, but * they're not. Linux, glibc and *BSD all treat ioctl numbers as * unsigned, and treating them as signed here can break things */ @@ -1995,6 +1995,7 @@ static int kvm_init(MachineState *ms) int ret; int type = 0; const char *kvm_type; + uint64_t dirty_log_manual_caps; s = KVM_STATE(ms->accelerator); @@ -2120,14 +2121,20 @@ static int kvm_init(MachineState *ms) s->coalesced_pio = s->coalesced_mmio && kvm_check_extension(s, KVM_CAP_COALESCED_PIO); - s->manual_dirty_log_protect = + dirty_log_manual_caps = kvm_check_extension(s, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2); - if (s->manual_dirty_log_protect) { - ret = kvm_vm_enable_cap(s, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2, 0, 1); + dirty_log_manual_caps &= (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | + KVM_DIRTY_LOG_INITIALLY_SET); + s->manual_dirty_log_protect = dirty_log_manual_caps; + if (dirty_log_manual_caps) { + ret = kvm_vm_enable_cap(s, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2, 0, + dirty_log_manual_caps); if (ret) { - warn_report("Trying to enable KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 " - "but failed. Falling back to the legacy mode. "); - s->manual_dirty_log_protect = false; + warn_report("Trying to enable capability %"PRIu64" of " + "KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2 but failed. " + "Falling back to the legacy mode. ", + dirty_log_manual_caps); + s->manual_dirty_log_protect = 0; } } diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index c3d37058a1..2afa46bd2b 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -2582,9 +2582,9 @@ int page_check_range(target_ulong start, target_ulong len, int flags) /* This function should never be called with addresses outside the guest address space. If this assert fires, it probably indicates a missing call to h2g_valid. */ -#if TARGET_ABI_BITS > L1_MAP_ADDR_SPACE_BITS - assert(start < ((target_ulong)1 << L1_MAP_ADDR_SPACE_BITS)); -#endif + if (TARGET_ABI_BITS > L1_MAP_ADDR_SPACE_BITS) { + assert(start < ((target_ulong)1 << L1_MAP_ADDR_SPACE_BITS)); + } if (len == 0) { return 0; diff --git a/backends/Kconfig b/backends/Kconfig new file mode 100644 index 0000000000..f35abc1609 --- /dev/null +++ b/backends/Kconfig @@ -0,0 +1 @@ +source tpm/Kconfig diff --git a/backends/Makefile.objs b/backends/Makefile.objs index 28a847cd57..22d204cb48 100644 --- a/backends/Makefile.objs +++ b/backends/Makefile.objs @@ -1,7 +1,7 @@ common-obj-y += rng.o rng-egd.o rng-builtin.o common-obj-$(CONFIG_POSIX) += rng-random.o -common-obj-$(CONFIG_TPM) += tpm.o +common-obj-$(CONFIG_TPM) += tpm/ common-obj-y += hostmem.o hostmem-ram.o common-obj-$(CONFIG_POSIX) += hostmem-file.o diff --git a/backends/tpm.c b/backends/tpm.c deleted file mode 100644 index 375587e743..0000000000 --- a/backends/tpm.c +++ /dev/null @@ -1,208 +0,0 @@ -/* - * QEMU TPM Backend - * - * Copyright IBM, Corp. 2013 - * - * Authors: - * Stefan Berger <stefanb@xxxxxxxxxx> - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - * Based on backends/rng.c by Anthony Liguori - */ - -#include "qemu/osdep.h" -#include "sysemu/tpm_backend.h" -#include "qapi/error.h" -#include "sysemu/tpm.h" -#include "qemu/thread.h" -#include "qemu/main-loop.h" -#include "qemu/module.h" -#include "block/thread-pool.h" -#include "qemu/error-report.h" - -static void tpm_backend_request_completed(void *opaque, int ret) -{ - TPMBackend *s = TPM_BACKEND(opaque); - TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif); - - tic->request_completed(s->tpmif, ret); - - /* no need for atomic, as long the BQL is taken */ - s->cmd = NULL; - object_unref(OBJECT(s)); -} - -static int tpm_backend_worker_thread(gpointer data) -{ - TPMBackend *s = TPM_BACKEND(data); - TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); - Error *err = NULL; - - k->handle_request(s, s->cmd, &err); - if (err) { - error_report_err(err); - return -1; - } - - return 0; -} - -void tpm_backend_finish_sync(TPMBackend *s) -{ - while (s->cmd) { - aio_poll(qemu_get_aio_context(), true); - } -} - -enum TpmType tpm_backend_get_type(TPMBackend *s) -{ - TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); - - return k->type; -} - -int tpm_backend_init(TPMBackend *s, TPMIf *tpmif, Error **errp) -{ - if (s->tpmif) { - error_setg(errp, "TPM backend '%s' is already initialized", s->id); - return -1; - } - - s->tpmif = tpmif; - object_ref(OBJECT(tpmif)); - - s->had_startup_error = false; - - return 0; -} - -int tpm_backend_startup_tpm(TPMBackend *s, size_t buffersize) -{ - int res = 0; - TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); - - /* terminate a running TPM */ - tpm_backend_finish_sync(s); - - res = k->startup_tpm ? k->startup_tpm(s, buffersize) : 0; - - s->had_startup_error = (res != 0); - - return res; -} - -bool tpm_backend_had_startup_error(TPMBackend *s) -{ - return s->had_startup_error; -} - -void tpm_backend_deliver_request(TPMBackend *s, TPMBackendCmd *cmd) -{ - ThreadPool *pool = aio_get_thread_pool(qemu_get_aio_context()); - - if (s->cmd != NULL) { - error_report("There is a TPM request pending"); - return; - } - - s->cmd = cmd; - object_ref(OBJECT(s)); - thread_pool_submit_aio(pool, tpm_backend_worker_thread, s, - tpm_backend_request_completed, s); -} - -void tpm_backend_reset(TPMBackend *s) -{ - TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); - - if (k->reset) { - k->reset(s); - } - - tpm_backend_finish_sync(s); - - s->had_startup_error = false; -} - -void tpm_backend_cancel_cmd(TPMBackend *s) -{ - TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); - - k->cancel_cmd(s); -} - -bool tpm_backend_get_tpm_established_flag(TPMBackend *s) -{ - TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); - - return k->get_tpm_established_flag ? - k->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->reset_tpm_established_flag ? - k->reset_tpm_established_flag(s, locty) : 0; -} - -TPMVersion tpm_backend_get_tpm_version(TPMBackend *s) -{ - TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); - - return k->get_tpm_version(s); -} - -size_t tpm_backend_get_buffer_size(TPMBackend *s) -{ - TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); - - return k->get_buffer_size(s); -} - -TPMInfo *tpm_backend_query_tpm(TPMBackend *s) -{ - TPMInfo *info = g_new0(TPMInfo, 1); - TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); - TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif); - - info->id = g_strdup(s->id); - info->model = tic->model; - info->options = k->get_tpm_options(s); - - return info; -} - -static void tpm_backend_instance_finalize(Object *obj) -{ - TPMBackend *s = TPM_BACKEND(obj); - - object_unref(OBJECT(s->tpmif)); - g_free(s->id); -} - -static const TypeInfo tpm_backend_info = { - .name = TYPE_TPM_BACKEND, - .parent = TYPE_OBJECT, - .instance_size = sizeof(TPMBackend), - .instance_finalize = tpm_backend_instance_finalize, - .class_size = sizeof(TPMBackendClass), - .abstract = true, -}; - -static const TypeInfo tpm_if_info = { - .name = TYPE_TPM_IF, - .parent = TYPE_INTERFACE, - .class_size = sizeof(TPMIfClass), -}; - -static void register_types(void) -{ - type_register_static(&tpm_backend_info); - type_register_static(&tpm_if_info); -} - -type_init(register_types); diff --git a/backends/tpm/Kconfig b/backends/tpm/Kconfig new file mode 100644 index 0000000000..5d91eb89c2 --- /dev/null +++ b/backends/tpm/Kconfig @@ -0,0 +1,14 @@ +config TPM_BACKEND + bool + depends on TPM + +config TPM_PASSTHROUGH + bool + default y + # FIXME: should check for x86 host as well + depends on TPM_BACKEND && LINUX + +config TPM_EMULATOR + bool + default y + depends on TPM_BACKEND diff --git a/backends/tpm/Makefile.objs b/backends/tpm/Makefile.objs new file mode 100644 index 0000000000..db2731f634 --- /dev/null +++ b/backends/tpm/Makefile.objs @@ -0,0 +1,4 @@ +common-obj-y += tpm_backend.o +common-obj-y += tpm_util.o +common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o +common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o diff --git a/backends/tpm/tpm_backend.c b/backends/tpm/tpm_backend.c new file mode 100644 index 0000000000..375587e743 --- /dev/null +++ b/backends/tpm/tpm_backend.c @@ -0,0 +1,208 @@ +/* + * QEMU TPM Backend + * + * Copyright IBM, Corp. 2013 + * + * Authors: + * Stefan Berger <stefanb@xxxxxxxxxx> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * Based on backends/rng.c by Anthony Liguori + */ + +#include "qemu/osdep.h" +#include "sysemu/tpm_backend.h" +#include "qapi/error.h" +#include "sysemu/tpm.h" +#include "qemu/thread.h" +#include "qemu/main-loop.h" +#include "qemu/module.h" +#include "block/thread-pool.h" +#include "qemu/error-report.h" + +static void tpm_backend_request_completed(void *opaque, int ret) +{ + TPMBackend *s = TPM_BACKEND(opaque); + TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif); + + tic->request_completed(s->tpmif, ret); + + /* no need for atomic, as long the BQL is taken */ + s->cmd = NULL; + object_unref(OBJECT(s)); +} + +static int tpm_backend_worker_thread(gpointer data) +{ + TPMBackend *s = TPM_BACKEND(data); + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + Error *err = NULL; + + k->handle_request(s, s->cmd, &err); + if (err) { + error_report_err(err); + return -1; + } + + return 0; +} + +void tpm_backend_finish_sync(TPMBackend *s) +{ + while (s->cmd) { + aio_poll(qemu_get_aio_context(), true); + } +} + +enum TpmType tpm_backend_get_type(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + return k->type; +} + +int tpm_backend_init(TPMBackend *s, TPMIf *tpmif, Error **errp) +{ + if (s->tpmif) { + error_setg(errp, "TPM backend '%s' is already initialized", s->id); + return -1; + } + + s->tpmif = tpmif; + object_ref(OBJECT(tpmif)); + + s->had_startup_error = false; + + return 0; +} + +int tpm_backend_startup_tpm(TPMBackend *s, size_t buffersize) +{ + int res = 0; + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + /* terminate a running TPM */ + tpm_backend_finish_sync(s); + + res = k->startup_tpm ? k->startup_tpm(s, buffersize) : 0; + + s->had_startup_error = (res != 0); + + return res; +} + +bool tpm_backend_had_startup_error(TPMBackend *s) +{ + return s->had_startup_error; +} + +void tpm_backend_deliver_request(TPMBackend *s, TPMBackendCmd *cmd) +{ + ThreadPool *pool = aio_get_thread_pool(qemu_get_aio_context()); + + if (s->cmd != NULL) { + error_report("There is a TPM request pending"); + return; + } + + s->cmd = cmd; + object_ref(OBJECT(s)); + thread_pool_submit_aio(pool, tpm_backend_worker_thread, s, + tpm_backend_request_completed, s); +} + +void tpm_backend_reset(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + if (k->reset) { + k->reset(s); + } + + tpm_backend_finish_sync(s); + + s->had_startup_error = false; +} + +void tpm_backend_cancel_cmd(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + k->cancel_cmd(s); +} + +bool tpm_backend_get_tpm_established_flag(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + return k->get_tpm_established_flag ? + k->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->reset_tpm_established_flag ? + k->reset_tpm_established_flag(s, locty) : 0; +} + +TPMVersion tpm_backend_get_tpm_version(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + return k->get_tpm_version(s); +} + +size_t tpm_backend_get_buffer_size(TPMBackend *s) +{ + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + return k->get_buffer_size(s); +} + +TPMInfo *tpm_backend_query_tpm(TPMBackend *s) +{ + TPMInfo *info = g_new0(TPMInfo, 1); + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif); + + info->id = g_strdup(s->id); + info->model = tic->model; + info->options = k->get_tpm_options(s); + + return info; +} + +static void tpm_backend_instance_finalize(Object *obj) +{ + TPMBackend *s = TPM_BACKEND(obj); + + object_unref(OBJECT(s->tpmif)); + g_free(s->id); +} + +static const TypeInfo tpm_backend_info = { + .name = TYPE_TPM_BACKEND, + .parent = TYPE_OBJECT, + .instance_size = sizeof(TPMBackend), + .instance_finalize = tpm_backend_instance_finalize, + .class_size = sizeof(TPMBackendClass), + .abstract = true, +}; + +static const TypeInfo tpm_if_info = { + .name = TYPE_TPM_IF, + .parent = TYPE_INTERFACE, + .class_size = sizeof(TPMIfClass), +}; + +static void register_types(void) +{ + type_register_static(&tpm_backend_info); + type_register_static(&tpm_if_info); +} + +type_init(register_types); diff --git a/backends/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c new file mode 100644 index 0000000000..9605339f93 --- /dev/null +++ b/backends/tpm/tpm_emulator.c @@ -0,0 +1,997 @@ +/* + * Emulator TPM driver + * + * Copyright (c) 2017 Intel Corporation + * Author: Amarnath Valluri <amarnath.valluri@xxxxxxxxx> + * + * Copyright (c) 2010 - 2013, 2018 IBM Corporation + * Authors: + * Stefan Berger <stefanb@xxxxxxxxxx> + * + * Copyright (C) 2011 IAIK, Graz University of Technology + * Author: Andreas Niederl + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/> + * + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/module.h" +#include "qemu/sockets.h" +#include "io/channel-socket.h" +#include "sysemu/tpm_backend.h" +#include "sysemu/tpm_util.h" +#include "tpm_int.h" +#include "tpm_ioctl.h" +#include "migration/blocker.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qapi/clone-visitor.h" +#include "qapi/qapi-visit-tpm.h" +#include "chardev/char-fe.h" +#include "trace.h" + +#define TYPE_TPM_EMULATOR "tpm-emulator" +#define TPM_EMULATOR(obj) \ + OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR) + +#define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap)) + +/* data structures */ + +/* blobs from the TPM; part of VM state when migrating */ +typedef struct TPMBlobBuffers { + uint32_t permanent_flags; + TPMSizedBuffer permanent; + + uint32_t volatil_flags; + TPMSizedBuffer volatil; + + uint32_t savestate_flags; + TPMSizedBuffer savestate; +} TPMBlobBuffers; + +typedef struct TPMEmulator { + TPMBackend parent; + + TPMEmulatorOptions *options; + CharBackend ctrl_chr; + QIOChannel *data_ioc; + TPMVersion tpm_version; + ptm_cap caps; /* capabilities of the TPM */ + uint8_t cur_locty_number; /* last set locality */ + Error *migration_blocker; + + QemuMutex mutex; + + unsigned int established_flag:1; + unsigned int established_flag_cached:1; + + TPMBlobBuffers state_blobs; +} TPMEmulator; + +struct tpm_error { + uint32_t tpm_result; + const char *string; +}; + +static const struct tpm_error tpm_errors[] = { + /* TPM 1.2 error codes */ + { TPM_BAD_PARAMETER , "a parameter is bad" }, + { TPM_FAIL , "operation failed" }, + { TPM_KEYNOTFOUND , "key could not be found" }, + { TPM_BAD_PARAM_SIZE , "bad parameter size"}, + { TPM_ENCRYPT_ERROR , "encryption error" }, + { TPM_DECRYPT_ERROR , "decryption error" }, + { TPM_BAD_KEY_PROPERTY, "bad key property" }, + { TPM_BAD_MODE , "bad (encryption) mode" }, + { TPM_BAD_VERSION , "bad version identifier" }, + { TPM_BAD_LOCALITY , "bad locality" }, + /* TPM 2 error codes */ + { TPM_RC_FAILURE , "operation failed" }, + { TPM_RC_LOCALITY , "bad locality" }, + { TPM_RC_INSUFFICIENT, "insufficient amount of data" }, +}; + +static const char *tpm_emulator_strerror(uint32_t tpm_result) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(tpm_errors); i++) { + if (tpm_errors[i].tpm_result == tpm_result) { + return tpm_errors[i].string; + } + } + return ""; +} + +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)); + memcpy(buf + sizeof(cmd_no), msg, msg_len_in); + + n = qemu_chr_fe_write_all(dev, buf, n); + if (n <= 0) { + goto end; + } + + if (msg_len_out != 0) { + n = qemu_chr_fe_read_all(dev, msg, msg_len_out); + if (n <= 0) { + goto end; + } + } + + ret = 0; + +end: + qemu_mutex_unlock(&tpm->mutex); + return ret; +} + +static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu, + const uint8_t *in, uint32_t in_len, + uint8_t *out, uint32_t out_len, + bool *selftest_done, + Error **errp) +{ + ssize_t ret; + bool is_selftest = false; + + if (selftest_done) { + *selftest_done = false; + is_selftest = tpm_util_is_selftest(in, in_len); + } + + ret = qio_channel_write_all(tpm_emu->data_ioc, (char *)in, in_len, errp); + if (ret != 0) { + return -1; + } + + ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out, + sizeof(struct tpm_resp_hdr), errp); + if (ret != 0) { + return -1; + } + + ret = qio_channel_read_all(tpm_emu->data_ioc, + (char *)out + sizeof(struct tpm_resp_hdr), + tpm_cmd_get_size(out) - sizeof(struct tpm_resp_hdr), errp); + if (ret != 0) { + return -1; + } + + if (is_selftest) { + *selftest_done = tpm_cmd_get_errcode(out) == 0; + } + + return 0; +} + +static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number, + Error **errp) +{ + ptm_loc loc; + + if (tpm_emu->cur_locty_number == locty_number) { + return 0; + } + + trace_tpm_emulator_set_locality(locty_number); + + memset(&loc, 0, sizeof(loc)); + loc.u.req.loc = locty_number; + 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)); + return -1; + } + + loc.u.resp.tpm_result = be32_to_cpu(loc.u.resp.tpm_result); + if (loc.u.resp.tpm_result != 0) { + error_setg(errp, "tpm-emulator: TPM result for set locality : 0x%x", + loc.u.resp.tpm_result); + return -1; + } + + tpm_emu->cur_locty_number = locty_number; + + return 0; +} + +static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd, + Error **errp) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + + trace_tpm_emulator_handle_request(); + + if (tpm_emulator_set_locality(tpm_emu, cmd->locty, errp) < 0 || + tpm_emulator_unix_tx_bufs(tpm_emu, cmd->in, cmd->in_len, + cmd->out, cmd->out_len, + &cmd->selftest_done, errp) < 0) { + tpm_util_write_fatal_error_response(cmd->out, cmd->out_len); + } +} + +static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu) +{ + 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; + } + + tpm_emu->caps = be64_to_cpu(tpm_emu->caps); + + trace_tpm_emulator_probe_caps(tpm_emu->caps); + + return 0; +} + +static int tpm_emulator_check_caps(TPMEmulator *tpm_emu) +{ + ptm_cap caps = 0; + const char *tpm = NULL; + + /* check for min. required capabilities */ + switch (tpm_emu->tpm_version) { + case TPM_VERSION_1_2: + caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED | + PTM_CAP_SET_LOCALITY | PTM_CAP_SET_DATAFD | PTM_CAP_STOP | + PTM_CAP_SET_BUFFERSIZE; + tpm = "1.2"; + break; + case TPM_VERSION_2_0: + caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED | + PTM_CAP_SET_LOCALITY | PTM_CAP_RESET_TPMESTABLISHED | + PTM_CAP_SET_DATAFD | PTM_CAP_STOP | PTM_CAP_SET_BUFFERSIZE; + tpm = "2"; + break; + case TPM_VERSION_UNSPEC: + error_report("tpm-emulator: TPM version has not been set"); + return -1; + } + + if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) { + error_report("tpm-emulator: TPM does not implement minimum set of " + "required capabilities for TPM %s (0x%x)", tpm, (int)caps); + return -1; + } + + return 0; +} + +static int tpm_emulator_stop_tpm(TPMBackend *tb) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + ptm_res res; + + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_STOP, &res, 0, sizeof(res)) < 0) { + error_report("tpm-emulator: Could not stop TPM: %s", + strerror(errno)); + return -1; + } + + res = be32_to_cpu(res); + if (res) { + error_report("tpm-emulator: TPM result for CMD_STOP: 0x%x %s", res, + tpm_emulator_strerror(res)); + return -1; + } + + return 0; +} + +static int tpm_emulator_set_buffer_size(TPMBackend *tb, + size_t wanted_size, + size_t *actual_size) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + ptm_setbuffersize psbs; + + if (tpm_emulator_stop_tpm(tb) < 0) { + return -1; + } + + psbs.u.req.buffersize = cpu_to_be32(wanted_size); + + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_BUFFERSIZE, &psbs, + sizeof(psbs.u.req), sizeof(psbs.u.resp)) < 0) { + error_report("tpm-emulator: Could not set buffer size: %s", + strerror(errno)); + return -1; + } + + psbs.u.resp.tpm_result = be32_to_cpu(psbs.u.resp.tpm_result); + if (psbs.u.resp.tpm_result != 0) { + error_report("tpm-emulator: TPM result for set buffer size : 0x%x %s", + psbs.u.resp.tpm_result, + tpm_emulator_strerror(psbs.u.resp.tpm_result)); + return -1; + } + + if (actual_size) { + *actual_size = be32_to_cpu(psbs.u.resp.buffersize); + } + + trace_tpm_emulator_set_buffer_size( + be32_to_cpu(psbs.u.resp.buffersize), + be32_to_cpu(psbs.u.resp.minsize), + be32_to_cpu(psbs.u.resp.maxsize)); + + return 0; +} + +static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize, + bool is_resume) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + ptm_init init = { + .u.req.init_flags = 0, + }; + ptm_res res; + + trace_tpm_emulator_startup_tpm_resume(is_resume, buffersize); + + if (buffersize != 0 && + tpm_emulator_set_buffer_size(tb, buffersize, NULL) < 0) { + goto err_exit; + } + + if (is_resume) { + init.u.req.init_flags |= cpu_to_be32(PTM_INIT_FLAG_DELETE_VOLATILE); + } + + 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; + } + + res = be32_to_cpu(init.u.resp.tpm_result); + if (res) { + error_report("tpm-emulator: TPM result for CMD_INIT: 0x%x %s", res, + tpm_emulator_strerror(res)); + goto err_exit; + } + return 0; + +err_exit: + return -1; +} + +static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize) +{ + return tpm_emulator_startup_tpm_resume(tb, buffersize, false); +} + +static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + ptm_est est; + + if (tpm_emu->established_flag_cached) { + return tpm_emu->established_flag; + } + + 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)); + return false; + } + trace_tpm_emulator_get_tpm_established_flag(est.u.resp.bit); + + tpm_emu->established_flag_cached = 1; + tpm_emu->established_flag = (est.u.resp.bit != 0); + + return tpm_emu->established_flag; +} + +static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb, + uint8_t locty) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + ptm_reset_est reset_est; + ptm_res res; + + /* only a TPM 2.0 will support this */ + if (tpm_emu->tpm_version != TPM_VERSION_2_0) { + return 0; + } + + reset_est.u.req.loc = tpm_emu->cur_locty_number; + 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", + strerror(errno)); + return -1; + } + + res = be32_to_cpu(reset_est.u.resp.tpm_result); + if (res) { + error_report( + "tpm-emulator: TPM result for rest established flag: 0x%x %s", + res, tpm_emulator_strerror(res)); + return -1; + } + + tpm_emu->established_flag_cached = 0; + + return 0; +} + +static void tpm_emulator_cancel_cmd(TPMBackend *tb) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + ptm_res res; + + if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, PTM_CAP_CANCEL_TPM_CMD)) { + trace_tpm_emulator_cancel_cmd_not_supt(); + return; + } + + /* FIXME: make the function non-blocking, or it may block a VCPU */ + 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)); + } else if (res != 0) { + error_report("tpm-emulator: Failed to cancel TPM: 0x%x", + be32_to_cpu(res)); + } +} + +static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + + return tpm_emu->tpm_version; +} + +static size_t tpm_emulator_get_buffer_size(TPMBackend *tb) +{ + size_t actual_size; + + if (tpm_emulator_set_buffer_size(tb, 0, &actual_size) < 0) { + return 4096; + } + + return actual_size; +} + +static int tpm_emulator_block_migration(TPMEmulator *tpm_emu) +{ + Error *err = NULL; + ptm_cap caps = PTM_CAP_GET_STATEBLOB | PTM_CAP_SET_STATEBLOB | + PTM_CAP_STOP; + + if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) { + error_setg(&tpm_emu->migration_blocker, + "Migration disabled: TPM emulator does not support " + "migration"); + migrate_add_blocker(tpm_emu->migration_blocker, &err); + if (err) { + error_report_err(err); + error_free(tpm_emu->migration_blocker); + tpm_emu->migration_blocker = NULL; + + return -1; + } + } + + return 0; +} + +static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu) +{ + ptm_res res; + Error *err = NULL; + int fds[2] = { -1, -1 }; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { + error_report("tpm-emulator: Failed to create socketpair"); + return -1; + } + + qemu_chr_fe_set_msgfds(&tpm_emu->ctrl_chr, fds + 1, 1); + + 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; + } + + tpm_emu->data_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err)); + if (err) { + error_prepend(&err, "tpm-emulator: Failed to create io channel: "); + error_report_err(err); + goto err_exit; + } + + closesocket(fds[1]); + + return 0; + +err_exit: + closesocket(fds[0]); + closesocket(fds[1]); + return -1; +} + +static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts) +{ + const char *value; + + value = qemu_opt_get(opts, "chardev"); + if (value) { + Error *err = NULL; + Chardev *dev = qemu_chr_find(value); + + if (!dev) { + error_report("tpm-emulator: tpm chardev '%s' not found.", value); + goto err; + } + + if (!qemu_chr_fe_init(&tpm_emu->ctrl_chr, dev, &err)) { + error_prepend(&err, "tpm-emulator: No valid chardev found at '%s':", + value); + error_report_err(err); + goto err; + } + + tpm_emu->options->chardev = g_strdup(value); + } + + if (tpm_emulator_prepare_data_fd(tpm_emu) < 0) { + goto err; + } + + /* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as it also used + * by passthrough driver, which not yet using GIOChannel. + */ + if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_emu->data_ioc)->fd, + &tpm_emu->tpm_version)) { + error_report("'%s' is not emulating TPM device. Error: %s", + tpm_emu->options->chardev, strerror(errno)); + goto err; + } + + switch (tpm_emu->tpm_version) { + case TPM_VERSION_1_2: + trace_tpm_emulator_handle_device_opts_tpm12(); + break; + case TPM_VERSION_2_0: + trace_tpm_emulator_handle_device_opts_tpm2(); + break; + default: + trace_tpm_emulator_handle_device_opts_unspec(); + } + + if (tpm_emulator_probe_caps(tpm_emu) || + tpm_emulator_check_caps(tpm_emu)) { + goto err; + } + + return tpm_emulator_block_migration(tpm_emu); + +err: + trace_tpm_emulator_handle_device_opts_startup_error(); + + return -1; +} + +static TPMBackend *tpm_emulator_create(QemuOpts *opts) +{ + TPMBackend *tb = TPM_BACKEND(object_new(TYPE_TPM_EMULATOR)); + + if (tpm_emulator_handle_device_opts(TPM_EMULATOR(tb), opts)) { + object_unref(OBJECT(tb)); + return NULL; + } + + return tb; +} + +static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + TpmTypeOptions *options = g_new0(TpmTypeOptions, 1); + + options->type = TPM_TYPE_OPTIONS_KIND_EMULATOR; + options->u.emulator.data = QAPI_CLONE(TPMEmulatorOptions, tpm_emu->options); + + return options; +} + +static const QemuOptDesc tpm_emulator_cmdline_opts[] = { + TPM_STANDARD_CMDLINE_OPTS, + { + .name = "chardev", + .type = QEMU_OPT_STRING, + .help = "Character device to use for out-of-band control messages", + }, + { /* end of list */ }, +}; + +/* + * Transfer a TPM state blob from the TPM into a provided buffer. + * + * @tpm_emu: TPMEmulator + * @type: the type of blob to transfer + * @tsb: the TPMSizeBuffer to fill with the blob + * @flags: the flags to return to the caller + */ +static int tpm_emulator_get_state_blob(TPMEmulator *tpm_emu, + uint8_t type, + TPMSizedBuffer *tsb, + uint32_t *flags) +{ + ptm_getstate pgs; + ptm_res res; + ssize_t n; + uint32_t totlength, length; + + tpm_sized_buffer_reset(tsb); + + pgs.u.req.state_flags = cpu_to_be32(PTM_STATE_FLAG_DECRYPTED); + pgs.u.req.type = cpu_to_be32(type); + pgs.u.req.offset = 0; + + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_STATEBLOB, + &pgs, sizeof(pgs.u.req), + offsetof(ptm_getstate, u.resp.data)) < 0) { + error_report("tpm-emulator: could not get state blob type %d : %s", + type, strerror(errno)); + return -1; + } + + res = be32_to_cpu(pgs.u.resp.tpm_result); + if (res != 0 && (res & 0x800) == 0) { + error_report("tpm-emulator: Getting the stateblob (type %d) failed " + "with a TPM error 0x%x %s", type, res, + tpm_emulator_strerror(res)); + return -1; + } + + totlength = be32_to_cpu(pgs.u.resp.totlength); + length = be32_to_cpu(pgs.u.resp.length); + if (totlength != length) { + error_report("tpm-emulator: Expecting to read %u bytes " + "but would get %u", totlength, length); + return -1; + } + + *flags = be32_to_cpu(pgs.u.resp.state_flags); + + if (totlength > 0) { + tsb->buffer = g_try_malloc(totlength); + if (!tsb->buffer) { + error_report("tpm-emulator: Out of memory allocating %u bytes", + totlength); + return -1; + } + + n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr, tsb->buffer, totlength); + if (n != totlength) { + error_report("tpm-emulator: Could not read stateblob (type %d); " + "expected %u bytes, got %zd", + type, totlength, n); + return -1; + } + } + tsb->size = totlength; + + trace_tpm_emulator_get_state_blob(type, tsb->size, *flags); + + return 0; +} + +static int tpm_emulator_get_state_blobs(TPMEmulator *tpm_emu) +{ + TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; + + if (tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT, + &state_blobs->permanent, + &state_blobs->permanent_flags) < 0 || + tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE, + &state_blobs->volatil, + &state_blobs->volatil_flags) < 0 || + tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE, + &state_blobs->savestate, + &state_blobs->savestate_flags) < 0) { + goto err_exit; + } + + return 0; + + err_exit: + tpm_sized_buffer_reset(&state_blobs->volatil); + tpm_sized_buffer_reset(&state_blobs->permanent); + tpm_sized_buffer_reset(&state_blobs->savestate); + + return -1; +} + +/* + * Transfer a TPM state blob to the TPM emulator. + * + * @tpm_emu: TPMEmulator + * @type: the type of TPM state blob to transfer + * @tsb: TPMSizedBuffer containing the TPM state blob + * @flags: Flags describing the (encryption) state of the TPM state blob + */ +static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu, + uint32_t type, + TPMSizedBuffer *tsb, + uint32_t flags) +{ + ssize_t n; + ptm_setstate pss; + ptm_res tpm_result; + + if (tsb->size == 0) { + return 0; + } + + pss = (ptm_setstate) { + .u.req.state_flags = cpu_to_be32(flags), + .u.req.type = cpu_to_be32(type), + .u.req.length = cpu_to_be32(tsb->size), + }; + + /* write the header only */ + if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_STATEBLOB, &pss, + offsetof(ptm_setstate, u.req.data), 0) < 0) { + error_report("tpm-emulator: could not set state blob type %d : %s", + type, strerror(errno)); + return -1; + } + + /* now the body */ + n = qemu_chr_fe_write_all(&tpm_emu->ctrl_chr, tsb->buffer, tsb->size); + if (n != tsb->size) { + error_report("tpm-emulator: Writing the stateblob (type %d) " + "failed; could not write %u bytes, but only %zd", + type, tsb->size, n); + return -1; + } + + /* now get the result */ + n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr, + (uint8_t *)&pss, sizeof(pss.u.resp)); + if (n != sizeof(pss.u.resp)) { + error_report("tpm-emulator: Reading response from writing stateblob " + "(type %d) failed; expected %zu bytes, got %zd", type, + sizeof(pss.u.resp), n); + return -1; + } + + tpm_result = be32_to_cpu(pss.u.resp.tpm_result); + if (tpm_result != 0) { + error_report("tpm-emulator: Setting the stateblob (type %d) failed " + "with a TPM error 0x%x %s", type, tpm_result, + tpm_emulator_strerror(tpm_result)); + return -1; + } + + trace_tpm_emulator_set_state_blob(type, tsb->size, flags); + + return 0; +} + +/* + * Set all the TPM state blobs. + * + * Returns a negative errno code in case of error. + */ +static int tpm_emulator_set_state_blobs(TPMBackend *tb) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; + + trace_tpm_emulator_set_state_blobs(); + + if (tpm_emulator_stop_tpm(tb) < 0) { + trace_tpm_emulator_set_state_blobs_error("Could not stop TPM"); + return -EIO; + } + + if (tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT, + &state_blobs->permanent, + state_blobs->permanent_flags) < 0 || + tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE, + &state_blobs->volatil, + state_blobs->volatil_flags) < 0 || + tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE, + &state_blobs->savestate, + state_blobs->savestate_flags) < 0) { + return -EIO; + } + + trace_tpm_emulator_set_state_blobs_done(); + + return 0; +} + +static int tpm_emulator_pre_save(void *opaque) +{ + TPMBackend *tb = opaque; + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + + trace_tpm_emulator_pre_save(); + + tpm_backend_finish_sync(tb); + + /* get the state blobs from the TPM */ + return tpm_emulator_get_state_blobs(tpm_emu); +} + +/* + * Load the TPM state blobs into the TPM. + * + * Returns negative errno codes in case of error. + */ +static int tpm_emulator_post_load(void *opaque, int version_id) +{ + TPMBackend *tb = opaque; + int ret; + + ret = tpm_emulator_set_state_blobs(tb); + if (ret < 0) { + return ret; + } + + if (tpm_emulator_startup_tpm_resume(tb, 0, true) < 0) { + return -EIO; + } + + return 0; +} + +static const VMStateDescription vmstate_tpm_emulator = { + .name = "tpm-emulator", + .version_id = 0, + .pre_save = tpm_emulator_pre_save, + .post_load = tpm_emulator_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(state_blobs.permanent_flags, TPMEmulator), + VMSTATE_UINT32(state_blobs.permanent.size, TPMEmulator), + VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.permanent.buffer, + TPMEmulator, 0, 0, + state_blobs.permanent.size), + + VMSTATE_UINT32(state_blobs.volatil_flags, TPMEmulator), + VMSTATE_UINT32(state_blobs.volatil.size, TPMEmulator), + VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.volatil.buffer, + TPMEmulator, 0, 0, + state_blobs.volatil.size), + + VMSTATE_UINT32(state_blobs.savestate_flags, TPMEmulator), + VMSTATE_UINT32(state_blobs.savestate.size, TPMEmulator), + VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.savestate.buffer, + TPMEmulator, 0, 0, + state_blobs.savestate.size), + + VMSTATE_END_OF_LIST() + } +}; + +static void tpm_emulator_inst_init(Object *obj) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(obj); + + trace_tpm_emulator_inst_init(); + + tpm_emu->options = g_new0(TPMEmulatorOptions, 1); + tpm_emu->cur_locty_number = ~0; + qemu_mutex_init(&tpm_emu->mutex); + + vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, + &vmstate_tpm_emulator, obj); +} + +/* + * Gracefully shut down the external TPM + */ +static void tpm_emulator_shutdown(TPMEmulator *tpm_emu) +{ + ptm_res res; + + 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) { + error_report("tpm-emulator: TPM result for shutdown: 0x%x %s", + be32_to_cpu(res), tpm_emulator_strerror(be32_to_cpu(res))); + } +} + +static void tpm_emulator_inst_finalize(Object *obj) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(obj); + TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; + + tpm_emulator_shutdown(tpm_emu); + + object_unref(OBJECT(tpm_emu->data_ioc)); + + qemu_chr_fe_deinit(&tpm_emu->ctrl_chr, false); + + qapi_free_TPMEmulatorOptions(tpm_emu->options); + + if (tpm_emu->migration_blocker) { + migrate_del_blocker(tpm_emu->migration_blocker); + error_free(tpm_emu->migration_blocker); + } + + tpm_sized_buffer_reset(&state_blobs->volatil); + tpm_sized_buffer_reset(&state_blobs->permanent); + tpm_sized_buffer_reset(&state_blobs->savestate); + + qemu_mutex_destroy(&tpm_emu->mutex); + + vmstate_unregister(NULL, &vmstate_tpm_emulator, obj); +} + +static void tpm_emulator_class_init(ObjectClass *klass, void *data) +{ + TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); + + tbc->type = TPM_TYPE_EMULATOR; + tbc->opts = tpm_emulator_cmdline_opts; + tbc->desc = "TPM emulator backend driver"; + tbc->create = tpm_emulator_create; + tbc->startup_tpm = tpm_emulator_startup_tpm; + tbc->cancel_cmd = tpm_emulator_cancel_cmd; + tbc->get_tpm_established_flag = tpm_emulator_get_tpm_established_flag; + tbc->reset_tpm_established_flag = tpm_emulator_reset_tpm_established_flag; + tbc->get_tpm_version = tpm_emulator_get_tpm_version; + tbc->get_buffer_size = tpm_emulator_get_buffer_size; + tbc->get_tpm_options = tpm_emulator_get_tpm_options; + + tbc->handle_request = tpm_emulator_handle_request; +} + +static const TypeInfo tpm_emulator_info = { + .name = TYPE_TPM_EMULATOR, + .parent = TYPE_TPM_BACKEND, + .instance_size = sizeof(TPMEmulator), + .class_init = tpm_emulator_class_init, + .instance_init = tpm_emulator_inst_init, + .instance_finalize = tpm_emulator_inst_finalize, +}; + +static void tpm_emulator_register(void) +{ + type_register_static(&tpm_emulator_info); +} + +type_init(tpm_emulator_register) diff --git a/backends/tpm/tpm_int.h b/backends/tpm/tpm_int.h new file mode 100644 index 0000000000..ba6109306e --- /dev/null +++ b/backends/tpm/tpm_int.h @@ -0,0 +1,88 @@ +/* + * TPM configuration + * + * Copyright (C) 2011-2013 IBM Corporation + * + * Authors: + * Stefan Berger <stefanb@xxxxxxxxxx> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef BACKENDS_TPM_INT_H +#define BACKENDS_TPM_INT_H + +#include "qemu/option.h" +#include "sysemu/tpm.h" + +#define TPM_STANDARD_CMDLINE_OPTS \ + { \ + .name = "type", \ + .type = QEMU_OPT_STRING, \ + .help = "Type of TPM backend", \ + } + +struct tpm_req_hdr { + uint16_t tag; + uint32_t len; + uint32_t ordinal; +} QEMU_PACKED; + +struct tpm_resp_hdr { + uint16_t tag; + uint32_t len; + uint32_t errcode; +} QEMU_PACKED; + +#define TPM_TAG_RQU_COMMAND 0xc1 +#define TPM_TAG_RQU_AUTH1_COMMAND 0xc2 +#define TPM_TAG_RQU_AUTH2_COMMAND 0xc3 + +#define TPM_TAG_RSP_COMMAND 0xc4 +#define TPM_TAG_RSP_AUTH1_COMMAND 0xc5 +#define TPM_TAG_RSP_AUTH2_COMMAND 0xc6 + +#define TPM_BAD_PARAMETER 3 +#define TPM_FAIL 9 +#define TPM_KEYNOTFOUND 13 +#define TPM_BAD_PARAM_SIZE 25 +#define TPM_ENCRYPT_ERROR 32 +#define TPM_DECRYPT_ERROR 33 +#define TPM_BAD_KEY_PROPERTY 40 +#define TPM_BAD_MODE 44 +#define TPM_BAD_VERSION 46 +#define TPM_BAD_LOCALITY 61 + +#define TPM_ORD_ContinueSelfTest 0x53 +#define TPM_ORD_GetTicks 0xf1 +#define TPM_ORD_GetCapability 0x65 + +#define TPM_CAP_PROPERTY 0x05 + +#define TPM_CAP_PROP_INPUT_BUFFER 0x124 + +/* TPM2 defines */ +#define TPM2_ST_NO_SESSIONS 0x8001 + +#define TPM2_CC_ReadClock 0x00000181 +#define TPM2_CC_GetCapability 0x0000017a + +#define TPM2_CAP_TPM_PROPERTIES 0x6 + +#define TPM2_PT_MAX_COMMAND_SIZE 0x11e + +#define TPM_RC_INSUFFICIENT 0x9a +#define TPM_RC_FAILURE 0x101 +#define TPM_RC_LOCALITY 0x907 + +int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version, + size_t *buffersize); + +typedef struct TPMSizedBuffer { + uint32_t size; + uint8_t *buffer; +} TPMSizedBuffer; + +void tpm_sized_buffer_reset(TPMSizedBuffer *tsb); + +#endif /* BACKENDS_TPM_INT_H */ diff --git a/backends/tpm/tpm_ioctl.h b/backends/tpm/tpm_ioctl.h new file mode 100644 index 0000000000..f5f5c553a9 --- /dev/null +++ b/backends/tpm/tpm_ioctl.h @@ -0,0 +1,271 @@ +/* + * tpm_ioctl.h + * + * (c) Copyright IBM Corporation 2014, 2015. + * + * This file is licensed under the terms of the 3-clause BSD license + */ + +#ifndef TPM_IOCTL_H +#define TPM_IOCTL_H + +#include <sys/uio.h> +#include <sys/ioctl.h> + +/* + * Every response from a command involving a TPM command execution must hold + * the ptm_res as the first element. + * ptm_res corresponds to the error code of a command executed by the TPM. + */ + +typedef uint32_t ptm_res; + +/* PTM_GET_TPMESTABLISHED: get the establishment bit */ +struct ptm_est { + union { + struct { + ptm_res tpm_result; + unsigned char bit; /* TPM established bit */ + } resp; /* response */ + } u; +}; + +/* PTM_RESET_TPMESTABLISHED: reset establishment bit */ +struct ptm_reset_est { + union { + struct { + uint8_t loc; /* locality to use */ + } req; /* request */ + struct { + ptm_res tpm_result; + } resp; /* response */ + } u; +}; + +/* PTM_INIT */ +struct ptm_init { + union { + struct { + uint32_t init_flags; /* see definitions below */ + } req; /* request */ + struct { + ptm_res tpm_result; + } resp; /* response */ + } u; +}; + +/* above init_flags */ +#define PTM_INIT_FLAG_DELETE_VOLATILE (1 << 0) + /* delete volatile state file after reading it */ + +/* PTM_SET_LOCALITY */ +struct ptm_loc { + union { + struct { + uint8_t loc; /* locality to set */ + } req; /* request */ + struct { + ptm_res tpm_result; + } resp; /* response */ + } u; +}; + +/* PTM_HASH_DATA: hash given data */ +struct ptm_hdata { + union { + struct { + uint32_t length; + uint8_t data[4096]; + } req; /* request */ + struct { + ptm_res tpm_result; + } resp; /* response */ + } u; +}; + +/* + * size of the TPM state blob to transfer; x86_64 can handle 8k, + * ppc64le only ~7k; keep the response below a 4k page size + */ +#define PTM_STATE_BLOB_SIZE (3 * 1024) + +/* + * The following is the data structure to get state blobs from the TPM. + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple reads + * with this ioctl and with adjusted offset are necessary. All bytes + * must be transferred and the transfer is done once the last byte has been + * returned. + * It is possible to use the read() interface for reading the data; however, the + * first bytes of the state blob will be part of the response to the ioctl(); a + * subsequent read() is only necessary if the total length (totlength) exceeds + * the number of received bytes. seek() is not supported. + */ +struct ptm_getstate { + union { + struct { + uint32_t state_flags; /* may be: PTM_STATE_FLAG_DECRYPTED */ + uint32_t type; /* which blob to pull */ + uint32_t offset; /* offset from where to read */ + } req; /* request */ + struct { + ptm_res tpm_result; + uint32_t state_flags; /* may be: PTM_STATE_FLAG_ENCRYPTED */ + uint32_t totlength; /* total length that will be transferred */ + uint32_t length; /* number of bytes in following buffer */ + uint8_t data[PTM_STATE_BLOB_SIZE]; + } resp; /* response */ + } u; +}; + +/* TPM state blob types */ +#define PTM_BLOB_TYPE_PERMANENT 1 +#define PTM_BLOB_TYPE_VOLATILE 2 +#define PTM_BLOB_TYPE_SAVESTATE 3 + +/* state_flags above : */ +#define PTM_STATE_FLAG_DECRYPTED 1 /* on input: get decrypted state */ +#define PTM_STATE_FLAG_ENCRYPTED 2 /* on output: state is encrypted */ + +/* + * The following is the data structure to set state blobs in the TPM. + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple + * 'writes' using this ioctl are necessary. The last packet is indicated + * by the length being smaller than the PTM_STATE_BLOB_SIZE. + * The very first packet may have a length indicator of '0' enabling + * a write() with all the bytes from a buffer. If the write() interface + * is used, a final ioctl with a non-full buffer must be made to indicate + * that all data were transferred (a write with 0 bytes would not work). + */ +struct ptm_setstate { + union { + struct { + uint32_t state_flags; /* may be PTM_STATE_FLAG_ENCRYPTED */ + uint32_t type; /* which blob to set */ + uint32_t length; /* length of the data; + use 0 on the first packet to + transfer using write() */ + uint8_t data[PTM_STATE_BLOB_SIZE]; + } req; /* request */ + struct { + ptm_res tpm_result; + } resp; /* response */ + } u; +}; + +/* + * PTM_GET_CONFIG: Data structure to get runtime configuration information + * such as which keys are applied. + */ +struct ptm_getconfig { + union { + struct { + ptm_res tpm_result; + uint32_t flags; + } resp; /* response */ + } u; +}; + +#define PTM_CONFIG_FLAG_FILE_KEY 0x1 +#define PTM_CONFIG_FLAG_MIGRATION_KEY 0x2 + +/* + * PTM_SET_BUFFERSIZE: Set the buffer size to be used by the TPM. + * A 0 on input queries for the current buffer size. Any other + * number will try to set the buffer size. The returned number is + * the buffer size that will be used, which can be larger than the + * requested one, if it was below the minimum, or smaller than the + * requested one, if it was above the maximum. + */ +struct ptm_setbuffersize { + union { + struct { + uint32_t buffersize; /* 0 to query for current buffer size */ + } req; /* request */ + struct { + ptm_res tpm_result; + uint32_t buffersize; /* buffer size in use */ + uint32_t minsize; /* min. supported buffer size */ + uint32_t maxsize; /* max. supported buffer size */ + } resp; /* response */ + } u; +}; + + +typedef uint64_t ptm_cap; +typedef struct ptm_est ptm_est; +typedef struct ptm_reset_est ptm_reset_est; +typedef struct ptm_loc ptm_loc; +typedef struct ptm_hdata ptm_hdata; +typedef struct ptm_init ptm_init; +typedef struct ptm_getstate ptm_getstate; +typedef struct ptm_setstate ptm_setstate; +typedef struct ptm_getconfig ptm_getconfig; +typedef struct ptm_setbuffersize ptm_setbuffersize; + +/* capability flags returned by PTM_GET_CAPABILITY */ +#define PTM_CAP_INIT (1) +#define PTM_CAP_SHUTDOWN (1 << 1) +#define PTM_CAP_GET_TPMESTABLISHED (1 << 2) +#define PTM_CAP_SET_LOCALITY (1 << 3) +#define PTM_CAP_HASHING (1 << 4) +#define PTM_CAP_CANCEL_TPM_CMD (1 << 5) +#define PTM_CAP_STORE_VOLATILE (1 << 6) +#define PTM_CAP_RESET_TPMESTABLISHED (1 << 7) +#define PTM_CAP_GET_STATEBLOB (1 << 8) +#define PTM_CAP_SET_STATEBLOB (1 << 9) +#define PTM_CAP_STOP (1 << 10) +#define PTM_CAP_GET_CONFIG (1 << 11) +#define PTM_CAP_SET_DATAFD (1 << 12) +#define PTM_CAP_SET_BUFFERSIZE (1 << 13) + +enum { + PTM_GET_CAPABILITY = _IOR('P', 0, ptm_cap), + PTM_INIT = _IOWR('P', 1, ptm_init), + PTM_SHUTDOWN = _IOR('P', 2, ptm_res), + PTM_GET_TPMESTABLISHED = _IOR('P', 3, ptm_est), + PTM_SET_LOCALITY = _IOWR('P', 4, ptm_loc), + PTM_HASH_START = _IOR('P', 5, ptm_res), + PTM_HASH_DATA = _IOWR('P', 6, ptm_hdata), + PTM_HASH_END = _IOR('P', 7, ptm_res), + PTM_CANCEL_TPM_CMD = _IOR('P', 8, ptm_res), + PTM_STORE_VOLATILE = _IOR('P', 9, ptm_res), + PTM_RESET_TPMESTABLISHED = _IOWR('P', 10, ptm_reset_est), + PTM_GET_STATEBLOB = _IOWR('P', 11, ptm_getstate), + PTM_SET_STATEBLOB = _IOWR('P', 12, ptm_setstate), + PTM_STOP = _IOR('P', 13, ptm_res), + PTM_GET_CONFIG = _IOR('P', 14, ptm_getconfig), + PTM_SET_DATAFD = _IOR('P', 15, ptm_res), + PTM_SET_BUFFERSIZE = _IOWR('P', 16, ptm_setbuffersize), +}; + +/* + * Commands used by the non-CUSE TPMs + * + * All messages container big-endian data. + * + * The return messages only contain the 'resp' part of the unions + * in the data structures above. Besides that the limits in the + * buffers above (ptm_hdata:u.req.data and ptm_get_state:u.resp.data + * and ptm_set_state:u.req.data) are 0xffffffff. + */ +enum { + CMD_GET_CAPABILITY = 1, + CMD_INIT, + CMD_SHUTDOWN, + CMD_GET_TPMESTABLISHED, + CMD_SET_LOCALITY, + CMD_HASH_START, + CMD_HASH_DATA, + CMD_HASH_END, + CMD_CANCEL_TPM_CMD, + CMD_STORE_VOLATILE, + CMD_RESET_TPMESTABLISHED, + CMD_GET_STATEBLOB, + CMD_SET_STATEBLOB, + CMD_STOP, + CMD_GET_CONFIG, + CMD_SET_DATAFD, + CMD_SET_BUFFERSIZE, +}; + +#endif /* TPM_IOCTL_H */ diff --git a/backends/tpm/tpm_passthrough.c b/backends/tpm/tpm_passthrough.c new file mode 100644 index 0000000000..7403807ec4 --- /dev/null +++ b/backends/tpm/tpm_passthrough.c @@ -0,0 +1,405 @@ +/* + * passthrough TPM driver + * + * Copyright (c) 2010 - 2013 IBM Corporation + * Authors: + * Stefan Berger <stefanb@xxxxxxxxxx> + * + * Copyright (C) 2011 IAIK, Graz University of Technology + * Author: Andreas Niederl + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/> + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qemu/error-report.h" +#include "qemu/module.h" +#include "qemu/sockets.h" +#include "sysemu/tpm_backend.h" +#include "sysemu/tpm_util.h" +#include "tpm_int.h" +#include "qapi/clone-visitor.h" +#include "qapi/qapi-visit-tpm.h" +#include "trace.h" + +#define TYPE_TPM_PASSTHROUGH "tpm-passthrough" +#define TPM_PASSTHROUGH(obj) \ + OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH) + +/* data structures */ +struct TPMPassthruState { + TPMBackend parent; + + TPMPassthroughOptions *options; + const char *tpm_dev; + int tpm_fd; + bool tpm_executing; + bool tpm_op_canceled; + int cancel_fd; + + TPMVersion tpm_version; + size_t tpm_buffersize; +}; + +typedef struct TPMPassthruState TPMPassthruState; + +#define TPM_PASSTHROUGH_DEFAULT_DEVICE "/dev/tpm0" + +/* functions */ + +static void tpm_passthrough_cancel_cmd(TPMBackend *tb); + +static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len) +{ + int ret; + reread: + ret = read(fd, buf, len); + if (ret < 0) { + if (errno != EINTR && errno != EAGAIN) { + return -1; + } + goto reread; + } + return ret; +} + +static void tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, + const uint8_t *in, uint32_t in_len, + uint8_t *out, uint32_t out_len, + bool *selftest_done, Error **errp) +{ + ssize_t ret; + bool is_selftest; + + /* FIXME: protect shared variables or use other sync mechanism */ + tpm_pt->tpm_op_canceled = false; + tpm_pt->tpm_executing = true; + *selftest_done = false; + + is_selftest = tpm_util_is_selftest(in, in_len); + + ret = qemu_write_full(tpm_pt->tpm_fd, in, in_len); + if (ret != in_len) { + if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) { + error_setg_errno(errp, errno, "tpm_passthrough: error while " + "transmitting data to TPM"); + } + goto err_exit; + } + + tpm_pt->tpm_executing = false; + + ret = tpm_passthrough_unix_read(tpm_pt->tpm_fd, out, out_len); + if (ret < 0) { + if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) { + error_setg_errno(errp, errno, "tpm_passthrough: error while " + "reading data from TPM"); + } + } else if (ret < sizeof(struct tpm_resp_hdr) || + tpm_cmd_get_size(out) != ret) { + ret = -1; + error_setg_errno(errp, errno, "tpm_passthrough: received invalid " + "response packet from TPM"); + } + + if (is_selftest && (ret >= sizeof(struct tpm_resp_hdr))) { + *selftest_done = tpm_cmd_get_errcode(out) == 0; + } + +err_exit: + if (ret < 0) { + tpm_util_write_fatal_error_response(out, out_len); + } + + tpm_pt->tpm_executing = false; +} + +static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd *cmd, + Error **errp) +{ + TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); + + trace_tpm_passthrough_handle_request(cmd); + + tpm_passthrough_unix_tx_bufs(tpm_pt, cmd->in, cmd->in_len, + cmd->out, cmd->out_len, &cmd->selftest_done, + errp); +} + +static void tpm_passthrough_reset(TPMBackend *tb) +{ + trace_tpm_passthrough_reset(); + + tpm_passthrough_cancel_cmd(tb); +} + +static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb) +{ + return false; +} + +static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb, + uint8_t locty) +{ + /* only a TPM 2.0 will support this */ + return 0; +} + +static void tpm_passthrough_cancel_cmd(TPMBackend *tb) +{ + TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); + int n; + + /* + * As of Linux 3.7 the tpm_tis driver does not properly cancel + * commands on all TPM manufacturers' TPMs. + * Only cancel if we're busy so we don't cancel someone else's + * command, e.g., a command executed on the host. + */ + if (tpm_pt->tpm_executing) { + if (tpm_pt->cancel_fd >= 0) { + tpm_pt->tpm_op_canceled = true; + n = write(tpm_pt->cancel_fd, "-", 1); + if (n != 1) { + error_report("Canceling TPM command failed: %s", + strerror(errno)); + } + } else { + error_report("Cannot cancel TPM command due to missing " + "TPM sysfs cancel entry"); + } + } +} + +static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb) +{ + TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); + + return tpm_pt->tpm_version; +} + +static size_t tpm_passthrough_get_buffer_size(TPMBackend *tb) +{ + TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); + int ret; + + ret = tpm_util_get_buffer_size(tpm_pt->tpm_fd, tpm_pt->tpm_version, + &tpm_pt->tpm_buffersize); + if (ret < 0) { + tpm_pt->tpm_buffersize = 4096; + } + return tpm_pt->tpm_buffersize; +} + +/* + * Unless path or file descriptor set has been provided by user, + * determine the sysfs cancel file following kernel documentation + * in Documentation/ABI/stable/sysfs-class-tpm. + * From /dev/tpm0 create /sys/class/tpm/tpm0/device/cancel + * before 4.0: /sys/class/misc/tpm0/device/cancel + */ +static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt) +{ + int fd = -1; + char *dev; + char path[PATH_MAX]; + + if (tpm_pt->options->cancel_path) { + fd = qemu_open(tpm_pt->options->cancel_path, O_WRONLY); + if (fd < 0) { + error_report("tpm_passthrough: Could not open TPM cancel path: %s", + strerror(errno)); + } + return fd; + } + + dev = strrchr(tpm_pt->tpm_dev, '/'); + if (!dev) { + error_report("tpm_passthrough: Bad TPM device path %s", + tpm_pt->tpm_dev); + return -1; + } + + dev++; + if (snprintf(path, sizeof(path), "/sys/class/tpm/%s/device/cancel", + dev) < sizeof(path)) { + fd = qemu_open(path, O_WRONLY); + if (fd < 0) { + if (snprintf(path, sizeof(path), "/sys/class/misc/%s/device/cancel", + dev) < sizeof(path)) { + fd = qemu_open(path, O_WRONLY); + } + } + } + + if (fd < 0) { + error_report("tpm_passthrough: Could not guess TPM cancel path"); + } else { + tpm_pt->options->cancel_path = g_strdup(path); + } + + return fd; +} + +static int +tpm_passthrough_handle_device_opts(TPMPassthruState *tpm_pt, QemuOpts *opts) +{ + const char *value; + + value = qemu_opt_get(opts, "cancel-path"); + if (value) { + tpm_pt->options->cancel_path = g_strdup(value); + tpm_pt->options->has_cancel_path = true; + } + + value = qemu_opt_get(opts, "path"); + if (value) { + tpm_pt->options->has_path = true; + tpm_pt->options->path = g_strdup(value); + } + + tpm_pt->tpm_dev = value ? value : TPM_PASSTHROUGH_DEFAULT_DEVICE; + tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR); + if (tpm_pt->tpm_fd < 0) { + error_report("Cannot access TPM device using '%s': %s", + tpm_pt->tpm_dev, strerror(errno)); + return -1; + } + + if (tpm_util_test_tpmdev(tpm_pt->tpm_fd, &tpm_pt->tpm_version)) { + error_report("'%s' is not a TPM device.", + tpm_pt->tpm_dev); + return -1; + } + + tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tpm_pt); + if (tpm_pt->cancel_fd < 0) { + return -1; + } + + return 0; +} + +static TPMBackend *tpm_passthrough_create(QemuOpts *opts) +{ + Object *obj = object_new(TYPE_TPM_PASSTHROUGH); + + if (tpm_passthrough_handle_device_opts(TPM_PASSTHROUGH(obj), opts)) { + object_unref(obj); + return NULL; + } + + return TPM_BACKEND(obj); +} + +static int tpm_passthrough_startup_tpm(TPMBackend *tb, size_t buffersize) +{ + TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); + + if (buffersize && buffersize < tpm_pt->tpm_buffersize) { + error_report("Requested buffer size of %zu is smaller than host TPM's " + "fixed buffer size of %zu", + buffersize, tpm_pt->tpm_buffersize); + return -1; + } + + return 0; +} + +static TpmTypeOptions *tpm_passthrough_get_tpm_options(TPMBackend *tb) +{ + TpmTypeOptions *options = g_new0(TpmTypeOptions, 1); + + options->type = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH; + options->u.passthrough.data = QAPI_CLONE(TPMPassthroughOptions, + TPM_PASSTHROUGH(tb)->options); + + return options; +} + +static const QemuOptDesc tpm_passthrough_cmdline_opts[] = { + TPM_STANDARD_CMDLINE_OPTS, + { + .name = "cancel-path", + .type = QEMU_OPT_STRING, + .help = "Sysfs file entry for canceling TPM commands", + }, + { + .name = "path", + .type = QEMU_OPT_STRING, + .help = "Path to TPM device on the host", + }, + { /* end of list */ }, +}; + +static void tpm_passthrough_inst_init(Object *obj) +{ + TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(obj); + + tpm_pt->options = g_new0(TPMPassthroughOptions, 1); + tpm_pt->tpm_fd = -1; + tpm_pt->cancel_fd = -1; +} + +static void tpm_passthrough_inst_finalize(Object *obj) +{ + TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(obj); + + tpm_passthrough_cancel_cmd(TPM_BACKEND(obj)); + + if (tpm_pt->tpm_fd >= 0) { + qemu_close(tpm_pt->tpm_fd); + } + if (tpm_pt->cancel_fd >= 0) { + qemu_close(tpm_pt->cancel_fd); + } + qapi_free_TPMPassthroughOptions(tpm_pt->options); +} + +static void tpm_passthrough_class_init(ObjectClass *klass, void *data) +{ + TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); + + tbc->type = TPM_TYPE_PASSTHROUGH; + tbc->opts = tpm_passthrough_cmdline_opts; + tbc->desc = "Passthrough TPM backend driver"; + tbc->create = tpm_passthrough_create; + tbc->startup_tpm = tpm_passthrough_startup_tpm; + tbc->reset = tpm_passthrough_reset; + tbc->cancel_cmd = tpm_passthrough_cancel_cmd; + tbc->get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag; + tbc->reset_tpm_established_flag = + tpm_passthrough_reset_tpm_established_flag; + tbc->get_tpm_version = tpm_passthrough_get_tpm_version; + tbc->get_buffer_size = tpm_passthrough_get_buffer_size; + tbc->get_tpm_options = tpm_passthrough_get_tpm_options; + tbc->handle_request = tpm_passthrough_handle_request; +} + +static const TypeInfo tpm_passthrough_info = { + .name = TYPE_TPM_PASSTHROUGH, + .parent = TYPE_TPM_BACKEND, + .instance_size = sizeof(TPMPassthruState), + .class_init = tpm_passthrough_class_init, + .instance_init = tpm_passthrough_inst_init, + .instance_finalize = tpm_passthrough_inst_finalize, +}; + +static void tpm_passthrough_register(void) +{ + type_register_static(&tpm_passthrough_info); +} + +type_init(tpm_passthrough_register) diff --git a/backends/tpm/tpm_util.c b/backends/tpm/tpm_util.c new file mode 100644 index 0000000000..cfc7572a61 --- /dev/null +++ b/backends/tpm/tpm_util.c @@ -0,0 +1,380 @@ +/* + * TPM utility functions + * + * Copyright (c) 2010 - 2015 IBM Corporation + * Authors: + * Stefan Berger <stefanb@xxxxxxxxxx> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/> + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "qapi/visitor.h" +#include "tpm_int.h" +#include "exec/memory.h" +#include "hw/qdev-properties.h" +#include "sysemu/tpm_backend.h" +#include "sysemu/tpm_util.h" +#include "trace.h" + +/* tpm backend property */ + +static void get_tpm(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) +{ + DeviceState *dev = DEVICE(obj); + TPMBackend **be = qdev_get_prop_ptr(dev, opaque); + char *p; + + p = g_strdup(*be ? (*be)->id : ""); + visit_type_str(v, name, &p, errp); + g_free(p); +} + +static void set_tpm(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Error *local_err = NULL; + Property *prop = opaque; + TPMBackend *s, **be = qdev_get_prop_ptr(dev, prop); + char *str; + + if (dev->realized) { + qdev_prop_set_after_realize(dev, name, errp); + return; + } + + visit_type_str(v, name, &str, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + s = qemu_find_tpm_be(str); + if (s == NULL) { + error_setg(errp, "Property '%s.%s' can't find value '%s'", + object_get_typename(obj), prop->name, str); + } else if (tpm_backend_init(s, TPM_IF(obj), errp) == 0) { + *be = s; /* weak reference, avoid cyclic ref */ + } + g_free(str); +} + +static void release_tpm(Object *obj, const char *name, void *opaque) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + TPMBackend **be = qdev_get_prop_ptr(dev, prop); + + if (*be) { + tpm_backend_reset(*be); + } +} + +const PropertyInfo qdev_prop_tpm = { + .name = "str", + .description = "ID of a tpm to use as a backend", + .get = get_tpm, + .set = set_tpm, + .release = release_tpm, +}; + +/* + * Write an error message in the given output buffer. + */ +void tpm_util_write_fatal_error_response(uint8_t *out, uint32_t out_len) +{ + if (out_len >= sizeof(struct tpm_resp_hdr)) { + tpm_cmd_set_tag(out, TPM_TAG_RSP_COMMAND); + tpm_cmd_set_size(out, sizeof(struct tpm_resp_hdr)); + tpm_cmd_set_error(out, TPM_FAIL); + } +} + +bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len) +{ + if (in_len >= sizeof(struct tpm_req_hdr)) { + return tpm_cmd_get_ordinal(in) == TPM_ORD_ContinueSelfTest; + } + + return false; +} + +/* + * Send request to a TPM device. We expect a response within one second. + */ +static int tpm_util_request(int fd, + const void *request, + size_t requestlen, + void *response, + size_t responselen) +{ + fd_set readfds; + int n; + struct timeval tv = { + .tv_sec = 1, + .tv_usec = 0, + }; + + n = write(fd, request, requestlen); + if (n < 0) { + return -errno; + } + if (n != requestlen) { + return -EFAULT; + } + + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + + /* wait for a second */ + n = select(fd + 1, &readfds, NULL, NULL, &tv); + if (n != 1) { + return -errno; + } + + n = read(fd, response, responselen); + if (n < sizeof(struct tpm_resp_hdr)) { + return -EFAULT; + } + + /* check the header */ + if (tpm_cmd_get_size(response) != n) { + return -EMSGSIZE; + } + + return 0; +} + +/* + * A basic test of a TPM device. We expect a well formatted response header + * (error response is fine). + */ +static int tpm_util_test(int fd, + const void *request, + size_t requestlen, + uint16_t *return_tag) +{ + char buf[1024]; + ssize_t ret; + + ret = tpm_util_request(fd, request, requestlen, + buf, sizeof(buf)); + if (ret < 0) { + return ret; + } + + *return_tag = tpm_cmd_get_tag(buf); + + return 0; +} + +/* + * Probe for the TPM device in the back + * Returns 0 on success with the version of the probed TPM set, 1 on failure. + */ +int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version) +{ + /* + * Sending a TPM1.2 command to a TPM2 should return a TPM1.2 + * header (tag = 0xc4) and error code (TPM_BADTAG = 0x1e) + * + * Sending a TPM2 command to a TPM 2 will give a TPM 2 tag in the + * header. + * Sending a TPM2 command to a TPM 1.2 will give a TPM 1.2 tag + * in the header and an error code. + */ + const struct tpm_req_hdr test_req = { + .tag = cpu_to_be16(TPM_TAG_RQU_COMMAND), + .len = cpu_to_be32(sizeof(test_req)), + .ordinal = cpu_to_be32(TPM_ORD_GetTicks), + }; + + const struct tpm_req_hdr test_req_tpm2 = { + .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS), + .len = cpu_to_be32(sizeof(test_req_tpm2)),
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |