1
2
3
4
5
6
7
8
9
10
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)
178