xref: /openbmc/qemu/hw/ppc/spapr_tpm_proxy.c (revision 6016b7b4)
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 "hw/ppc/spapr.h"
19 #include "hw/qdev-properties.h"
20 #include "trace.h"
21 
22 #define TPM_SPAPR_BUFSIZE 4096
23 
24 enum {
25     TPM_COMM_OP_EXECUTE = 1,
26     TPM_COMM_OP_CLOSE_SESSION = 2,
27 };
28 
29 static 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 
39 static 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 
103 static 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 
129 static 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 
142 static 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 
149 static Property spapr_tpm_proxy_properties[] = {
150     DEFINE_PROP_STRING("host-path", SpaprTpmProxy, host_path),
151     DEFINE_PROP_END_OF_LIST(),
152 };
153 
154 static 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 
164 static 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 
171 static 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 
177 type_init(spapr_tpm_proxy_register_types)
178