]> git.proxmox.com Git - mirror_qemu.git/blame - tpm/tpm.c
Support for TPM command line options
[mirror_qemu.git] / tpm / tpm.c
CommitLineData
d1a0cf73
SB
1/*
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 */
14#include "config-host.h"
15
16#include "monitor/monitor.h"
17#include "qapi/qmp/qerror.h"
18#include "tpm_int.h"
19#include "tpm/tpm.h"
20#include "qemu/config-file.h"
21#include "qmp-commands.h"
22
23static QLIST_HEAD(, TPMBackend) tpm_backends =
24 QLIST_HEAD_INITIALIZER(tpm_backends);
25
26
27#define TPM_MAX_MODELS 1
28#define TPM_MAX_DRIVERS 1
29
30static TPMDriverOps const *be_drivers[TPM_MAX_DRIVERS] = {
31 NULL,
32};
33
34static enum TpmModel tpm_models[TPM_MAX_MODELS] = {
35 -1,
36};
37
38int tpm_register_model(enum TpmModel model)
39{
40 int i;
41
42 for (i = 0; i < TPM_MAX_MODELS; i++) {
43 if (tpm_models[i] == -1) {
44 tpm_models[i] = model;
45 return 0;
46 }
47 }
48 error_report("Could not register TPM model");
49 return 1;
50}
51
52static bool tpm_model_is_registered(enum TpmModel model)
53{
54 int i;
55
56 for (i = 0; i < TPM_MAX_MODELS; i++) {
57 if (tpm_models[i] == model) {
58 return true;
59 }
60 }
61 return false;
62}
63
64const TPMDriverOps *tpm_get_backend_driver(const char *type)
65{
66 int i;
67
68 for (i = 0; i < TPM_MAX_DRIVERS && be_drivers[i] != NULL; i++) {
69 if (!strcmp(TpmType_lookup[be_drivers[i]->type], type)) {
70 return be_drivers[i];
71 }
72 }
73
74 return NULL;
75}
76
77#ifdef CONFIG_TPM
78
79int tpm_register_driver(const TPMDriverOps *tdo)
80{
81 int i;
82
83 for (i = 0; i < TPM_MAX_DRIVERS; i++) {
84 if (!be_drivers[i]) {
85 be_drivers[i] = tdo;
86 return 0;
87 }
88 }
89 error_report("Could not register TPM driver");
90 return 1;
91}
92
93/*
94 * Walk the list of available TPM backend drivers and display them on the
95 * screen.
96 */
97void tpm_display_backend_drivers(void)
98{
99 int i;
100
101 fprintf(stderr, "Supported TPM types (choose only one):\n");
102
103 for (i = 0; i < TPM_MAX_DRIVERS && be_drivers[i] != NULL; i++) {
104 fprintf(stderr, "%12s %s\n",
105 TpmType_lookup[be_drivers[i]->type], be_drivers[i]->desc());
106 }
107 fprintf(stderr, "\n");
108}
109
110/*
111 * Find the TPM with the given Id
112 */
113TPMBackend *qemu_find_tpm(const char *id)
114{
115 TPMBackend *drv;
116
117 if (id) {
118 QLIST_FOREACH(drv, &tpm_backends, list) {
119 if (!strcmp(drv->id, id)) {
120 return drv;
121 }
122 }
123 }
124
125 return NULL;
126}
127
128static int configure_tpm(QemuOpts *opts)
129{
130 const char *value;
131 const char *id;
132 const TPMDriverOps *be;
133 TPMBackend *drv;
134
135 if (!QLIST_EMPTY(&tpm_backends)) {
136 error_report("Only one TPM is allowed.\n");
137 return 1;
138 }
139
140 id = qemu_opts_id(opts);
141 if (id == NULL) {
142 qerror_report(QERR_MISSING_PARAMETER, "id");
143 return 1;
144 }
145
146 value = qemu_opt_get(opts, "type");
147 if (!value) {
148 qerror_report(QERR_MISSING_PARAMETER, "type");
149 tpm_display_backend_drivers();
150 return 1;
151 }
152
153 be = tpm_get_backend_driver(value);
154 if (be == NULL) {
155 qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
156 "a TPM backend type");
157 tpm_display_backend_drivers();
158 return 1;
159 }
160
161 drv = be->create(opts, id);
162 if (!drv) {
163 return 1;
164 }
165
166 QLIST_INSERT_HEAD(&tpm_backends, drv, list);
167
168 return 0;
169}
170
171static int tpm_init_tpmdev(QemuOpts *opts, void *dummy)
172{
173 return configure_tpm(opts);
174}
175
176/*
177 * Walk the list of TPM backend drivers that are in use and call their
178 * destroy function to have them cleaned up.
179 */
180void tpm_cleanup(void)
181{
182 TPMBackend *drv, *next;
183
184 QLIST_FOREACH_SAFE(drv, &tpm_backends, list, next) {
185 QLIST_REMOVE(drv, list);
186 drv->ops->destroy(drv);
187 }
188}
189
190/*
191 * Initialize the TPM. Process the tpmdev command line options describing the
192 * TPM backend.
193 */
194int tpm_init(void)
195{
196 if (qemu_opts_foreach(qemu_find_opts("tpmdev"),
197 tpm_init_tpmdev, NULL, 1) != 0) {
198 return -1;
199 }
200
201 atexit(tpm_cleanup);
202
203 return 0;
204}
205
206/*
207 * Parse the TPM configuration options.
208 * To display all available TPM backends the user may use '-tpmdev help'
209 */
210int tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
211{
212 QemuOpts *opts;
213
214 if (!strcmp(optarg, "help")) {
215 tpm_display_backend_drivers();
216 return -1;
217 }
218 opts = qemu_opts_parse(opts_list, optarg, 1);
219 if (!opts) {
220 return -1;
221 }
222 return 0;
223}
224
225#endif /* CONFIG_TPM */
226
227static const TPMDriverOps *tpm_driver_find_by_type(enum TpmType type)
228{
229 int i;
230
231 for (i = 0; i < TPM_MAX_DRIVERS && be_drivers[i] != NULL; i++) {
232 if (be_drivers[i]->type == type) {
233 return be_drivers[i];
234 }
235 }
236 return NULL;
237}
238
239static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
240{
241 TPMInfo *res = g_new0(TPMInfo, 1);
242 TPMPassthroughOptions *tpo;
243
244 res->id = g_strdup(drv->id);
245 res->model = drv->fe_model;
246 res->type = drv->ops->type;
247 res->tpm_options = g_new0(TpmTypeOptions, 1);
248
249 switch (res->type) {
250 case TPM_TYPE_PASSTHROUGH:
251 res->tpm_options->kind = TPM_TYPE_OPTIONS_KIND_TPM_PASSTHROUGH_OPTIONS;
252 tpo = g_new0(TPMPassthroughOptions, 1);
253 res->tpm_options->tpm_passthrough_options = tpo;
254 if (drv->path) {
255 tpo->path = g_strdup(drv->path);
256 tpo->has_path = true;
257 }
258 if (drv->cancel_path) {
259 tpo->cancel_path = g_strdup(drv->cancel_path);
260 tpo->has_cancel_path = true;
261 }
262 break;
263 case TPM_TYPE_MAX:
264 break;
265 }
266
267 return res;
268}
269
270/*
271 * Walk the list of active TPM backends and collect information about them
272 * following the schema description in qapi-schema.json.
273 */
274TPMInfoList *qmp_query_tpm(Error **errp)
275{
276 TPMBackend *drv;
277 TPMInfoList *info, *head = NULL, *cur_item = NULL;
278
279 QLIST_FOREACH(drv, &tpm_backends, list) {
280 if (!tpm_model_is_registered(drv->fe_model)) {
281 continue;
282 }
283 info = g_new0(TPMInfoList, 1);
284 info->value = qmp_query_tpm_inst(drv);
285
286 if (!cur_item) {
287 head = cur_item = info;
288 } else {
289 cur_item->next = info;
290 cur_item = info;
291 }
292 }
293
294 return head;
295}
296
297TpmTypeList *qmp_query_tpm_types(Error **errp)
298{
299 unsigned int i = 0;
300 TpmTypeList *head = NULL, *prev = NULL, *cur_item;
301
302 for (i = 0; i < TPM_TYPE_MAX; i++) {
303 if (!tpm_driver_find_by_type(i)) {
304 continue;
305 }
306 cur_item = g_new0(TpmTypeList, 1);
307 cur_item->value = i;
308
309 if (prev) {
310 prev->next = cur_item;
311 }
312 if (!head) {
313 head = cur_item;
314 }
315 prev = cur_item;
316 }
317
318 return head;
319}
320
321TpmModelList *qmp_query_tpm_models(Error **errp)
322{
323 unsigned int i = 0;
324 TpmModelList *head = NULL, *prev = NULL, *cur_item;
325
326 for (i = 0; i < TPM_MODEL_MAX; i++) {
327 if (!tpm_model_is_registered(i)) {
328 continue;
329 }
330 cur_item = g_new0(TpmModelList, 1);
331 cur_item->value = i;
332
333 if (prev) {
334 prev->next = cur_item;
335 }
336 if (!head) {
337 head = cur_item;
338 }
339 prev = cur_item;
340 }
341
342 return head;
343}