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