|  | /* | 
|  | * TPM configuration | 
|  | * | 
|  | * Copyright (C) 2011-2013 IBM Corporation | 
|  | * | 
|  | * Authors: | 
|  | *  Stefan Berger    <stefanb@us.ibm.com> | 
|  | * | 
|  | * This work is licensed under the terms of the GNU GPL, version 2 or later. | 
|  | * See the COPYING file in the top-level directory. | 
|  | * | 
|  | * Based on net.c | 
|  | */ | 
|  | #include "config-host.h" | 
|  |  | 
|  | #include "qapi/qmp/qerror.h" | 
|  | #include "sysemu/tpm_backend.h" | 
|  | #include "sysemu/tpm.h" | 
|  | #include "qemu/config-file.h" | 
|  | #include "qemu/error-report.h" | 
|  | #include "qmp-commands.h" | 
|  |  | 
|  | static QLIST_HEAD(, TPMBackend) tpm_backends = | 
|  | QLIST_HEAD_INITIALIZER(tpm_backends); | 
|  |  | 
|  |  | 
|  | #define TPM_MAX_MODELS      1 | 
|  | #define TPM_MAX_DRIVERS     1 | 
|  |  | 
|  | static TPMDriverOps const *be_drivers[TPM_MAX_DRIVERS] = { | 
|  | NULL, | 
|  | }; | 
|  |  | 
|  | static enum TpmModel tpm_models[TPM_MAX_MODELS] = { | 
|  | TPM_MODEL_MAX, | 
|  | }; | 
|  |  | 
|  | int tpm_register_model(enum TpmModel model) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < TPM_MAX_MODELS; i++) { | 
|  | if (tpm_models[i] == TPM_MODEL_MAX) { | 
|  | tpm_models[i] = model; | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | error_report("Could not register TPM model"); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static bool tpm_model_is_registered(enum TpmModel model) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < TPM_MAX_MODELS; i++) { | 
|  | if (tpm_models[i] == model) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | const TPMDriverOps *tpm_get_backend_driver(const char *type) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < TPM_MAX_DRIVERS && be_drivers[i] != NULL; i++) { | 
|  | if (!strcmp(TpmType_lookup[be_drivers[i]->type], type)) { | 
|  | return be_drivers[i]; | 
|  | } | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_TPM | 
|  |  | 
|  | int tpm_register_driver(const TPMDriverOps *tdo) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < TPM_MAX_DRIVERS; i++) { | 
|  | if (!be_drivers[i]) { | 
|  | be_drivers[i] = tdo; | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | error_report("Could not register TPM driver"); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Walk the list of available TPM backend drivers and display them on the | 
|  | * screen. | 
|  | */ | 
|  | static void tpm_display_backend_drivers(void) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | fprintf(stderr, "Supported TPM types (choose only one):\n"); | 
|  |  | 
|  | for (i = 0; i < TPM_MAX_DRIVERS && be_drivers[i] != NULL; i++) { | 
|  | fprintf(stderr, "%12s   %s\n", | 
|  | TpmType_lookup[be_drivers[i]->type], be_drivers[i]->desc()); | 
|  | } | 
|  | fprintf(stderr, "\n"); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Find the TPM with the given Id | 
|  | */ | 
|  | TPMBackend *qemu_find_tpm(const char *id) | 
|  | { | 
|  | TPMBackend *drv; | 
|  |  | 
|  | if (id) { | 
|  | QLIST_FOREACH(drv, &tpm_backends, list) { | 
|  | if (!strcmp(drv->id, id)) { | 
|  | return drv; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static int configure_tpm(QemuOpts *opts) | 
|  | { | 
|  | const char *value; | 
|  | const char *id; | 
|  | const TPMDriverOps *be; | 
|  | TPMBackend *drv; | 
|  | Error *local_err = NULL; | 
|  |  | 
|  | if (!QLIST_EMPTY(&tpm_backends)) { | 
|  | error_report("Only one TPM is allowed."); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | id = qemu_opts_id(opts); | 
|  | if (id == NULL) { | 
|  | error_report(QERR_MISSING_PARAMETER, "id"); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | value = qemu_opt_get(opts, "type"); | 
|  | if (!value) { | 
|  | error_report(QERR_MISSING_PARAMETER, "type"); | 
|  | tpm_display_backend_drivers(); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | be = tpm_get_backend_driver(value); | 
|  | if (be == NULL) { | 
|  | error_report(QERR_INVALID_PARAMETER_VALUE, | 
|  | "type", "a TPM backend type"); | 
|  | tpm_display_backend_drivers(); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* validate backend specific opts */ | 
|  | qemu_opts_validate(opts, be->opts, &local_err); | 
|  | if (local_err) { | 
|  | error_report_err(local_err); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | drv = be->create(opts, id); | 
|  | if (!drv) { | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | tpm_backend_open(drv, &local_err); | 
|  | if (local_err) { | 
|  | error_report_err(local_err); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | QLIST_INSERT_HEAD(&tpm_backends, drv, list); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int tpm_init_tpmdev(void *dummy, QemuOpts *opts, Error **errp) | 
|  | { | 
|  | return configure_tpm(opts); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Walk the list of TPM backend drivers that are in use and call their | 
|  | * destroy function to have them cleaned up. | 
|  | */ | 
|  | void tpm_cleanup(void) | 
|  | { | 
|  | TPMBackend *drv, *next; | 
|  |  | 
|  | QLIST_FOREACH_SAFE(drv, &tpm_backends, list, next) { | 
|  | QLIST_REMOVE(drv, list); | 
|  | tpm_backend_destroy(drv); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Initialize the TPM. Process the tpmdev command line options describing the | 
|  | * TPM backend. | 
|  | */ | 
|  | int tpm_init(void) | 
|  | { | 
|  | if (qemu_opts_foreach(qemu_find_opts("tpmdev"), | 
|  | tpm_init_tpmdev, NULL, NULL)) { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | atexit(tpm_cleanup); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Parse the TPM configuration options. | 
|  | * To display all available TPM backends the user may use '-tpmdev help' | 
|  | */ | 
|  | int tpm_config_parse(QemuOptsList *opts_list, const char *optarg) | 
|  | { | 
|  | QemuOpts *opts; | 
|  |  | 
|  | if (!strcmp(optarg, "help")) { | 
|  | tpm_display_backend_drivers(); | 
|  | return -1; | 
|  | } | 
|  | opts = qemu_opts_parse_noisily(opts_list, optarg, true); | 
|  | if (!opts) { | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #endif /* CONFIG_TPM */ | 
|  |  | 
|  | static const TPMDriverOps *tpm_driver_find_by_type(enum TpmType type) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < TPM_MAX_DRIVERS && be_drivers[i] != NULL; i++) { | 
|  | if (be_drivers[i]->type == type) { | 
|  | return be_drivers[i]; | 
|  | } | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv) | 
|  | { | 
|  | TPMInfo *res = g_new0(TPMInfo, 1); | 
|  | TPMPassthroughOptions *tpo; | 
|  |  | 
|  | res->id = g_strdup(drv->id); | 
|  | res->model = drv->fe_model; | 
|  | res->options = g_new0(TpmTypeOptions, 1); | 
|  |  | 
|  | switch (drv->ops->type) { | 
|  | case TPM_TYPE_PASSTHROUGH: | 
|  | res->options->type = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH; | 
|  | tpo = g_new0(TPMPassthroughOptions, 1); | 
|  | res->options->u.passthrough = tpo; | 
|  | if (drv->path) { | 
|  | tpo->path = g_strdup(drv->path); | 
|  | tpo->has_path = true; | 
|  | } | 
|  | if (drv->cancel_path) { | 
|  | tpo->cancel_path = g_strdup(drv->cancel_path); | 
|  | tpo->has_cancel_path = true; | 
|  | } | 
|  | break; | 
|  | case TPM_TYPE_MAX: | 
|  | break; | 
|  | } | 
|  |  | 
|  | return res; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Walk the list of active TPM backends and collect information about them | 
|  | * following the schema description in qapi-schema.json. | 
|  | */ | 
|  | TPMInfoList *qmp_query_tpm(Error **errp) | 
|  | { | 
|  | TPMBackend *drv; | 
|  | TPMInfoList *info, *head = NULL, *cur_item = NULL; | 
|  |  | 
|  | QLIST_FOREACH(drv, &tpm_backends, list) { | 
|  | if (!tpm_model_is_registered(drv->fe_model)) { | 
|  | continue; | 
|  | } | 
|  | info = g_new0(TPMInfoList, 1); | 
|  | info->value = qmp_query_tpm_inst(drv); | 
|  |  | 
|  | if (!cur_item) { | 
|  | head = cur_item = info; | 
|  | } else { | 
|  | cur_item->next = info; | 
|  | cur_item = info; | 
|  | } | 
|  | } | 
|  |  | 
|  | return head; | 
|  | } | 
|  |  | 
|  | TpmTypeList *qmp_query_tpm_types(Error **errp) | 
|  | { | 
|  | unsigned int i = 0; | 
|  | TpmTypeList *head = NULL, *prev = NULL, *cur_item; | 
|  |  | 
|  | for (i = 0; i < TPM_TYPE_MAX; i++) { | 
|  | if (!tpm_driver_find_by_type(i)) { | 
|  | continue; | 
|  | } | 
|  | cur_item = g_new0(TpmTypeList, 1); | 
|  | cur_item->value = i; | 
|  |  | 
|  | if (prev) { | 
|  | prev->next = cur_item; | 
|  | } | 
|  | if (!head) { | 
|  | head = cur_item; | 
|  | } | 
|  | prev = cur_item; | 
|  | } | 
|  |  | 
|  | return head; | 
|  | } | 
|  |  | 
|  | TpmModelList *qmp_query_tpm_models(Error **errp) | 
|  | { | 
|  | unsigned int i = 0; | 
|  | TpmModelList *head = NULL, *prev = NULL, *cur_item; | 
|  |  | 
|  | for (i = 0; i < TPM_MODEL_MAX; i++) { | 
|  | if (!tpm_model_is_registered(i)) { | 
|  | continue; | 
|  | } | 
|  | cur_item = g_new0(TpmModelList, 1); | 
|  | cur_item->value = i; | 
|  |  | 
|  | if (prev) { | 
|  | prev->next = cur_item; | 
|  | } | 
|  | if (!head) { | 
|  | head = cur_item; | 
|  | } | 
|  | prev = cur_item; | 
|  | } | 
|  |  | 
|  | return head; | 
|  | } |