xref: /qemu/hw/ppc/spapr_tpm_proxy.c (revision 27a4a30e)
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 
25 enum {
26     TPM_COMM_OP_EXECUTE = 1,
27     TPM_COMM_OP_CLOSE_SESSION = 2,
28 };
29 
30 static 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 
40 static 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 
104 static 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, 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 
130 static 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 
143 static 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 
150 static Property spapr_tpm_proxy_properties[] = {
151     DEFINE_PROP_STRING("host-path", SpaprTpmProxy, host_path),
152     DEFINE_PROP_END_OF_LIST(),
153 };
154 
155 static 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     device_class_set_props(dk, spapr_tpm_proxy_properties);
163 }
164 
165 static 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 
172 static 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 
178 type_init(spapr_tpm_proxy_register_types)
179