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
25enum {
26 TPM_COMM_OP_EXECUTE = 1,
27 TPM_COMM_OP_CLOSE_SESSION = 2,
28};
29
30static 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
40static 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
104static 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 ?: "null", 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
130static 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
143static void spapr_tpm_proxy_unrealize(DeviceState *d, Error **errp)
144{
145 SpaprTpmProxy *tpm_proxy = SPAPR_TPM_PROXY(d);
146
147 qemu_unregister_reset(spapr_tpm_proxy_reset, tpm_proxy);
148}
149
150static Property spapr_tpm_proxy_properties[] = {
151 DEFINE_PROP_STRING("host-path", SpaprTpmProxy, host_path),
152 DEFINE_PROP_END_OF_LIST(),
153};
154
155static 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 dk->props = spapr_tpm_proxy_properties;
163}
164
165static 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
172static 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
178type_init(spapr_tpm_proxy_register_types)
179