spapr_tpm_proxy.c (4828B)
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 24enum { 25 TPM_COMM_OP_EXECUTE = 1, 26 TPM_COMM_OP_CLOSE_SESSION = 2, 27}; 28 29static 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 39static 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 103static 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 129static 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 142static 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 149static Property spapr_tpm_proxy_properties[] = { 150 DEFINE_PROP_STRING("host-path", SpaprTpmProxy, host_path), 151 DEFINE_PROP_END_OF_LIST(), 152}; 153 154static 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 164static 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 171static 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 177type_init(spapr_tpm_proxy_register_types)