1 | /* |
2 | * QEMU TPM Backend |
3 | * |
4 | * Copyright IBM, Corp. 2013 |
5 | * |
6 | * Authors: |
7 | * Stefan Berger <stefanb@us.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 | * Based on backends/rng.c by Anthony Liguori |
13 | */ |
14 | |
15 | #include "qemu/osdep.h" |
16 | #include "sysemu/tpm_backend.h" |
17 | #include "qapi/error.h" |
18 | #include "sysemu/tpm.h" |
19 | #include "qemu/thread.h" |
20 | #include "qemu/main-loop.h" |
21 | #include "qemu/module.h" |
22 | #include "block/thread-pool.h" |
23 | #include "qemu/error-report.h" |
24 | |
25 | static void tpm_backend_request_completed(void *opaque, int ret) |
26 | { |
27 | TPMBackend *s = TPM_BACKEND(opaque); |
28 | TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif); |
29 | |
30 | tic->request_completed(s->tpmif, ret); |
31 | |
32 | /* no need for atomic, as long the BQL is taken */ |
33 | s->cmd = NULL; |
34 | object_unref(OBJECT(s)); |
35 | } |
36 | |
37 | static int tpm_backend_worker_thread(gpointer data) |
38 | { |
39 | TPMBackend *s = TPM_BACKEND(data); |
40 | TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); |
41 | Error *err = NULL; |
42 | |
43 | k->handle_request(s, s->cmd, &err); |
44 | if (err) { |
45 | error_report_err(err); |
46 | return -1; |
47 | } |
48 | |
49 | return 0; |
50 | } |
51 | |
52 | void tpm_backend_finish_sync(TPMBackend *s) |
53 | { |
54 | while (s->cmd) { |
55 | aio_poll(qemu_get_aio_context(), true); |
56 | } |
57 | } |
58 | |
59 | enum TpmType tpm_backend_get_type(TPMBackend *s) |
60 | { |
61 | TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); |
62 | |
63 | return k->type; |
64 | } |
65 | |
66 | int tpm_backend_init(TPMBackend *s, TPMIf *tpmif, Error **errp) |
67 | { |
68 | if (s->tpmif) { |
69 | error_setg(errp, "TPM backend '%s' is already initialized" , s->id); |
70 | return -1; |
71 | } |
72 | |
73 | s->tpmif = tpmif; |
74 | object_ref(OBJECT(tpmif)); |
75 | |
76 | s->had_startup_error = false; |
77 | |
78 | return 0; |
79 | } |
80 | |
81 | int tpm_backend_startup_tpm(TPMBackend *s, size_t buffersize) |
82 | { |
83 | int res = 0; |
84 | TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); |
85 | |
86 | /* terminate a running TPM */ |
87 | tpm_backend_finish_sync(s); |
88 | |
89 | res = k->startup_tpm ? k->startup_tpm(s, buffersize) : 0; |
90 | |
91 | s->had_startup_error = (res != 0); |
92 | |
93 | return res; |
94 | } |
95 | |
96 | bool tpm_backend_had_startup_error(TPMBackend *s) |
97 | { |
98 | return s->had_startup_error; |
99 | } |
100 | |
101 | void tpm_backend_deliver_request(TPMBackend *s, TPMBackendCmd *cmd) |
102 | { |
103 | ThreadPool *pool = aio_get_thread_pool(qemu_get_aio_context()); |
104 | |
105 | if (s->cmd != NULL) { |
106 | error_report("There is a TPM request pending" ); |
107 | return; |
108 | } |
109 | |
110 | s->cmd = cmd; |
111 | object_ref(OBJECT(s)); |
112 | thread_pool_submit_aio(pool, tpm_backend_worker_thread, s, |
113 | tpm_backend_request_completed, s); |
114 | } |
115 | |
116 | void tpm_backend_reset(TPMBackend *s) |
117 | { |
118 | TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); |
119 | |
120 | if (k->reset) { |
121 | k->reset(s); |
122 | } |
123 | |
124 | tpm_backend_finish_sync(s); |
125 | |
126 | s->had_startup_error = false; |
127 | } |
128 | |
129 | void tpm_backend_cancel_cmd(TPMBackend *s) |
130 | { |
131 | TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); |
132 | |
133 | k->cancel_cmd(s); |
134 | } |
135 | |
136 | bool tpm_backend_get_tpm_established_flag(TPMBackend *s) |
137 | { |
138 | TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); |
139 | |
140 | return k->get_tpm_established_flag ? |
141 | k->get_tpm_established_flag(s) : false; |
142 | } |
143 | |
144 | int tpm_backend_reset_tpm_established_flag(TPMBackend *s, uint8_t locty) |
145 | { |
146 | TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); |
147 | |
148 | return k->reset_tpm_established_flag ? |
149 | k->reset_tpm_established_flag(s, locty) : 0; |
150 | } |
151 | |
152 | TPMVersion tpm_backend_get_tpm_version(TPMBackend *s) |
153 | { |
154 | TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); |
155 | |
156 | return k->get_tpm_version(s); |
157 | } |
158 | |
159 | size_t tpm_backend_get_buffer_size(TPMBackend *s) |
160 | { |
161 | TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); |
162 | |
163 | return k->get_buffer_size(s); |
164 | } |
165 | |
166 | TPMInfo *tpm_backend_query_tpm(TPMBackend *s) |
167 | { |
168 | TPMInfo *info = g_new0(TPMInfo, 1); |
169 | TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); |
170 | TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif); |
171 | |
172 | info->id = g_strdup(s->id); |
173 | info->model = tic->model; |
174 | info->options = k->get_tpm_options(s); |
175 | |
176 | return info; |
177 | } |
178 | |
179 | static void tpm_backend_instance_finalize(Object *obj) |
180 | { |
181 | TPMBackend *s = TPM_BACKEND(obj); |
182 | |
183 | object_unref(OBJECT(s->tpmif)); |
184 | g_free(s->id); |
185 | } |
186 | |
187 | static const TypeInfo tpm_backend_info = { |
188 | .name = TYPE_TPM_BACKEND, |
189 | .parent = TYPE_OBJECT, |
190 | .instance_size = sizeof(TPMBackend), |
191 | .instance_finalize = tpm_backend_instance_finalize, |
192 | .class_size = sizeof(TPMBackendClass), |
193 | .abstract = true, |
194 | }; |
195 | |
196 | static const TypeInfo tpm_if_info = { |
197 | .name = TYPE_TPM_IF, |
198 | .parent = TYPE_INTERFACE, |
199 | .class_size = sizeof(TPMIfClass), |
200 | }; |
201 | |
202 | static void register_types(void) |
203 | { |
204 | type_register_static(&tpm_backend_info); |
205 | type_register_static(&tpm_if_info); |
206 | } |
207 | |
208 | type_init(register_types); |
209 | |