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
spapr_tpm_proxy_reset(void * opaque)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
tpm_execute(SpaprTpmProxy * tpm_proxy,target_ulong * args)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
h_tpm_comm(PowerPCCPU * cpu,SpaprMachineState * spapr,target_ulong opcode,target_ulong * args)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
spapr_tpm_proxy_realize(DeviceState * d,Error ** errp)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
spapr_tpm_proxy_unrealize(DeviceState * d)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
spapr_tpm_proxy_class_init(ObjectClass * k,void * data)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
spapr_tpm_proxy_register_types(void)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