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, 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) 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 device_class_set_props(dk, 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) 179