xref: /qemu/hw/ppc/spapr_tpm_proxy.c (revision ca61e750)
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 
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 
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 
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 
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 
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 
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 
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