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 "hw/ppc/spapr.h" 19 #include "hw/qdev-properties.h" 20 #include "trace.h" 21 22 #define TPM_SPAPR_BUFSIZE 4096 23 24 enum { 25 TPM_COMM_OP_EXECUTE = 1, 26 TPM_COMM_OP_CLOSE_SESSION = 2, 27 }; 28 29 static void spapr_tpm_proxy_reset(void *opaque) 30 { 31 SpaprTpmProxy *tpm_proxy = SPAPR_TPM_PROXY(opaque); 32 33 if (tpm_proxy->host_fd != -1) { 34 close(tpm_proxy->host_fd); 35 tpm_proxy->host_fd = -1; 36 } 37 } 38 39 static ssize_t tpm_execute(SpaprTpmProxy *tpm_proxy, target_ulong *args) 40 { 41 uint64_t data_in = ppc64_phys_to_real(args[1]); 42 target_ulong data_in_size = args[2]; 43 uint64_t data_out = ppc64_phys_to_real(args[3]); 44 target_ulong data_out_size = args[4]; 45 uint8_t buf_in[TPM_SPAPR_BUFSIZE]; 46 uint8_t buf_out[TPM_SPAPR_BUFSIZE]; 47 ssize_t ret; 48 49 trace_spapr_tpm_execute(data_in, data_in_size, data_out, data_out_size); 50 51 if (data_in_size > TPM_SPAPR_BUFSIZE) { 52 error_report("invalid TPM input buffer size: " TARGET_FMT_lu, 53 data_in_size); 54 return H_P3; 55 } 56 57 if (data_out_size < TPM_SPAPR_BUFSIZE) { 58 error_report("invalid TPM output buffer size: " TARGET_FMT_lu, 59 data_out_size); 60 return H_P5; 61 } 62 63 if (tpm_proxy->host_fd == -1) { 64 tpm_proxy->host_fd = open(tpm_proxy->host_path, O_RDWR); 65 if (tpm_proxy->host_fd == -1) { 66 error_report("failed to open TPM device %s: %d", 67 tpm_proxy->host_path, errno); 68 return H_RESOURCE; 69 } 70 } 71 72 cpu_physical_memory_read(data_in, buf_in, data_in_size); 73 74 do { 75 ret = write(tpm_proxy->host_fd, buf_in, data_in_size); 76 if (ret > 0) { 77 data_in_size -= ret; 78 } 79 } while ((ret >= 0 && data_in_size > 0) || (ret == -1 && errno == EINTR)); 80 81 if (ret == -1) { 82 error_report("failed to write to TPM device %s: %d", 83 tpm_proxy->host_path, errno); 84 return H_RESOURCE; 85 } 86 87 do { 88 ret = read(tpm_proxy->host_fd, buf_out, data_out_size); 89 } while (ret == 0 || (ret == -1 && errno == EINTR)); 90 91 if (ret == -1) { 92 error_report("failed to read from TPM device %s: %d", 93 tpm_proxy->host_path, errno); 94 return H_RESOURCE; 95 } 96 97 cpu_physical_memory_write(data_out, buf_out, ret); 98 args[0] = ret; 99 100 return H_SUCCESS; 101 } 102 103 static target_ulong h_tpm_comm(PowerPCCPU *cpu, 104 SpaprMachineState *spapr, 105 target_ulong opcode, 106 target_ulong *args) 107 { 108 target_ulong op = args[0]; 109 SpaprTpmProxy *tpm_proxy = spapr->tpm_proxy; 110 111 if (!tpm_proxy) { 112 error_report("TPM proxy not available"); 113 return H_FUNCTION; 114 } 115 116 trace_spapr_h_tpm_comm(tpm_proxy->host_path, op); 117 118 switch (op) { 119 case TPM_COMM_OP_EXECUTE: 120 return tpm_execute(tpm_proxy, args); 121 case TPM_COMM_OP_CLOSE_SESSION: 122 spapr_tpm_proxy_reset(tpm_proxy); 123 return H_SUCCESS; 124 default: 125 return H_PARAMETER; 126 } 127 } 128 129 static void spapr_tpm_proxy_realize(DeviceState *d, Error **errp) 130 { 131 SpaprTpmProxy *tpm_proxy = SPAPR_TPM_PROXY(d); 132 133 if (tpm_proxy->host_path == NULL) { 134 error_setg(errp, "must specify 'host-path' option for device"); 135 return; 136 } 137 138 tpm_proxy->host_fd = -1; 139 qemu_register_reset(spapr_tpm_proxy_reset, tpm_proxy); 140 } 141 142 static void spapr_tpm_proxy_unrealize(DeviceState *d) 143 { 144 SpaprTpmProxy *tpm_proxy = SPAPR_TPM_PROXY(d); 145 146 qemu_unregister_reset(spapr_tpm_proxy_reset, tpm_proxy); 147 } 148 149 static Property spapr_tpm_proxy_properties[] = { 150 DEFINE_PROP_STRING("host-path", SpaprTpmProxy, host_path), 151 DEFINE_PROP_END_OF_LIST(), 152 }; 153 154 static void spapr_tpm_proxy_class_init(ObjectClass *k, void *data) 155 { 156 DeviceClass *dk = DEVICE_CLASS(k); 157 158 dk->realize = spapr_tpm_proxy_realize; 159 dk->unrealize = spapr_tpm_proxy_unrealize; 160 dk->user_creatable = true; 161 device_class_set_props(dk, spapr_tpm_proxy_properties); 162 } 163 164 static const TypeInfo spapr_tpm_proxy_info = { 165 .name = TYPE_SPAPR_TPM_PROXY, 166 .parent = TYPE_DEVICE, 167 .instance_size = sizeof(SpaprTpmProxy), 168 .class_init = spapr_tpm_proxy_class_init, 169 }; 170 171 static void spapr_tpm_proxy_register_types(void) 172 { 173 type_register_static(&spapr_tpm_proxy_info); 174 spapr_register_hypercall(SVM_H_TPM_COMM, h_tpm_comm); 175 } 176 177 type_init(spapr_tpm_proxy_register_types) 178