qemu/softmmu/tpm.c
<<
>>
Prefs
   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
  15#include "qemu/osdep.h"
  16
  17#include "qapi/error.h"
  18#include "qapi/qapi-commands-tpm.h"
  19#include "qapi/qmp/qerror.h"
  20#include "sysemu/tpm_backend.h"
  21#include "sysemu/tpm.h"
  22#include "qemu/config-file.h"
  23#include "qemu/error-report.h"
  24
  25static QLIST_HEAD(, TPMBackend) tpm_backends =
  26    QLIST_HEAD_INITIALIZER(tpm_backends);
  27
  28static const TPMBackendClass *
  29tpm_be_find_by_type(enum TpmType type)
  30{
  31    ObjectClass *oc;
  32    char *typename = g_strdup_printf("tpm-%s", TpmType_str(type));
  33
  34    oc = object_class_by_name(typename);
  35    g_free(typename);
  36
  37    if (!object_class_dynamic_cast(oc, TYPE_TPM_BACKEND)) {
  38        return NULL;
  39    }
  40
  41    return TPM_BACKEND_CLASS(oc);
  42}
  43
  44/*
  45 * Walk the list of available TPM backend drivers and display them on the
  46 * screen.
  47 */
  48static void tpm_display_backend_drivers(void)
  49{
  50    bool got_one = false;
  51    int i;
  52
  53    for (i = 0; i < TPM_TYPE__MAX; i++) {
  54        const TPMBackendClass *bc = tpm_be_find_by_type(i);
  55        if (!bc) {
  56            continue;
  57        }
  58        if (!got_one) {
  59            error_printf("Supported TPM types (choose only one):\n");
  60            got_one = true;
  61        }
  62        error_printf("%12s   %s\n", TpmType_str(i), bc->desc);
  63    }
  64    if (!got_one) {
  65        error_printf("No TPM backend types are available\n");
  66    }
  67}
  68
  69/*
  70 * Find the TPM with the given Id
  71 */
  72TPMBackend *qemu_find_tpm_be(const char *id)
  73{
  74    TPMBackend *drv;
  75
  76    if (id) {
  77        QLIST_FOREACH(drv, &tpm_backends, list) {
  78            if (!strcmp(drv->id, id)) {
  79                return drv;
  80            }
  81        }
  82    }
  83
  84    return NULL;
  85}
  86
  87static int tpm_init_tpmdev(void *dummy, QemuOpts *opts, Error **errp)
  88{
  89    /*
  90     * Use of error_report() in a function with an Error ** parameter
  91     * is suspicious.  It is okay here.  The parameter only exists to
  92     * make the function usable with qemu_opts_foreach().  It is not
  93     * actually used.
  94     */
  95    const char *value;
  96    const char *id;
  97    const TPMBackendClass *be;
  98    TPMBackend *drv;
  99    Error *local_err = NULL;
 100    int i;
 101
 102    if (!QLIST_EMPTY(&tpm_backends)) {
 103        error_report("Only one TPM is allowed.");
 104        return 1;
 105    }
 106
 107    id = qemu_opts_id(opts);
 108    if (id == NULL) {
 109        error_report(QERR_MISSING_PARAMETER, "id");
 110        return 1;
 111    }
 112
 113    value = qemu_opt_get(opts, "type");
 114    if (!value) {
 115        error_report(QERR_MISSING_PARAMETER, "type");
 116        tpm_display_backend_drivers();
 117        return 1;
 118    }
 119
 120    i = qapi_enum_parse(&TpmType_lookup, value, -1, NULL);
 121    be = i >= 0 ? tpm_be_find_by_type(i) : NULL;
 122    if (be == NULL) {
 123        error_report(QERR_INVALID_PARAMETER_VALUE,
 124                     "type", "a TPM backend type");
 125        tpm_display_backend_drivers();
 126        return 1;
 127    }
 128
 129    /* validate backend specific opts */
 130    if (!qemu_opts_validate(opts, be->opts, &local_err)) {
 131        error_report_err(local_err);
 132        return 1;
 133    }
 134
 135    drv = be->create(opts);
 136    if (!drv) {
 137        return 1;
 138    }
 139
 140    drv->id = g_strdup(id);
 141    QLIST_INSERT_HEAD(&tpm_backends, drv, list);
 142
 143    return 0;
 144}
 145
 146/*
 147 * Walk the list of TPM backend drivers that are in use and call their
 148 * destroy function to have them cleaned up.
 149 */
 150void tpm_cleanup(void)
 151{
 152    TPMBackend *drv, *next;
 153
 154    QLIST_FOREACH_SAFE(drv, &tpm_backends, list, next) {
 155        QLIST_REMOVE(drv, list);
 156        object_unref(OBJECT(drv));
 157    }
 158}
 159
 160/*
 161 * Initialize the TPM. Process the tpmdev command line options describing the
 162 * TPM backend.
 163 */
 164int tpm_init(void)
 165{
 166    if (qemu_opts_foreach(qemu_find_opts("tpmdev"),
 167                          tpm_init_tpmdev, NULL, NULL)) {
 168        return -1;
 169    }
 170
 171    return 0;
 172}
 173
 174/*
 175 * Parse the TPM configuration options.
 176 * To display all available TPM backends the user may use '-tpmdev help'
 177 */
 178int tpm_config_parse(QemuOptsList *opts_list, const char *optarg)
 179{
 180    QemuOpts *opts;
 181
 182    if (!strcmp(optarg, "help")) {
 183        tpm_display_backend_drivers();
 184        return -1;
 185    }
 186    opts = qemu_opts_parse_noisily(opts_list, optarg, true);
 187    if (!opts) {
 188        return -1;
 189    }
 190    return 0;
 191}
 192
 193/*
 194 * Walk the list of active TPM backends and collect information about them.
 195 */
 196TPMInfoList *qmp_query_tpm(Error **errp)
 197{
 198    TPMBackend *drv;
 199    TPMInfoList *head = NULL, **tail = &head;
 200
 201    QLIST_FOREACH(drv, &tpm_backends, list) {
 202        if (!drv->tpmif) {
 203            continue;
 204        }
 205
 206        QAPI_LIST_APPEND(tail, tpm_backend_query_tpm(drv));
 207    }
 208
 209    return head;
 210}
 211
 212TpmTypeList *qmp_query_tpm_types(Error **errp)
 213{
 214    unsigned int i = 0;
 215    TpmTypeList *head = NULL, **tail = &head;
 216
 217    for (i = 0; i < TPM_TYPE__MAX; i++) {
 218        if (!tpm_be_find_by_type(i)) {
 219            continue;
 220        }
 221        QAPI_LIST_APPEND(tail, i);
 222    }
 223
 224    return head;
 225}
 226TpmModelList *qmp_query_tpm_models(Error **errp)
 227{
 228    TpmModelList *head = NULL, **tail = &head;
 229    GSList *e, *l = object_class_get_list(TYPE_TPM_IF, false);
 230
 231    for (e = l; e; e = e->next) {
 232        TPMIfClass *c = TPM_IF_CLASS(e->data);
 233
 234        QAPI_LIST_APPEND(tail, c->model);
 235    }
 236    g_slist_free(l);
 237
 238    return head;
 239}
 240