xref: /qemu/system/tpm.c (revision 91e11db7bcc486db2dc2bdab94ac5de62c02ce9a)
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  
25  static QLIST_HEAD(, TPMBackend) tpm_backends =
26      QLIST_HEAD_INITIALIZER(tpm_backends);
27  
28  static const TPMBackendClass *
29  tpm_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   */
48  static 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   */
72  TPMBackend *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  
87  static 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   */
150  void 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   */
164  int 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   */
178  int tpm_config_parse(QemuOptsList *opts_list, const char *optstr)
179  {
180      QemuOpts *opts;
181  
182      if (!strcmp(optstr, "help")) {
183          tpm_display_backend_drivers();
184          return -1;
185      }
186      opts = qemu_opts_parse_noisily(opts_list, optstr, 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   */
196  TPMInfoList *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  
212  TpmTypeList *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  }
226  TpmModelList *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