blob: 6f39ec9192e8050f4f28899e452590add0f05ade [file] [log] [blame]
Stefan Bergerd1a0cf72013-02-27 12:47:49 -05001/*
2 * TPM configuration
3 *
4 * Copyright (C) 2011-2013 IBM Corporation
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 net.c
13 */
Peter Maydelld38ea872016-01-29 17:50:05 +000014#include "qemu/osdep.h"
Stefan Bergerd1a0cf72013-02-27 12:47:49 -050015
Stefan Bergerd1a0cf72013-02-27 12:47:49 -050016#include "qapi/qmp/qerror.h"
Marc-André Lureaua9a72ae2017-08-24 10:45:58 +020017#include "qapi/util.h"
Paolo Bonzinidccfcd02013-04-08 16:55:25 +020018#include "sysemu/tpm_backend.h"
Paolo Bonzinibdee56f2013-04-02 18:28:41 +020019#include "sysemu/tpm.h"
Stefan Bergerd1a0cf72013-02-27 12:47:49 -050020#include "qemu/config-file.h"
Markus Armbrusterd49b6832015-03-17 18:29:20 +010021#include "qemu/error-report.h"
Stefan Bergerd1a0cf72013-02-27 12:47:49 -050022#include "qmp-commands.h"
23
24static QLIST_HEAD(, TPMBackend) tpm_backends =
25 QLIST_HEAD_INITIALIZER(tpm_backends);
26
27
28#define TPM_MAX_MODELS 1
Stefan Bergerd1a0cf72013-02-27 12:47:49 -050029
Marc-André Lureaua9a72ae2017-08-24 10:45:58 +020030static TPMDriverOps const *be_drivers[TPM_TYPE__MAX];
Stefan Bergerd1a0cf72013-02-27 12:47:49 -050031
32static enum TpmModel tpm_models[TPM_MAX_MODELS] = {
Eric Blake7fb1cf12015-11-18 01:52:57 -070033 TPM_MODEL__MAX,
Stefan Bergerd1a0cf72013-02-27 12:47:49 -050034};
35
36int tpm_register_model(enum TpmModel model)
37{
38 int i;
39
40 for (i = 0; i < TPM_MAX_MODELS; i++) {
Eric Blake7fb1cf12015-11-18 01:52:57 -070041 if (tpm_models[i] == TPM_MODEL__MAX) {
Stefan Bergerd1a0cf72013-02-27 12:47:49 -050042 tpm_models[i] = model;
43 return 0;
44 }
45 }
46 error_report("Could not register TPM model");
47 return 1;
48}
49
50static bool tpm_model_is_registered(enum TpmModel model)
51{
52 int i;
53
54 for (i = 0; i < TPM_MAX_MODELS; i++) {
55 if (tpm_models[i] == model) {
56 return true;
57 }
58 }
59 return false;
60}
61
Stefan Bergerd1a0cf72013-02-27 12:47:49 -050062const TPMDriverOps *tpm_get_backend_driver(const char *type)
63{
Marc-André Lureaua9a72ae2017-08-24 10:45:58 +020064 int i = qapi_enum_parse(TpmType_lookup, type, -1, NULL);
Stefan Bergerd1a0cf72013-02-27 12:47:49 -050065
Marc-André Lureaua9a72ae2017-08-24 10:45:58 +020066 return i >= 0 ? be_drivers[i] : NULL;
Stefan Bergerd1a0cf72013-02-27 12:47:49 -050067}
68
69#ifdef CONFIG_TPM
70
Marc-André Lureaua9a72ae2017-08-24 10:45:58 +020071void tpm_register_driver(const TPMDriverOps *tdo)
Stefan Bergerd1a0cf72013-02-27 12:47:49 -050072{
Marc-André Lureaua9a72ae2017-08-24 10:45:58 +020073 assert(!be_drivers[tdo->type]);
Stefan Bergerd1a0cf72013-02-27 12:47:49 -050074
Marc-André Lureaua9a72ae2017-08-24 10:45:58 +020075 be_drivers[tdo->type] = tdo;
Stefan Bergerd1a0cf72013-02-27 12:47:49 -050076}
77
78/*
79 * Walk the list of available TPM backend drivers and display them on the
80 * screen.
81 */
Paolo Bonzinibdee56f2013-04-02 18:28:41 +020082static void tpm_display_backend_drivers(void)
Stefan Bergerd1a0cf72013-02-27 12:47:49 -050083{
84 int i;
85
86 fprintf(stderr, "Supported TPM types (choose only one):\n");
87
Marc-André Lureaua9a72ae2017-08-24 10:45:58 +020088 for (i = 0; i < TPM_TYPE__MAX; i++) {
89 if (be_drivers[i] == NULL) {
90 continue;
91 }
Stefan Bergerd1a0cf72013-02-27 12:47:49 -050092 fprintf(stderr, "%12s %s\n",
Marc-André Lureaua9a72ae2017-08-24 10:45:58 +020093 TpmType_lookup[i], be_drivers[i]->desc());
Stefan Bergerd1a0cf72013-02-27 12:47:49 -050094 }
95 fprintf(stderr, "\n");
96}
97
98/*
99 * Find the TPM with the given Id
100 */
101TPMBackend *qemu_find_tpm(const char *id)
102{
103 TPMBackend *drv;
104
105 if (id) {
106 QLIST_FOREACH(drv, &tpm_backends, list) {
107 if (!strcmp(drv->id, id)) {
108 return drv;
109 }
110 }
111 }
112
113 return NULL;
114}
115
116static int configure_tpm(QemuOpts *opts)
117{
118 const char *value;
119 const char *id;
120 const TPMDriverOps *be;
121 TPMBackend *drv;
Stefan Berger8f0605c2013-03-28 07:26:21 -0400122 Error *local_err = NULL;
Stefan Bergerd1a0cf72013-02-27 12:47:49 -0500123
124 if (!QLIST_EMPTY(&tpm_backends)) {
Gonglei27215a22015-02-25 12:22:35 +0800125 error_report("Only one TPM is allowed.");
Stefan Bergerd1a0cf72013-02-27 12:47:49 -0500126 return 1;
127 }
128
129 id = qemu_opts_id(opts);
130 if (id == NULL) {
Markus Armbruster8b53a192015-03-17 12:09:02 +0100131 error_report(QERR_MISSING_PARAMETER, "id");
Stefan Bergerd1a0cf72013-02-27 12:47:49 -0500132 return 1;
133 }
134
135 value = qemu_opt_get(opts, "type");
136 if (!value) {
Markus Armbruster8b53a192015-03-17 12:09:02 +0100137 error_report(QERR_MISSING_PARAMETER, "type");
Stefan Bergerd1a0cf72013-02-27 12:47:49 -0500138 tpm_display_backend_drivers();
139 return 1;
140 }
141
142 be = tpm_get_backend_driver(value);
143 if (be == NULL) {
Markus Armbruster8b53a192015-03-17 12:09:02 +0100144 error_report(QERR_INVALID_PARAMETER_VALUE,
145 "type", "a TPM backend type");
Stefan Bergerd1a0cf72013-02-27 12:47:49 -0500146 tpm_display_backend_drivers();
147 return 1;
148 }
149
Stefan Bergerbb716232013-04-22 10:41:39 -0400150 /* validate backend specific opts */
151 qemu_opts_validate(opts, be->opts, &local_err);
Markus Armbruster84d18f02014-01-30 15:07:28 +0100152 if (local_err) {
Markus Armbrusterbc09a282015-02-10 15:11:17 +0100153 error_report_err(local_err);
Stefan Bergerbb716232013-04-22 10:41:39 -0400154 return 1;
155 }
156
Stefan Bergerd1a0cf72013-02-27 12:47:49 -0500157 drv = be->create(opts, id);
158 if (!drv) {
159 return 1;
160 }
161
Stefan Berger8f0605c2013-03-28 07:26:21 -0400162 tpm_backend_open(drv, &local_err);
163 if (local_err) {
Markus Armbrusterbc09a282015-02-10 15:11:17 +0100164 error_report_err(local_err);
Stefan Berger8f0605c2013-03-28 07:26:21 -0400165 return 1;
166 }
167
Stefan Bergerd1a0cf72013-02-27 12:47:49 -0500168 QLIST_INSERT_HEAD(&tpm_backends, drv, list);
169
170 return 0;
171}
172
Markus Armbruster28d0de72015-03-13 13:35:14 +0100173static int tpm_init_tpmdev(void *dummy, QemuOpts *opts, Error **errp)
Stefan Bergerd1a0cf72013-02-27 12:47:49 -0500174{
175 return configure_tpm(opts);
176}
177
178/*
179 * Walk the list of TPM backend drivers that are in use and call their
180 * destroy function to have them cleaned up.
181 */
182void tpm_cleanup(void)
183{
184 TPMBackend *drv, *next;
185
186 QLIST_FOREACH_SAFE(drv, &tpm_backends, list, next) {
187 QLIST_REMOVE(drv, list);
Stefan Berger8f0605c2013-03-28 07:26:21 -0400188 tpm_backend_destroy(drv);
Stefan Bergerd1a0cf72013-02-27 12:47:49 -0500189 }
190}
191
192/*
193 * Initialize the TPM. Process the tpmdev command line options describing the
194 * TPM backend.
195 */
196int tpm_init(void)
197{
Markus Armbruster28d0de72015-03-13 13:35:14 +0100198 if (qemu_opts_foreach(qemu_find_opts("tpmdev"),
199 tpm_init_tpmdev, NULL, NULL)) {
Stefan Bergerd1a0cf72013-02-27 12:47:49 -0500200 return -1;
201 }
202
203 atexit(tpm_cleanup);
Stefan Bergerd1a0cf72013-02-27 12:47:49 -0500204 return 0;
205}
206
207/*
208 * Parse the TPM configuration options.
209 * To display all available TPM backends the user may use '-tpmdev help'
210 */
211int tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
212{
213 QemuOpts *opts;
214
215 if (!strcmp(optarg, "help")) {
216 tpm_display_backend_drivers();
217 return -1;
218 }
Markus Armbruster70b94332015-02-13 12:50:26 +0100219 opts = qemu_opts_parse_noisily(opts_list, optarg, true);
Stefan Bergerd1a0cf72013-02-27 12:47:49 -0500220 if (!opts) {
221 return -1;
222 }
223 return 0;
224}
225
226#endif /* CONFIG_TPM */
227
228static const TPMDriverOps *tpm_driver_find_by_type(enum TpmType type)
229{
Marc-André Lureaua9a72ae2017-08-24 10:45:58 +0200230 return be_drivers[type];
Stefan Bergerd1a0cf72013-02-27 12:47:49 -0500231}
232
233static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
234{
235 TPMInfo *res = g_new0(TPMInfo, 1);
236 TPMPassthroughOptions *tpo;
237
238 res->id = g_strdup(drv->id);
239 res->model = drv->fe_model;
Corey Bryant88ca7bc2013-03-20 12:34:48 -0400240 res->options = g_new0(TpmTypeOptions, 1);
Stefan Bergerd1a0cf72013-02-27 12:47:49 -0500241
Corey Bryant88ca7bc2013-03-20 12:34:48 -0400242 switch (drv->ops->type) {
Stefan Bergerd1a0cf72013-02-27 12:47:49 -0500243 case TPM_TYPE_PASSTHROUGH:
Eric Blakece211312015-10-26 16:35:00 -0600244 res->options->type = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH;
Stefan Bergerd1a0cf72013-02-27 12:47:49 -0500245 tpo = g_new0(TPMPassthroughOptions, 1);
Eric Blake32bafa82016-03-17 16:48:37 -0600246 res->options->u.passthrough.data = tpo;
Stefan Bergerd1a0cf72013-02-27 12:47:49 -0500247 if (drv->path) {
248 tpo->path = g_strdup(drv->path);
249 tpo->has_path = true;
250 }
251 if (drv->cancel_path) {
252 tpo->cancel_path = g_strdup(drv->cancel_path);
253 tpo->has_cancel_path = true;
254 }
255 break;
Eric Blake7fb1cf12015-11-18 01:52:57 -0700256 case TPM_TYPE__MAX:
Stefan Bergerd1a0cf72013-02-27 12:47:49 -0500257 break;
258 }
259
260 return res;
261}
262
263/*
264 * Walk the list of active TPM backends and collect information about them
265 * following the schema description in qapi-schema.json.
266 */
267TPMInfoList *qmp_query_tpm(Error **errp)
268{
269 TPMBackend *drv;
270 TPMInfoList *info, *head = NULL, *cur_item = NULL;
271
272 QLIST_FOREACH(drv, &tpm_backends, list) {
273 if (!tpm_model_is_registered(drv->fe_model)) {
274 continue;
275 }
276 info = g_new0(TPMInfoList, 1);
277 info->value = qmp_query_tpm_inst(drv);
278
279 if (!cur_item) {
280 head = cur_item = info;
281 } else {
282 cur_item->next = info;
283 cur_item = info;
284 }
285 }
286
287 return head;
288}
289
290TpmTypeList *qmp_query_tpm_types(Error **errp)
291{
292 unsigned int i = 0;
293 TpmTypeList *head = NULL, *prev = NULL, *cur_item;
294
Eric Blake7fb1cf12015-11-18 01:52:57 -0700295 for (i = 0; i < TPM_TYPE__MAX; i++) {
Stefan Bergerd1a0cf72013-02-27 12:47:49 -0500296 if (!tpm_driver_find_by_type(i)) {
297 continue;
298 }
299 cur_item = g_new0(TpmTypeList, 1);
300 cur_item->value = i;
301
302 if (prev) {
303 prev->next = cur_item;
304 }
305 if (!head) {
306 head = cur_item;
307 }
308 prev = cur_item;
309 }
310
311 return head;
312}
313
314TpmModelList *qmp_query_tpm_models(Error **errp)
315{
316 unsigned int i = 0;
317 TpmModelList *head = NULL, *prev = NULL, *cur_item;
318
Eric Blake7fb1cf12015-11-18 01:52:57 -0700319 for (i = 0; i < TPM_MODEL__MAX; i++) {
Stefan Bergerd1a0cf72013-02-27 12:47:49 -0500320 if (!tpm_model_is_registered(i)) {
321 continue;
322 }
323 cur_item = g_new0(TpmModelList, 1);
324 cur_item->value = i;
325
326 if (prev) {
327 prev->next = cur_item;
328 }
329 if (!head) {
330 head = cur_item;
331 }
332 prev = cur_item;
333 }
334
335 return head;
336}