Statistics
| Branch: | Revision:

root / tpm.c @ bdee56f5

History | View | Annotate | Download (7.3 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 "backends/tpm.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
    -1,
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] == -1) {
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
    drv = be->create(opts, id);
163
    if (!drv) {
164
        return 1;
165
    }
166

    
167
    tpm_backend_open(drv, &local_err);
168
    if (local_err) {
169
        qerror_report_err(local_err);
170
        error_free(local_err);
171
        return 1;
172
    }
173

    
174
    QLIST_INSERT_HEAD(&tpm_backends, drv, list);
175

    
176
    return 0;
177
}
178

    
179
static int tpm_init_tpmdev(QemuOpts *opts, void *dummy)
180
{
181
    return configure_tpm(opts);
182
}
183

    
184
/*
185
 * Walk the list of TPM backend drivers that are in use and call their
186
 * destroy function to have them cleaned up.
187
 */
188
void tpm_cleanup(void)
189
{
190
    TPMBackend *drv, *next;
191

    
192
    QLIST_FOREACH_SAFE(drv, &tpm_backends, list, next) {
193
        QLIST_REMOVE(drv, list);
194
        tpm_backend_destroy(drv);
195
    }
196
}
197

    
198
/*
199
 * Initialize the TPM. Process the tpmdev command line options describing the
200
 * TPM backend.
201
 */
202
int tpm_init(void)
203
{
204
    if (qemu_opts_foreach(qemu_find_opts("tpmdev"),
205
                          tpm_init_tpmdev, NULL, 1) != 0) {
206
        return -1;
207
    }
208

    
209
    atexit(tpm_cleanup);
210

    
211
    return 0;
212
}
213

    
214
/*
215
 * Parse the TPM configuration options.
216
 * To display all available TPM backends the user may use '-tpmdev help'
217
 */
218
int tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
219
{
220
    QemuOpts *opts;
221

    
222
    if (!strcmp(optarg, "help")) {
223
        tpm_display_backend_drivers();
224
        return -1;
225
    }
226
    opts = qemu_opts_parse(opts_list, optarg, 1);
227
    if (!opts) {
228
        return -1;
229
    }
230
    return 0;
231
}
232

    
233
#endif /* CONFIG_TPM */
234

    
235
static const TPMDriverOps *tpm_driver_find_by_type(enum TpmType type)
236
{
237
    int i;
238

    
239
    for (i = 0; i < TPM_MAX_DRIVERS && be_drivers[i] != NULL; i++) {
240
        if (be_drivers[i]->type == type) {
241
            return be_drivers[i];
242
        }
243
    }
244
    return NULL;
245
}
246

    
247
static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv)
248
{
249
    TPMInfo *res = g_new0(TPMInfo, 1);
250
    TPMPassthroughOptions *tpo;
251

    
252
    res->id = g_strdup(drv->id);
253
    res->model = drv->fe_model;
254
    res->options = g_new0(TpmTypeOptions, 1);
255

    
256
    switch (drv->ops->type) {
257
    case TPM_TYPE_PASSTHROUGH:
258
        res->options->kind = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH;
259
        tpo = g_new0(TPMPassthroughOptions, 1);
260
        res->options->passthrough = tpo;
261
        if (drv->path) {
262
            tpo->path = g_strdup(drv->path);
263
            tpo->has_path = true;
264
        }
265
        if (drv->cancel_path) {
266
            tpo->cancel_path = g_strdup(drv->cancel_path);
267
            tpo->has_cancel_path = true;
268
        }
269
        break;
270
    case TPM_TYPE_MAX:
271
        break;
272
    }
273

    
274
    return res;
275
}
276

    
277
/*
278
 * Walk the list of active TPM backends and collect information about them
279
 * following the schema description in qapi-schema.json.
280
 */
281
TPMInfoList *qmp_query_tpm(Error **errp)
282
{
283
    TPMBackend *drv;
284
    TPMInfoList *info, *head = NULL, *cur_item = NULL;
285

    
286
    QLIST_FOREACH(drv, &tpm_backends, list) {
287
        if (!tpm_model_is_registered(drv->fe_model)) {
288
            continue;
289
        }
290
        info = g_new0(TPMInfoList, 1);
291
        info->value = qmp_query_tpm_inst(drv);
292

    
293
        if (!cur_item) {
294
            head = cur_item = info;
295
        } else {
296
            cur_item->next = info;
297
            cur_item = info;
298
        }
299
    }
300

    
301
    return head;
302
}
303

    
304
TpmTypeList *qmp_query_tpm_types(Error **errp)
305
{
306
    unsigned int i = 0;
307
    TpmTypeList *head = NULL, *prev = NULL, *cur_item;
308

    
309
    for (i = 0; i < TPM_TYPE_MAX; i++) {
310
        if (!tpm_driver_find_by_type(i)) {
311
            continue;
312
        }
313
        cur_item = g_new0(TpmTypeList, 1);
314
        cur_item->value = i;
315

    
316
        if (prev) {
317
            prev->next = cur_item;
318
        }
319
        if (!head) {
320
            head = cur_item;
321
        }
322
        prev = cur_item;
323
    }
324

    
325
    return head;
326
}
327

    
328
TpmModelList *qmp_query_tpm_models(Error **errp)
329
{
330
    unsigned int i = 0;
331
    TpmModelList *head = NULL, *prev = NULL, *cur_item;
332

    
333
    for (i = 0; i < TPM_MODEL_MAX; i++) {
334
        if (!tpm_model_is_registered(i)) {
335
            continue;
336
        }
337
        cur_item = g_new0(TpmModelList, 1);
338
        cur_item->value = i;
339

    
340
        if (prev) {
341
            prev->next = cur_item;
342
        }
343
        if (!head) {
344
            head = cur_item;
345
        }
346
        prev = cur_item;
347
    }
348

    
349
    return head;
350
}