qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

spapr: initial implementation for H_TPM_COMM/spapr-tpm-proxy

This implements the H_TPM_COMM hypercall, which is used by an
Ultravisor to pass TPM commands directly to the host's TPM device, or
a TPM Resource Manager associated with the device.

This also introduces a new virtual device, spapr-tpm-proxy, which
is used to configure the host TPM path to be used to service
requests sent by H_TPM_COMM hcalls, for example:

-device spapr-tpm-proxy,id=tpmp0,host-path=/dev/tpmrm0

By default, no spapr-tpm-proxy will be created, and hcalls will return
H_FUNCTION.

The full specification for this hypercall can be found in
docs/specs/ppc-spapr-uv-hcalls.txt

Since SVM-related hcalls like H_TPM_COMM use a reserved range of
0xEF00-0xEF80, we introduce a separate hcall table here to handle
them.

Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com
Message-Id: <20190717205842.17827-3-mdroth@linux.vnet.ibm.com>
[dwg: Corrected #include for upstream change]
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>

authored by

Michael Roth and committed by
David Gibson
0fb6bd07 1daba4d1

+270 -1
+1
hw/ppc/Makefile.objs
··· 5 5 obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o 6 6 obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o 7 7 obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o spapr_irq.o 8 + obj-$(CONFIG_PSERIES) += spapr_tpm_proxy.o 8 9 obj-$(CONFIG_SPAPR_RNG) += spapr_rng.o 9 10 # IBM PowerNV 10 11 obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o pnv_occ.o pnv_bmc.o
+32 -1
hw/ppc/spapr.c
··· 79 79 #include "qemu/cutils.h" 80 80 #include "hw/ppc/spapr_cpu_core.h" 81 81 #include "hw/mem/memory-device.h" 82 + #include "hw/ppc/spapr_tpm_proxy.h" 82 83 83 84 #include <libfdt.h> 84 85 ··· 4036 4037 } 4037 4038 } 4038 4039 4040 + static void spapr_tpm_proxy_plug(HotplugHandler *hotplug_dev, DeviceState *dev, 4041 + Error **errp) 4042 + { 4043 + SpaprMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); 4044 + SpaprTpmProxy *tpm_proxy = SPAPR_TPM_PROXY(dev); 4045 + 4046 + if (spapr->tpm_proxy != NULL) { 4047 + error_setg(errp, "Only one TPM proxy can be specified for this machine"); 4048 + return; 4049 + } 4050 + 4051 + spapr->tpm_proxy = tpm_proxy; 4052 + } 4053 + 4054 + static void spapr_tpm_proxy_unplug(HotplugHandler *hotplug_dev, DeviceState *dev) 4055 + { 4056 + SpaprMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); 4057 + 4058 + object_property_set_bool(OBJECT(dev), false, "realized", NULL); 4059 + object_unparent(OBJECT(dev)); 4060 + spapr->tpm_proxy = NULL; 4061 + } 4062 + 4039 4063 static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, 4040 4064 DeviceState *dev, Error **errp) 4041 4065 { ··· 4045 4069 spapr_core_plug(hotplug_dev, dev, errp); 4046 4070 } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) { 4047 4071 spapr_phb_plug(hotplug_dev, dev, errp); 4072 + } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_TPM_PROXY)) { 4073 + spapr_tpm_proxy_plug(hotplug_dev, dev, errp); 4048 4074 } 4049 4075 } 4050 4076 ··· 4057 4083 spapr_core_unplug(hotplug_dev, dev); 4058 4084 } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) { 4059 4085 spapr_phb_unplug(hotplug_dev, dev); 4086 + } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_TPM_PROXY)) { 4087 + spapr_tpm_proxy_unplug(hotplug_dev, dev); 4060 4088 } 4061 4089 } 4062 4090 ··· 4091 4119 return; 4092 4120 } 4093 4121 spapr_phb_unplug_request(hotplug_dev, dev, errp); 4122 + } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_TPM_PROXY)) { 4123 + spapr_tpm_proxy_unplug(hotplug_dev, dev); 4094 4124 } 4095 4125 } 4096 4126 ··· 4111 4141 { 4112 4142 if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) || 4113 4143 object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE) || 4114 - object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) { 4144 + object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE) || 4145 + object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_TPM_PROXY)) { 4115 4146 return HOTPLUG_HANDLER(machine); 4116 4147 } 4117 4148 if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
+13
hw/ppc/spapr_hcall.c
··· 1963 1963 1964 1964 static spapr_hcall_fn papr_hypercall_table[(MAX_HCALL_OPCODE / 4) + 1]; 1965 1965 static spapr_hcall_fn kvmppc_hypercall_table[KVMPPC_HCALL_MAX - KVMPPC_HCALL_BASE + 1]; 1966 + static spapr_hcall_fn svm_hypercall_table[(SVM_HCALL_MAX - SVM_HCALL_BASE) / 4 + 1]; 1966 1967 1967 1968 void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn) 1968 1969 { ··· 1972 1973 assert((opcode & 0x3) == 0); 1973 1974 1974 1975 slot = &papr_hypercall_table[opcode / 4]; 1976 + } else if (opcode >= SVM_HCALL_BASE && opcode <= SVM_HCALL_MAX) { 1977 + /* we only have SVM-related hcall numbers assigned in multiples of 4 */ 1978 + assert((opcode & 0x3) == 0); 1979 + 1980 + slot = &svm_hypercall_table[(opcode - SVM_HCALL_BASE) / 4]; 1975 1981 } else { 1976 1982 assert((opcode >= KVMPPC_HCALL_BASE) && (opcode <= KVMPPC_HCALL_MAX)); 1977 1983 ··· 1990 1996 if ((opcode <= MAX_HCALL_OPCODE) 1991 1997 && ((opcode & 0x3) == 0)) { 1992 1998 spapr_hcall_fn fn = papr_hypercall_table[opcode / 4]; 1999 + 2000 + if (fn) { 2001 + return fn(cpu, spapr, opcode, args); 2002 + } 2003 + } else if ((opcode >= SVM_HCALL_BASE) && 2004 + (opcode <= SVM_HCALL_MAX)) { 2005 + spapr_hcall_fn fn = svm_hypercall_table[(opcode - SVM_HCALL_BASE) / 4]; 1993 2006 1994 2007 if (fn) { 1995 2008 return fn(cpu, spapr, opcode, args);
+178
hw/ppc/spapr_tpm_proxy.c
··· 1 + /* 2 + * SPAPR TPM Proxy/Hypercall 3 + * 4 + * Copyright IBM Corp. 2019 5 + * 6 + * Authors: 7 + * Michael Roth <mdroth@linux.vnet.ibm.com> 8 + * 9 + * This work is licensed under the terms of the GNU GPL, version 2 or later. 10 + * See the COPYING file in the top-level directory. 11 + */ 12 + 13 + #include "qemu/osdep.h" 14 + #include "qemu-common.h" 15 + #include "qapi/error.h" 16 + #include "qemu/error-report.h" 17 + #include "sysemu/reset.h" 18 + #include "cpu.h" 19 + #include "hw/ppc/spapr.h" 20 + #include "hw/qdev-properties.h" 21 + #include "trace.h" 22 + 23 + #define TPM_SPAPR_BUFSIZE 4096 24 + 25 + enum { 26 + TPM_COMM_OP_EXECUTE = 1, 27 + TPM_COMM_OP_CLOSE_SESSION = 2, 28 + }; 29 + 30 + static void spapr_tpm_proxy_reset(void *opaque) 31 + { 32 + SpaprTpmProxy *tpm_proxy = SPAPR_TPM_PROXY(opaque); 33 + 34 + if (tpm_proxy->host_fd != -1) { 35 + close(tpm_proxy->host_fd); 36 + tpm_proxy->host_fd = -1; 37 + } 38 + } 39 + 40 + static ssize_t tpm_execute(SpaprTpmProxy *tpm_proxy, target_ulong *args) 41 + { 42 + uint64_t data_in = ppc64_phys_to_real(args[1]); 43 + target_ulong data_in_size = args[2]; 44 + uint64_t data_out = ppc64_phys_to_real(args[3]); 45 + target_ulong data_out_size = args[4]; 46 + uint8_t buf_in[TPM_SPAPR_BUFSIZE]; 47 + uint8_t buf_out[TPM_SPAPR_BUFSIZE]; 48 + ssize_t ret; 49 + 50 + trace_spapr_tpm_execute(data_in, data_in_size, data_out, data_out_size); 51 + 52 + if (data_in_size > TPM_SPAPR_BUFSIZE) { 53 + error_report("invalid TPM input buffer size: " TARGET_FMT_lu, 54 + data_in_size); 55 + return H_P3; 56 + } 57 + 58 + if (data_out_size < TPM_SPAPR_BUFSIZE) { 59 + error_report("invalid TPM output buffer size: " TARGET_FMT_lu, 60 + data_out_size); 61 + return H_P5; 62 + } 63 + 64 + if (tpm_proxy->host_fd == -1) { 65 + tpm_proxy->host_fd = open(tpm_proxy->host_path, O_RDWR); 66 + if (tpm_proxy->host_fd == -1) { 67 + error_report("failed to open TPM device %s: %d", 68 + tpm_proxy->host_path, errno); 69 + return H_RESOURCE; 70 + } 71 + } 72 + 73 + cpu_physical_memory_read(data_in, buf_in, data_in_size); 74 + 75 + do { 76 + ret = write(tpm_proxy->host_fd, buf_in, data_in_size); 77 + if (ret > 0) { 78 + data_in_size -= ret; 79 + } 80 + } while ((ret >= 0 && data_in_size > 0) || (ret == -1 && errno == EINTR)); 81 + 82 + if (ret == -1) { 83 + error_report("failed to write to TPM device %s: %d", 84 + tpm_proxy->host_path, errno); 85 + return H_RESOURCE; 86 + } 87 + 88 + do { 89 + ret = read(tpm_proxy->host_fd, buf_out, data_out_size); 90 + } while (ret == 0 || (ret == -1 && errno == EINTR)); 91 + 92 + if (ret == -1) { 93 + error_report("failed to read from TPM device %s: %d", 94 + tpm_proxy->host_path, errno); 95 + return H_RESOURCE; 96 + } 97 + 98 + cpu_physical_memory_write(data_out, buf_out, ret); 99 + args[0] = ret; 100 + 101 + return H_SUCCESS; 102 + } 103 + 104 + static target_ulong h_tpm_comm(PowerPCCPU *cpu, 105 + SpaprMachineState *spapr, 106 + target_ulong opcode, 107 + target_ulong *args) 108 + { 109 + target_ulong op = args[0]; 110 + SpaprTpmProxy *tpm_proxy = spapr->tpm_proxy; 111 + 112 + if (!tpm_proxy) { 113 + error_report("TPM proxy not available"); 114 + return H_FUNCTION; 115 + } 116 + 117 + trace_spapr_h_tpm_comm(tpm_proxy->host_path ?: "null", op); 118 + 119 + switch (op) { 120 + case TPM_COMM_OP_EXECUTE: 121 + return tpm_execute(tpm_proxy, args); 122 + case TPM_COMM_OP_CLOSE_SESSION: 123 + spapr_tpm_proxy_reset(tpm_proxy); 124 + return H_SUCCESS; 125 + default: 126 + return H_PARAMETER; 127 + } 128 + } 129 + 130 + static void spapr_tpm_proxy_realize(DeviceState *d, Error **errp) 131 + { 132 + SpaprTpmProxy *tpm_proxy = SPAPR_TPM_PROXY(d); 133 + 134 + if (tpm_proxy->host_path == NULL) { 135 + error_setg(errp, "must specify 'host-path' option for device"); 136 + return; 137 + } 138 + 139 + tpm_proxy->host_fd = -1; 140 + qemu_register_reset(spapr_tpm_proxy_reset, tpm_proxy); 141 + } 142 + 143 + static void spapr_tpm_proxy_unrealize(DeviceState *d, Error **errp) 144 + { 145 + SpaprTpmProxy *tpm_proxy = SPAPR_TPM_PROXY(d); 146 + 147 + qemu_unregister_reset(spapr_tpm_proxy_reset, tpm_proxy); 148 + } 149 + 150 + static Property spapr_tpm_proxy_properties[] = { 151 + DEFINE_PROP_STRING("host-path", SpaprTpmProxy, host_path), 152 + DEFINE_PROP_END_OF_LIST(), 153 + }; 154 + 155 + static void spapr_tpm_proxy_class_init(ObjectClass *k, void *data) 156 + { 157 + DeviceClass *dk = DEVICE_CLASS(k); 158 + 159 + dk->realize = spapr_tpm_proxy_realize; 160 + dk->unrealize = spapr_tpm_proxy_unrealize; 161 + dk->user_creatable = true; 162 + dk->props = spapr_tpm_proxy_properties; 163 + } 164 + 165 + static const TypeInfo spapr_tpm_proxy_info = { 166 + .name = TYPE_SPAPR_TPM_PROXY, 167 + .parent = TYPE_DEVICE, 168 + .instance_size = sizeof(SpaprTpmProxy), 169 + .class_init = spapr_tpm_proxy_class_init, 170 + }; 171 + 172 + static void spapr_tpm_proxy_register_types(void) 173 + { 174 + type_register_static(&spapr_tpm_proxy_info); 175 + spapr_register_hypercall(SVM_H_TPM_COMM, h_tpm_comm); 176 + } 177 + 178 + type_init(spapr_tpm_proxy_register_types)
+4
hw/ppc/trace-events
··· 25 25 spapr_update_dt_failed_size(unsigned cbold, unsigned cbnew, unsigned magic) "Old blob %u bytes, new blob %u bytes, magic 0x%x" 26 26 spapr_update_dt_failed_check(unsigned cbold, unsigned cbnew, unsigned magic) "Old blob %u bytes, new blob %u bytes, magic 0x%x" 27 27 28 + # spapr_hcall_tpm.c 29 + spapr_h_tpm_comm(const char *device_path, uint64_t operation) "tpm_device_path=%s operation=0x%"PRIu64 30 + spapr_tpm_execute(uint64_t data_in, uint64_t data_in_sz, uint64_t data_out, uint64_t data_out_sz) "data_in=0x%"PRIx64", data_in_sz=%"PRIu64", data_out=0x%"PRIx64", data_out_sz=%"PRIu64 31 + 28 32 # spapr_iommu.c 29 33 spapr_iommu_put(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t ret) "liobn=0x%"PRIx64" ioba=0x%"PRIx64" tce=0x%"PRIx64" ret=%"PRId64 30 34 spapr_iommu_get(uint64_t liobn, uint64_t ioba, uint64_t ret, uint64_t tce) "liobn=0x%"PRIx64" ioba=0x%"PRIx64" ret=%"PRId64" tce=0x%"PRIx64
+11
include/hw/ppc/spapr.h
··· 10 10 #include "hw/ppc/spapr_irq.h" 11 11 #include "hw/ppc/spapr_xive.h" /* For SpaprXive */ 12 12 #include "hw/ppc/xics.h" /* For ICSState */ 13 + #include "hw/ppc/spapr_tpm_proxy.h" 13 14 14 15 struct SpaprVioBus; 15 16 struct SpaprPhbState; ··· 203 204 SpaprCapabilities def, eff, mig; 204 205 205 206 unsigned gpu_numa_id; 207 + SpaprTpmProxy *tpm_proxy; 206 208 }; 207 209 208 210 #define H_SUCCESS 0 ··· 507 509 #define KVMPPC_H_CAS (KVMPPC_HCALL_BASE + 0x2) 508 510 #define KVMPPC_H_UPDATE_DT (KVMPPC_HCALL_BASE + 0x3) 509 511 #define KVMPPC_HCALL_MAX KVMPPC_H_UPDATE_DT 512 + 513 + /* 514 + * The hcall range 0xEF00 to 0xEF80 is reserved for use in facilitating 515 + * Secure VM mode via an Ultravisor / Protected Execution Facility 516 + */ 517 + #define SVM_HCALL_BASE 0xEF00 518 + #define SVM_H_TPM_COMM 0xEF10 519 + #define SVM_HCALL_MAX SVM_H_TPM_COMM 520 + 510 521 511 522 typedef struct SpaprDeviceTreeUpdateHeader { 512 523 uint32_t version_id;
+31
include/hw/ppc/spapr_tpm_proxy.h
··· 1 + /* 2 + * SPAPR TPM Proxy/Hypercall 3 + * 4 + * Copyright IBM Corp. 2019 5 + * 6 + * Authors: 7 + * Michael Roth <mdroth@linux.vnet.ibm.com> 8 + * 9 + * This work is licensed under the terms of the GNU GPL, version 2 or later. 10 + * See the COPYING file in the top-level directory. 11 + */ 12 + 13 + #ifndef HW_SPAPR_TPM_PROXY_H 14 + #define HW_SPAPR_TPM_PROXY_H 15 + 16 + #include "qom/object.h" 17 + #include "hw/qdev-core.h" 18 + 19 + #define TYPE_SPAPR_TPM_PROXY "spapr-tpm-proxy" 20 + #define SPAPR_TPM_PROXY(obj) OBJECT_CHECK(SpaprTpmProxy, (obj), \ 21 + TYPE_SPAPR_TPM_PROXY) 22 + 23 + typedef struct SpaprTpmProxy { 24 + /*< private >*/ 25 + DeviceState parent; 26 + 27 + char *host_path; 28 + int host_fd; 29 + } SpaprTpmProxy; 30 + 31 + #endif /* HW_SPAPR_TPM_PROXY_H */