Statistics
| Branch: | Revision:

root / tpm.c @ feature-archipelago

History | View | Annotate | Download (7.6 kB)

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 "sysemu/tpm_backend.h"
19
#include "sysemu/tpm.h"
20
#include "qemu/config-file.h"
21
#include "qmp-commands.h"
22

    
23
static 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

    
30
static TPMDriverOps const *be_drivers[TPM_MAX_DRIVERS] = {
31
    NULL,
32
};
33

    
34
static enum TpmModel tpm_models[TPM_MAX_MODELS] = {
35
    TPM_MODEL_MAX,
36
};
37

    
38
int 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] == TPM_MODEL_MAX) {
44
            tpm_models[i] = model;
45
            return 0;
46
        }
47
    }
48
    error_report("Could not register TPM model");
49
    return 1;
50
}
51

    
52
static 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

    
64
const 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

    
79
int 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
 */
97
static void 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
 */
113
TPMBackend *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

    
128
static int configure_tpm(QemuOpts *opts)
129
{
130
    const char *value;
131
    const char *id;
132
    const TPMDriverOps *be;
133
    TPMBackend *drv;
134
    Error *local_err = NULL;
135

    
136
    if (!QLIST_EMPTY(&tpm_backends)) {
137
        error_report("Only one TPM is allowed.\n");
138
        return 1;
139
    }
140

    
141
    id = qemu_opts_id(opts);
142
    if (id == NULL) {
143
        qerror_report(QERR_MISSING_PARAMETER, "id");
144
        return 1;
145
    }
146

    
147
    value = qemu_opt_get(opts, "type");
148
    if (!value) {
149
        qerror_report(QERR_MISSING_PARAMETER, "type");
150
        tpm_display_backend_drivers();
151
        return 1;
152
    }
153

    
154
    be = tpm_get_backend_driver(value);
155
    if (be == NULL) {
156
        qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
157
                      "a TPM backend type");
158
        tpm_display_backend_drivers();
159
        return 1;
160
    }
161

    
162
    /* validate backend specific opts */
163
    qemu_opts_validate(opts, be->opts, &local_err);
164
    if (local_err) {
165
        qerror_report_err(local_err);
166
        error_free(local_err);
167
        return 1;
168
    }
169

    
170
    drv = be->create(opts, id);
171
    if (!drv) {
172
        return 1;
173
    }
174

    
175
    tpm_backend_open(drv, &local_err);
176
    if (local_err) {
177
        qerror_report_err(local_err);
178
        error_free(local_err);
179
        return 1;
180
    }
181

    
182
    QLIST_INSERT_HEAD(&tpm_backends, drv, list);
183

    
184
    return 0;
185
}
186

    
187
static int tpm_init_tpmdev(QemuOpts *opts, void *dummy)
188
{
189
    return configure_tpm(opts);
190
}
191

    
192
/*
193
 * Walk the list of TPM backend drivers that are in use and call their
194
 * destroy function to have them cleaned up.
195
 */
196
void tpm_cleanup(void)
197
{
198
    TPMBackend *drv, *next;
199

    
200
    QLIST_FOREACH_SAFE(drv, &tpm_backends, list, next) {
201
        QLIST_REMOVE(drv, list);
202
        tpm_backend_destroy(drv);
203
    }
204
}
205

    
206
/*
207
 * Initialize the TPM. Process the tpmdev command line options describing the
208
 * TPM backend.
209
 */
210
int tpm_init(void)
211
{
212
    if (qemu_opts_foreach(qemu_find_opts("tpmdev"),
213
                          tpm_init_tpmdev, NULL, 1) != 0) {
214
        return -1;
215
    }
216

    
217
    atexit(tpm_cleanup);
218

    
219
    return 0;
220
}
221

    
222
/*
223
 * Parse the TPM configuration options.
224
 * To display all available TPM backends the user may use '-tpmdev help'
225
 */
226
int tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
227
{
228
    QemuOpts *opts;
229

    
230
    if (!strcmp(optarg, "help")) {
231
        tpm_display_backend_drivers();
232
        return -1;
233
    }
234
    opts = qemu_opts_parse(opts_list, optarg, 1);
235
    if (!opts) {
236
        return -1;
237
    }
238
    return 0;
239
}
240

    
241
#endif /* CONFIG_TPM */
242

    
243
static const TPMDriverOps *tpm_driver_find_by_type(enum TpmType type)
244
{
245
    int i;
246

    
247
    for (i = 0; i < TPM_MAX_DRIVERS && be_drivers[i] != NULL; i++) {
248
        if (be_drivers[i]->type == type) {
249
            return be_drivers[i];
250
        }
251
    }
252
    return NULL;
253
}
254

    
255
static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
256
{
257
    TPMInfo *res = g_new0(TPMInfo, 1);
258
    TPMPassthroughOptions *tpo;
259

    
260
    res->id = g_strdup(drv->id);
261
    res->model = drv->fe_model;
262
    res->options = g_new0(TpmTypeOptions, 1);
263

    
264
    switch (drv->ops->type) {
265
    case TPM_TYPE_PASSTHROUGH:
266
        res->options->kind = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH;
267
        tpo = g_new0(TPMPassthroughOptions, 1);
268
        res->options->passthrough = tpo;
269
        if (drv->path) {
270
            tpo->path = g_strdup(drv->path);
271
            tpo->has_path = true;
272
        }
273
        if (drv->cancel_path) {
274
            tpo->cancel_path = g_strdup(drv->cancel_path);
275
            tpo->has_cancel_path = true;
276
        }
277
        break;
278
    case TPM_TYPE_MAX:
279
        break;
280
    }
281

    
282
    return res;
283
}
284

    
285
/*
286
 * Walk the list of active TPM backends and collect information about them
287
 * following the schema description in qapi-schema.json.
288
 */
289
TPMInfoList *qmp_query_tpm(Error **errp)
290
{
291
    TPMBackend *drv;
292
    TPMInfoList *info, *head = NULL, *cur_item = NULL;
293

    
294
    QLIST_FOREACH(drv, &tpm_backends, list) {
295
        if (!tpm_model_is_registered(drv->fe_model)) {
296
            continue;
297
        }
298
        info = g_new0(TPMInfoList, 1);
299
        info->value = qmp_query_tpm_inst(drv);
300

    
301
        if (!cur_item) {
302
            head = cur_item = info;
303
        } else {
304
            cur_item->next = info;
305
            cur_item = info;
306
        }
307
    }
308

    
309
    return head;
310
}
311

    
312
TpmTypeList *qmp_query_tpm_types(Error **errp)
313
{
314
    unsigned int i = 0;
315
    TpmTypeList *head = NULL, *prev = NULL, *cur_item;
316

    
317
    for (i = 0; i < TPM_TYPE_MAX; i++) {
318
        if (!tpm_driver_find_by_type(i)) {
319
            continue;
320
        }
321
        cur_item = g_new0(TpmTypeList, 1);
322
        cur_item->value = i;
323

    
324
        if (prev) {
325
            prev->next = cur_item;
326
        }
327
        if (!head) {
328
            head = cur_item;
329
        }
330
        prev = cur_item;
331
    }
332

    
333
    return head;
334
}
335

    
336
TpmModelList *qmp_query_tpm_models(Error **errp)
337
{
338
    unsigned int i = 0;
339
    TpmModelList *head = NULL, *prev = NULL, *cur_item;
340

    
341
    for (i = 0; i < TPM_MODEL_MAX; i++) {
342
        if (!tpm_model_is_registered(i)) {
343
            continue;
344
        }
345
        cur_item = g_new0(TpmModelList, 1);
346
        cur_item->value = i;
347

    
348
        if (prev) {
349
            prev->next = cur_item;
350
        }
351
        if (!head) {
352
            head = cur_item;
353
        }
354
        prev = cur_item;
355
    }
356

    
357
    return head;
358
}