xref: /qemu/backends/tpm/tpm_emulator.c (revision 99bdcd2cc2d05833f5c11caca22193f8dd878ae9)
1f4ede81eSAmarnath Valluri /*
2f4ede81eSAmarnath Valluri  *  Emulator TPM driver
3f4ede81eSAmarnath Valluri  *
4f4ede81eSAmarnath Valluri  *  Copyright (c) 2017 Intel Corporation
5f4ede81eSAmarnath Valluri  *  Author: Amarnath Valluri <amarnath.valluri@intel.com>
6f4ede81eSAmarnath Valluri  *
738ab74e7SStefan Berger  *  Copyright (c) 2010 - 2013, 2018 IBM Corporation
8f4ede81eSAmarnath Valluri  *  Authors:
9f4ede81eSAmarnath Valluri  *    Stefan Berger <stefanb@us.ibm.com>
10f4ede81eSAmarnath Valluri  *
11f4ede81eSAmarnath Valluri  *  Copyright (C) 2011 IAIK, Graz University of Technology
12f4ede81eSAmarnath Valluri  *    Author: Andreas Niederl
13f4ede81eSAmarnath Valluri  *
14f4ede81eSAmarnath Valluri  * This library is free software; you can redistribute it and/or
15f4ede81eSAmarnath Valluri  * modify it under the terms of the GNU Lesser General Public
16f4ede81eSAmarnath Valluri  * License as published by the Free Software Foundation; either
17eac2fce9SChetan Pant  * version 2.1 of the License, or (at your option) any later version.
18f4ede81eSAmarnath Valluri  *
19f4ede81eSAmarnath Valluri  * This library is distributed in the hope that it will be useful,
20f4ede81eSAmarnath Valluri  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21f4ede81eSAmarnath Valluri  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22f4ede81eSAmarnath Valluri  * Lesser General Public License for more details.
23f4ede81eSAmarnath Valluri  *
24f4ede81eSAmarnath Valluri  * You should have received a copy of the GNU Lesser General Public
25f4ede81eSAmarnath Valluri  * License along with this library; if not, see <http://www.gnu.org/licenses/>
26f4ede81eSAmarnath Valluri  *
27f4ede81eSAmarnath Valluri  */
28f4ede81eSAmarnath Valluri 
29f4ede81eSAmarnath Valluri #include "qemu/osdep.h"
30f4ede81eSAmarnath Valluri #include "qemu/error-report.h"
310b8fa32fSMarkus Armbruster #include "qemu/module.h"
32f4ede81eSAmarnath Valluri #include "qemu/sockets.h"
33bf5dcf8fSPhilippe Mathieu-Daudé #include "qemu/lockable.h"
34f4ede81eSAmarnath Valluri #include "io/channel-socket.h"
35a0bcec03SRoss Lagerwall #include "sysemu/runstate.h"
36f4ede81eSAmarnath Valluri #include "sysemu/tpm_backend.h"
370f7d2148SPhilippe Mathieu-Daudé #include "sysemu/tpm_util.h"
38*99bdcd2cSStefan Berger #include "sysemu/runstate.h"
39f4ede81eSAmarnath Valluri #include "tpm_int.h"
40f4ede81eSAmarnath Valluri #include "tpm_ioctl.h"
41f4ede81eSAmarnath Valluri #include "migration/blocker.h"
42d6454270SMarkus Armbruster #include "migration/vmstate.h"
43f4ede81eSAmarnath Valluri #include "qapi/error.h"
44f4ede81eSAmarnath Valluri #include "qapi/clone-visitor.h"
459af23989SMarkus Armbruster #include "qapi/qapi-visit-tpm.h"
46f4ede81eSAmarnath Valluri #include "chardev/char-fe.h"
479d9dcd96SStefan Berger #include "trace.h"
48db1015e9SEduardo Habkost #include "qom/object.h"
49f4ede81eSAmarnath Valluri 
50f4ede81eSAmarnath Valluri #define TYPE_TPM_EMULATOR "tpm-emulator"
518063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(TPMEmulator, TPM_EMULATOR)
52f4ede81eSAmarnath Valluri 
53f4ede81eSAmarnath Valluri #define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap))
54f4ede81eSAmarnath Valluri 
55f4ede81eSAmarnath Valluri /* data structures */
5638ab74e7SStefan Berger 
5738ab74e7SStefan Berger /* blobs from the TPM; part of VM state when migrating */
5838ab74e7SStefan Berger typedef struct TPMBlobBuffers {
5938ab74e7SStefan Berger     uint32_t permanent_flags;
6038ab74e7SStefan Berger     TPMSizedBuffer permanent;
6138ab74e7SStefan Berger 
6238ab74e7SStefan Berger     uint32_t volatil_flags;
6338ab74e7SStefan Berger     TPMSizedBuffer volatil;
6438ab74e7SStefan Berger 
6538ab74e7SStefan Berger     uint32_t savestate_flags;
6638ab74e7SStefan Berger     TPMSizedBuffer savestate;
6738ab74e7SStefan Berger } TPMBlobBuffers;
6838ab74e7SStefan Berger 
69db1015e9SEduardo Habkost struct TPMEmulator {
70f4ede81eSAmarnath Valluri     TPMBackend parent;
71f4ede81eSAmarnath Valluri 
72f4ede81eSAmarnath Valluri     TPMEmulatorOptions *options;
73f4ede81eSAmarnath Valluri     CharBackend ctrl_chr;
74f4ede81eSAmarnath Valluri     QIOChannel *data_ioc;
75f4ede81eSAmarnath Valluri     TPMVersion tpm_version;
76f4ede81eSAmarnath Valluri     ptm_cap caps; /* capabilities of the TPM */
77f4ede81eSAmarnath Valluri     uint8_t cur_locty_number; /* last set locality */
78f4ede81eSAmarnath Valluri     Error *migration_blocker;
7917b1af77SMarc-André Lureau 
8017b1af77SMarc-André Lureau     QemuMutex mutex;
810b4c7c65SStefan Berger 
820b4c7c65SStefan Berger     unsigned int established_flag:1;
830b4c7c65SStefan Berger     unsigned int established_flag_cached:1;
8438ab74e7SStefan Berger 
8538ab74e7SStefan Berger     TPMBlobBuffers state_blobs;
86*99bdcd2cSStefan Berger 
87*99bdcd2cSStefan Berger     bool relock_storage;
88*99bdcd2cSStefan Berger     VMChangeStateEntry *vmstate;
89db1015e9SEduardo Habkost };
90f4ede81eSAmarnath Valluri 
917e095e84SStefan Berger struct tpm_error {
927e095e84SStefan Berger     uint32_t tpm_result;
937e095e84SStefan Berger     const char *string;
947e095e84SStefan Berger };
957e095e84SStefan Berger 
967e095e84SStefan Berger static const struct tpm_error tpm_errors[] = {
977e095e84SStefan Berger     /* TPM 1.2 error codes */
987e095e84SStefan Berger     { TPM_BAD_PARAMETER   , "a parameter is bad" },
997e095e84SStefan Berger     { TPM_FAIL            , "operation failed" },
1007e095e84SStefan Berger     { TPM_KEYNOTFOUND     , "key could not be found" },
1017e095e84SStefan Berger     { TPM_BAD_PARAM_SIZE  , "bad parameter size"},
1027e095e84SStefan Berger     { TPM_ENCRYPT_ERROR   , "encryption error" },
1037e095e84SStefan Berger     { TPM_DECRYPT_ERROR   , "decryption error" },
1047e095e84SStefan Berger     { TPM_BAD_KEY_PROPERTY, "bad key property" },
1057e095e84SStefan Berger     { TPM_BAD_MODE        , "bad (encryption) mode" },
1067e095e84SStefan Berger     { TPM_BAD_VERSION     , "bad version identifier" },
1077e095e84SStefan Berger     { TPM_BAD_LOCALITY    , "bad locality" },
1087e095e84SStefan Berger     /* TPM 2 error codes */
1097e095e84SStefan Berger     { TPM_RC_FAILURE     , "operation failed" },
1107e095e84SStefan Berger     { TPM_RC_LOCALITY    , "bad locality"     },
1117e095e84SStefan Berger     { TPM_RC_INSUFFICIENT, "insufficient amount of data" },
1127e095e84SStefan Berger };
1137e095e84SStefan Berger 
1147e095e84SStefan Berger static const char *tpm_emulator_strerror(uint32_t tpm_result)
1157e095e84SStefan Berger {
1167e095e84SStefan Berger     size_t i;
1177e095e84SStefan Berger 
1187e095e84SStefan Berger     for (i = 0; i < ARRAY_SIZE(tpm_errors); i++) {
1197e095e84SStefan Berger         if (tpm_errors[i].tpm_result == tpm_result) {
1207e095e84SStefan Berger             return tpm_errors[i].string;
1217e095e84SStefan Berger         }
1227e095e84SStefan Berger     }
1237e095e84SStefan Berger     return "";
1247e095e84SStefan Berger }
125f4ede81eSAmarnath Valluri 
12617b1af77SMarc-André Lureau static int tpm_emulator_ctrlcmd(TPMEmulator *tpm, unsigned long cmd, void *msg,
127f4ede81eSAmarnath Valluri                                 size_t msg_len_in, size_t msg_len_out)
128f4ede81eSAmarnath Valluri {
12917b1af77SMarc-André Lureau     CharBackend *dev = &tpm->ctrl_chr;
130f4ede81eSAmarnath Valluri     uint32_t cmd_no = cpu_to_be32(cmd);
131f4ede81eSAmarnath Valluri     ssize_t n = sizeof(uint32_t) + msg_len_in;
132f4ede81eSAmarnath Valluri     uint8_t *buf = NULL;
13317b1af77SMarc-André Lureau 
134bf5dcf8fSPhilippe Mathieu-Daudé     WITH_QEMU_LOCK_GUARD(&tpm->mutex) {
135f4ede81eSAmarnath Valluri         buf = g_alloca(n);
136f4ede81eSAmarnath Valluri         memcpy(buf, &cmd_no, sizeof(cmd_no));
137f4ede81eSAmarnath Valluri         memcpy(buf + sizeof(cmd_no), msg, msg_len_in);
138f4ede81eSAmarnath Valluri 
139f4ede81eSAmarnath Valluri         n = qemu_chr_fe_write_all(dev, buf, n);
140f4ede81eSAmarnath Valluri         if (n <= 0) {
141bf5dcf8fSPhilippe Mathieu-Daudé             return -1;
142f4ede81eSAmarnath Valluri         }
143f4ede81eSAmarnath Valluri 
144f4ede81eSAmarnath Valluri         if (msg_len_out != 0) {
145f4ede81eSAmarnath Valluri             n = qemu_chr_fe_read_all(dev, msg, msg_len_out);
146f4ede81eSAmarnath Valluri             if (n <= 0) {
147bf5dcf8fSPhilippe Mathieu-Daudé                 return -1;
148bf5dcf8fSPhilippe Mathieu-Daudé             }
149f4ede81eSAmarnath Valluri         }
150f4ede81eSAmarnath Valluri     }
151f4ede81eSAmarnath Valluri 
152bf5dcf8fSPhilippe Mathieu-Daudé     return 0;
153f4ede81eSAmarnath Valluri }
154f4ede81eSAmarnath Valluri 
155f4ede81eSAmarnath Valluri static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu,
156f4ede81eSAmarnath Valluri                                      const uint8_t *in, uint32_t in_len,
157f4ede81eSAmarnath Valluri                                      uint8_t *out, uint32_t out_len,
158f4ede81eSAmarnath Valluri                                      bool *selftest_done,
159e04e3321SVladimir Sementsov-Ogievskiy                                      Error **errp)
160f4ede81eSAmarnath Valluri {
161f4ede81eSAmarnath Valluri     ssize_t ret;
162f4ede81eSAmarnath Valluri     bool is_selftest = false;
163f4ede81eSAmarnath Valluri 
164f4ede81eSAmarnath Valluri     if (selftest_done) {
165f4ede81eSAmarnath Valluri         *selftest_done = false;
166f4ede81eSAmarnath Valluri         is_selftest = tpm_util_is_selftest(in, in_len);
167f4ede81eSAmarnath Valluri     }
168f4ede81eSAmarnath Valluri 
169e04e3321SVladimir Sementsov-Ogievskiy     ret = qio_channel_write_all(tpm_emu->data_ioc, (char *)in, in_len, errp);
170f4ede81eSAmarnath Valluri     if (ret != 0) {
171f4ede81eSAmarnath Valluri         return -1;
172f4ede81eSAmarnath Valluri     }
173f4ede81eSAmarnath Valluri 
174cc1b6c55SMarc-André Lureau     ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out,
175e04e3321SVladimir Sementsov-Ogievskiy               sizeof(struct tpm_resp_hdr), errp);
176f4ede81eSAmarnath Valluri     if (ret != 0) {
177f4ede81eSAmarnath Valluri         return -1;
178f4ede81eSAmarnath Valluri     }
179f4ede81eSAmarnath Valluri 
180cc1b6c55SMarc-André Lureau     ret = qio_channel_read_all(tpm_emu->data_ioc,
181cc1b6c55SMarc-André Lureau               (char *)out + sizeof(struct tpm_resp_hdr),
182e04e3321SVladimir Sementsov-Ogievskiy               tpm_cmd_get_size(out) - sizeof(struct tpm_resp_hdr), errp);
183f4ede81eSAmarnath Valluri     if (ret != 0) {
184f4ede81eSAmarnath Valluri         return -1;
185f4ede81eSAmarnath Valluri     }
186f4ede81eSAmarnath Valluri 
187f4ede81eSAmarnath Valluri     if (is_selftest) {
188cc1b6c55SMarc-André Lureau         *selftest_done = tpm_cmd_get_errcode(out) == 0;
189f4ede81eSAmarnath Valluri     }
190f4ede81eSAmarnath Valluri 
191f4ede81eSAmarnath Valluri     return 0;
192f4ede81eSAmarnath Valluri }
193f4ede81eSAmarnath Valluri 
194c106ede9SMarc-André Lureau static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number,
195c106ede9SMarc-André Lureau                                      Error **errp)
196f4ede81eSAmarnath Valluri {
197f4ede81eSAmarnath Valluri     ptm_loc loc;
198f4ede81eSAmarnath Valluri 
199f4ede81eSAmarnath Valluri     if (tpm_emu->cur_locty_number == locty_number) {
200f4ede81eSAmarnath Valluri         return 0;
201f4ede81eSAmarnath Valluri     }
202f4ede81eSAmarnath Valluri 
2039d9dcd96SStefan Berger     trace_tpm_emulator_set_locality(locty_number);
2049d9dcd96SStefan Berger 
205eff1fe9fSStefan Berger     memset(&loc, 0, sizeof(loc));
206f4ede81eSAmarnath Valluri     loc.u.req.loc = locty_number;
20717b1af77SMarc-André Lureau     if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_LOCALITY, &loc,
208f4ede81eSAmarnath Valluri                              sizeof(loc), sizeof(loc)) < 0) {
209c106ede9SMarc-André Lureau         error_setg(errp, "tpm-emulator: could not set locality : %s",
210f4ede81eSAmarnath Valluri                    strerror(errno));
211f4ede81eSAmarnath Valluri         return -1;
212f4ede81eSAmarnath Valluri     }
213f4ede81eSAmarnath Valluri 
214f4ede81eSAmarnath Valluri     loc.u.resp.tpm_result = be32_to_cpu(loc.u.resp.tpm_result);
215f4ede81eSAmarnath Valluri     if (loc.u.resp.tpm_result != 0) {
216c106ede9SMarc-André Lureau         error_setg(errp, "tpm-emulator: TPM result for set locality : 0x%x",
217f4ede81eSAmarnath Valluri                    loc.u.resp.tpm_result);
218f4ede81eSAmarnath Valluri         return -1;
219f4ede81eSAmarnath Valluri     }
220f4ede81eSAmarnath Valluri 
221f4ede81eSAmarnath Valluri     tpm_emu->cur_locty_number = locty_number;
222f4ede81eSAmarnath Valluri 
223f4ede81eSAmarnath Valluri     return 0;
224f4ede81eSAmarnath Valluri }
225f4ede81eSAmarnath Valluri 
2266a8a2354SMarc-André Lureau static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd,
2276a8a2354SMarc-André Lureau                                         Error **errp)
228f4ede81eSAmarnath Valluri {
229f4ede81eSAmarnath Valluri     TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
230f4ede81eSAmarnath Valluri 
2319d9dcd96SStefan Berger     trace_tpm_emulator_handle_request();
232f4ede81eSAmarnath Valluri 
2336a8a2354SMarc-André Lureau     if (tpm_emulator_set_locality(tpm_emu, cmd->locty, errp) < 0 ||
2346a8a2354SMarc-André Lureau         tpm_emulator_unix_tx_bufs(tpm_emu, cmd->in, cmd->in_len,
2350e43b7e6SMarc-André Lureau                                   cmd->out, cmd->out_len,
2366a8a2354SMarc-André Lureau                                   &cmd->selftest_done, errp) < 0) {
2370e43b7e6SMarc-André Lureau         tpm_util_write_fatal_error_response(cmd->out, cmd->out_len);
2386a8a2354SMarc-André Lureau     }
239f4ede81eSAmarnath Valluri }
240f4ede81eSAmarnath Valluri 
241f4ede81eSAmarnath Valluri static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu)
242f4ede81eSAmarnath Valluri {
24317b1af77SMarc-André Lureau     if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_CAPABILITY,
244f4ede81eSAmarnath Valluri                              &tpm_emu->caps, 0, sizeof(tpm_emu->caps)) < 0) {
245f4ede81eSAmarnath Valluri         error_report("tpm-emulator: probing failed : %s", strerror(errno));
246f4ede81eSAmarnath Valluri         return -1;
247f4ede81eSAmarnath Valluri     }
248f4ede81eSAmarnath Valluri 
249f4ede81eSAmarnath Valluri     tpm_emu->caps = be64_to_cpu(tpm_emu->caps);
250f4ede81eSAmarnath Valluri 
2519d9dcd96SStefan Berger     trace_tpm_emulator_probe_caps(tpm_emu->caps);
252f4ede81eSAmarnath Valluri 
253f4ede81eSAmarnath Valluri     return 0;
254f4ede81eSAmarnath Valluri }
255f4ede81eSAmarnath Valluri 
256f4ede81eSAmarnath Valluri static int tpm_emulator_check_caps(TPMEmulator *tpm_emu)
257f4ede81eSAmarnath Valluri {
258f4ede81eSAmarnath Valluri     ptm_cap caps = 0;
259f4ede81eSAmarnath Valluri     const char *tpm = NULL;
260f4ede81eSAmarnath Valluri 
261f4ede81eSAmarnath Valluri     /* check for min. required capabilities */
262f4ede81eSAmarnath Valluri     switch (tpm_emu->tpm_version) {
263f4ede81eSAmarnath Valluri     case TPM_VERSION_1_2:
264f4ede81eSAmarnath Valluri         caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED |
2659375c44fSStefan Berger                PTM_CAP_SET_LOCALITY | PTM_CAP_SET_DATAFD | PTM_CAP_STOP |
2669375c44fSStefan Berger                PTM_CAP_SET_BUFFERSIZE;
267f4ede81eSAmarnath Valluri         tpm = "1.2";
268f4ede81eSAmarnath Valluri         break;
269f4ede81eSAmarnath Valluri     case TPM_VERSION_2_0:
270f4ede81eSAmarnath Valluri         caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED |
271f4ede81eSAmarnath Valluri                PTM_CAP_SET_LOCALITY | PTM_CAP_RESET_TPMESTABLISHED |
2729375c44fSStefan Berger                PTM_CAP_SET_DATAFD | PTM_CAP_STOP | PTM_CAP_SET_BUFFERSIZE;
273f4ede81eSAmarnath Valluri         tpm = "2";
274f4ede81eSAmarnath Valluri         break;
275f4ede81eSAmarnath Valluri     case TPM_VERSION_UNSPEC:
276f4ede81eSAmarnath Valluri         error_report("tpm-emulator: TPM version has not been set");
277f4ede81eSAmarnath Valluri         return -1;
278f4ede81eSAmarnath Valluri     }
279f4ede81eSAmarnath Valluri 
280f4ede81eSAmarnath Valluri     if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) {
281f4ede81eSAmarnath Valluri         error_report("tpm-emulator: TPM does not implement minimum set of "
282f4ede81eSAmarnath Valluri                      "required capabilities for TPM %s (0x%x)", tpm, (int)caps);
283f4ede81eSAmarnath Valluri         return -1;
284f4ede81eSAmarnath Valluri     }
285f4ede81eSAmarnath Valluri 
286f4ede81eSAmarnath Valluri     return 0;
287f4ede81eSAmarnath Valluri }
288f4ede81eSAmarnath Valluri 
2899375c44fSStefan Berger static int tpm_emulator_stop_tpm(TPMBackend *tb)
2909375c44fSStefan Berger {
2919375c44fSStefan Berger     TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
2929375c44fSStefan Berger     ptm_res res;
2939375c44fSStefan Berger 
2949375c44fSStefan Berger     if (tpm_emulator_ctrlcmd(tpm_emu, CMD_STOP, &res, 0, sizeof(res)) < 0) {
2959375c44fSStefan Berger         error_report("tpm-emulator: Could not stop TPM: %s",
2969375c44fSStefan Berger                      strerror(errno));
2979375c44fSStefan Berger         return -1;
2989375c44fSStefan Berger     }
2999375c44fSStefan Berger 
3009375c44fSStefan Berger     res = be32_to_cpu(res);
3019375c44fSStefan Berger     if (res) {
3027e095e84SStefan Berger         error_report("tpm-emulator: TPM result for CMD_STOP: 0x%x %s", res,
3037e095e84SStefan Berger                      tpm_emulator_strerror(res));
3049375c44fSStefan Berger         return -1;
3059375c44fSStefan Berger     }
3069375c44fSStefan Berger 
3079375c44fSStefan Berger     return 0;
3089375c44fSStefan Berger }
3099375c44fSStefan Berger 
310*99bdcd2cSStefan Berger static int tpm_emulator_lock_storage(TPMEmulator *tpm_emu)
311*99bdcd2cSStefan Berger {
312*99bdcd2cSStefan Berger     ptm_lockstorage pls;
313*99bdcd2cSStefan Berger 
314*99bdcd2cSStefan Berger     if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, PTM_CAP_LOCK_STORAGE)) {
315*99bdcd2cSStefan Berger         trace_tpm_emulator_lock_storage_cmd_not_supt();
316*99bdcd2cSStefan Berger         return 0;
317*99bdcd2cSStefan Berger     }
318*99bdcd2cSStefan Berger 
319*99bdcd2cSStefan Berger     /* give failing side 300 * 10ms time to release lock */
320*99bdcd2cSStefan Berger     pls.u.req.retries = cpu_to_be32(300);
321*99bdcd2cSStefan Berger     if (tpm_emulator_ctrlcmd(tpm_emu, CMD_LOCK_STORAGE, &pls,
322*99bdcd2cSStefan Berger                              sizeof(pls.u.req), sizeof(pls.u.resp)) < 0) {
323*99bdcd2cSStefan Berger         error_report("tpm-emulator: Could not lock storage within 3 seconds: "
324*99bdcd2cSStefan Berger                      "%s", strerror(errno));
325*99bdcd2cSStefan Berger         return -1;
326*99bdcd2cSStefan Berger     }
327*99bdcd2cSStefan Berger 
328*99bdcd2cSStefan Berger     pls.u.resp.tpm_result = be32_to_cpu(pls.u.resp.tpm_result);
329*99bdcd2cSStefan Berger     if (pls.u.resp.tpm_result != 0) {
330*99bdcd2cSStefan Berger         error_report("tpm-emulator: TPM result for CMD_LOCK_STORAGE: 0x%x %s",
331*99bdcd2cSStefan Berger                      pls.u.resp.tpm_result,
332*99bdcd2cSStefan Berger                      tpm_emulator_strerror(pls.u.resp.tpm_result));
333*99bdcd2cSStefan Berger         return -1;
334*99bdcd2cSStefan Berger     }
335*99bdcd2cSStefan Berger 
336*99bdcd2cSStefan Berger     return 0;
337*99bdcd2cSStefan Berger }
338*99bdcd2cSStefan Berger 
3399375c44fSStefan Berger static int tpm_emulator_set_buffer_size(TPMBackend *tb,
3409375c44fSStefan Berger                                         size_t wanted_size,
3419375c44fSStefan Berger                                         size_t *actual_size)
3429375c44fSStefan Berger {
3439375c44fSStefan Berger     TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
3449375c44fSStefan Berger     ptm_setbuffersize psbs;
3459375c44fSStefan Berger 
3469375c44fSStefan Berger     if (tpm_emulator_stop_tpm(tb) < 0) {
3479375c44fSStefan Berger         return -1;
3489375c44fSStefan Berger     }
3499375c44fSStefan Berger 
3509375c44fSStefan Berger     psbs.u.req.buffersize = cpu_to_be32(wanted_size);
3519375c44fSStefan Berger 
3529375c44fSStefan Berger     if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_BUFFERSIZE, &psbs,
3539375c44fSStefan Berger                              sizeof(psbs.u.req), sizeof(psbs.u.resp)) < 0) {
3549375c44fSStefan Berger         error_report("tpm-emulator: Could not set buffer size: %s",
3559375c44fSStefan Berger                      strerror(errno));
3569375c44fSStefan Berger         return -1;
3579375c44fSStefan Berger     }
3589375c44fSStefan Berger 
3599375c44fSStefan Berger     psbs.u.resp.tpm_result = be32_to_cpu(psbs.u.resp.tpm_result);
3609375c44fSStefan Berger     if (psbs.u.resp.tpm_result != 0) {
3617e095e84SStefan Berger         error_report("tpm-emulator: TPM result for set buffer size : 0x%x %s",
3627e095e84SStefan Berger                      psbs.u.resp.tpm_result,
3637e095e84SStefan Berger                      tpm_emulator_strerror(psbs.u.resp.tpm_result));
3649375c44fSStefan Berger         return -1;
3659375c44fSStefan Berger     }
3669375c44fSStefan Berger 
3679375c44fSStefan Berger     if (actual_size) {
3689375c44fSStefan Berger         *actual_size = be32_to_cpu(psbs.u.resp.buffersize);
3699375c44fSStefan Berger     }
3709375c44fSStefan Berger 
3719d9dcd96SStefan Berger     trace_tpm_emulator_set_buffer_size(
3729375c44fSStefan Berger             be32_to_cpu(psbs.u.resp.buffersize),
3739375c44fSStefan Berger             be32_to_cpu(psbs.u.resp.minsize),
3749375c44fSStefan Berger             be32_to_cpu(psbs.u.resp.maxsize));
3759375c44fSStefan Berger 
3769375c44fSStefan Berger     return 0;
3779375c44fSStefan Berger }
3789375c44fSStefan Berger 
37938ab74e7SStefan Berger static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize,
38038ab74e7SStefan Berger                                      bool is_resume)
381f4ede81eSAmarnath Valluri {
382f4ede81eSAmarnath Valluri     TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
38330270587SStefan Berger     ptm_init init = {
38430270587SStefan Berger         .u.req.init_flags = 0,
38530270587SStefan Berger     };
386f4ede81eSAmarnath Valluri     ptm_res res;
387f4ede81eSAmarnath Valluri 
38838ab74e7SStefan Berger     trace_tpm_emulator_startup_tpm_resume(is_resume, buffersize);
38938ab74e7SStefan Berger 
3909375c44fSStefan Berger     if (buffersize != 0 &&
3919375c44fSStefan Berger         tpm_emulator_set_buffer_size(tb, buffersize, NULL) < 0) {
3929375c44fSStefan Berger         goto err_exit;
3939375c44fSStefan Berger     }
3949375c44fSStefan Berger 
39538ab74e7SStefan Berger     if (is_resume) {
39638ab74e7SStefan Berger         init.u.req.init_flags |= cpu_to_be32(PTM_INIT_FLAG_DELETE_VOLATILE);
39738ab74e7SStefan Berger     }
39838ab74e7SStefan Berger 
39917b1af77SMarc-André Lureau     if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init),
400f4ede81eSAmarnath Valluri                              sizeof(init)) < 0) {
401f4ede81eSAmarnath Valluri         error_report("tpm-emulator: could not send INIT: %s",
402f4ede81eSAmarnath Valluri                      strerror(errno));
403f4ede81eSAmarnath Valluri         goto err_exit;
404f4ede81eSAmarnath Valluri     }
405f4ede81eSAmarnath Valluri 
406f4ede81eSAmarnath Valluri     res = be32_to_cpu(init.u.resp.tpm_result);
407f4ede81eSAmarnath Valluri     if (res) {
4087e095e84SStefan Berger         error_report("tpm-emulator: TPM result for CMD_INIT: 0x%x %s", res,
4097e095e84SStefan Berger                      tpm_emulator_strerror(res));
410f4ede81eSAmarnath Valluri         goto err_exit;
411f4ede81eSAmarnath Valluri     }
412f4ede81eSAmarnath Valluri     return 0;
413f4ede81eSAmarnath Valluri 
414f4ede81eSAmarnath Valluri err_exit:
415f4ede81eSAmarnath Valluri     return -1;
416f4ede81eSAmarnath Valluri }
417f4ede81eSAmarnath Valluri 
41838ab74e7SStefan Berger static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize)
41938ab74e7SStefan Berger {
420a0bcec03SRoss Lagerwall     /* TPM startup will be done from post_load hook */
421a0bcec03SRoss Lagerwall     if (runstate_check(RUN_STATE_INMIGRATE)) {
422a0bcec03SRoss Lagerwall         if (buffersize != 0) {
423a0bcec03SRoss Lagerwall             return tpm_emulator_set_buffer_size(tb, buffersize, NULL);
424a0bcec03SRoss Lagerwall         }
425a0bcec03SRoss Lagerwall 
426a0bcec03SRoss Lagerwall         return 0;
427a0bcec03SRoss Lagerwall     }
428a0bcec03SRoss Lagerwall 
42938ab74e7SStefan Berger     return tpm_emulator_startup_tpm_resume(tb, buffersize, false);
43038ab74e7SStefan Berger }
43138ab74e7SStefan Berger 
432f4ede81eSAmarnath Valluri static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb)
433f4ede81eSAmarnath Valluri {
434f4ede81eSAmarnath Valluri     TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
435f4ede81eSAmarnath Valluri     ptm_est est;
436f4ede81eSAmarnath Valluri 
4370b4c7c65SStefan Berger     if (tpm_emu->established_flag_cached) {
4380b4c7c65SStefan Berger         return tpm_emu->established_flag;
4390b4c7c65SStefan Berger     }
4400b4c7c65SStefan Berger 
44117b1af77SMarc-André Lureau     if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_TPMESTABLISHED, &est,
442f4ede81eSAmarnath Valluri                              0, sizeof(est)) < 0) {
443f4ede81eSAmarnath Valluri         error_report("tpm-emulator: Could not get the TPM established flag: %s",
444f4ede81eSAmarnath Valluri                      strerror(errno));
445f4ede81eSAmarnath Valluri         return false;
446f4ede81eSAmarnath Valluri     }
4479d9dcd96SStefan Berger     trace_tpm_emulator_get_tpm_established_flag(est.u.resp.bit);
448f4ede81eSAmarnath Valluri 
4490b4c7c65SStefan Berger     tpm_emu->established_flag_cached = 1;
4500b4c7c65SStefan Berger     tpm_emu->established_flag = (est.u.resp.bit != 0);
4510b4c7c65SStefan Berger 
4520b4c7c65SStefan Berger     return tpm_emu->established_flag;
453f4ede81eSAmarnath Valluri }
454f4ede81eSAmarnath Valluri 
455f4ede81eSAmarnath Valluri static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb,
456f4ede81eSAmarnath Valluri                                                    uint8_t locty)
457f4ede81eSAmarnath Valluri {
458f4ede81eSAmarnath Valluri     TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
459f4ede81eSAmarnath Valluri     ptm_reset_est reset_est;
460f4ede81eSAmarnath Valluri     ptm_res res;
461f4ede81eSAmarnath Valluri 
462f4ede81eSAmarnath Valluri     /* only a TPM 2.0 will support this */
463f4ede81eSAmarnath Valluri     if (tpm_emu->tpm_version != TPM_VERSION_2_0) {
464f4ede81eSAmarnath Valluri         return 0;
465f4ede81eSAmarnath Valluri     }
466f4ede81eSAmarnath Valluri 
467f4ede81eSAmarnath Valluri     reset_est.u.req.loc = tpm_emu->cur_locty_number;
46817b1af77SMarc-André Lureau     if (tpm_emulator_ctrlcmd(tpm_emu, CMD_RESET_TPMESTABLISHED,
469f4ede81eSAmarnath Valluri                              &reset_est, sizeof(reset_est),
470f4ede81eSAmarnath Valluri                              sizeof(reset_est)) < 0) {
471f4ede81eSAmarnath Valluri         error_report("tpm-emulator: Could not reset the establishment bit: %s",
472f4ede81eSAmarnath Valluri                      strerror(errno));
473f4ede81eSAmarnath Valluri         return -1;
474f4ede81eSAmarnath Valluri     }
475f4ede81eSAmarnath Valluri 
476f4ede81eSAmarnath Valluri     res = be32_to_cpu(reset_est.u.resp.tpm_result);
477f4ede81eSAmarnath Valluri     if (res) {
4787e095e84SStefan Berger         error_report(
4797e095e84SStefan Berger             "tpm-emulator: TPM result for rest established flag: 0x%x %s",
4807e095e84SStefan Berger             res, tpm_emulator_strerror(res));
481f4ede81eSAmarnath Valluri         return -1;
482f4ede81eSAmarnath Valluri     }
483f4ede81eSAmarnath Valluri 
4840b4c7c65SStefan Berger     tpm_emu->established_flag_cached = 0;
4850b4c7c65SStefan Berger 
486f4ede81eSAmarnath Valluri     return 0;
487f4ede81eSAmarnath Valluri }
488f4ede81eSAmarnath Valluri 
489f4ede81eSAmarnath Valluri static void tpm_emulator_cancel_cmd(TPMBackend *tb)
490f4ede81eSAmarnath Valluri {
491f4ede81eSAmarnath Valluri     TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
492f4ede81eSAmarnath Valluri     ptm_res res;
493f4ede81eSAmarnath Valluri 
494f4ede81eSAmarnath Valluri     if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, PTM_CAP_CANCEL_TPM_CMD)) {
4959d9dcd96SStefan Berger         trace_tpm_emulator_cancel_cmd_not_supt();
496f4ede81eSAmarnath Valluri         return;
497f4ede81eSAmarnath Valluri     }
498f4ede81eSAmarnath Valluri 
4993d011411SMarc-André Lureau     /* FIXME: make the function non-blocking, or it may block a VCPU */
50017b1af77SMarc-André Lureau     if (tpm_emulator_ctrlcmd(tpm_emu, CMD_CANCEL_TPM_CMD, &res, 0,
501f4ede81eSAmarnath Valluri                              sizeof(res)) < 0) {
502f4ede81eSAmarnath Valluri         error_report("tpm-emulator: Could not cancel command: %s",
503f4ede81eSAmarnath Valluri                      strerror(errno));
504f4ede81eSAmarnath Valluri     } else if (res != 0) {
505f4ede81eSAmarnath Valluri         error_report("tpm-emulator: Failed to cancel TPM: 0x%x",
506f4ede81eSAmarnath Valluri                      be32_to_cpu(res));
507f4ede81eSAmarnath Valluri     }
508f4ede81eSAmarnath Valluri }
509f4ede81eSAmarnath Valluri 
510f4ede81eSAmarnath Valluri static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb)
511f4ede81eSAmarnath Valluri {
512f4ede81eSAmarnath Valluri     TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
513f4ede81eSAmarnath Valluri 
514f4ede81eSAmarnath Valluri     return tpm_emu->tpm_version;
515f4ede81eSAmarnath Valluri }
516f4ede81eSAmarnath Valluri 
517b21e6aafSStefan Berger static size_t tpm_emulator_get_buffer_size(TPMBackend *tb)
518b21e6aafSStefan Berger {
5199375c44fSStefan Berger     size_t actual_size;
5209375c44fSStefan Berger 
5219375c44fSStefan Berger     if (tpm_emulator_set_buffer_size(tb, 0, &actual_size) < 0) {
522b21e6aafSStefan Berger         return 4096;
523b21e6aafSStefan Berger     }
524b21e6aafSStefan Berger 
5259375c44fSStefan Berger     return actual_size;
5269375c44fSStefan Berger }
5279375c44fSStefan Berger 
528f4ede81eSAmarnath Valluri static int tpm_emulator_block_migration(TPMEmulator *tpm_emu)
529f4ede81eSAmarnath Valluri {
530f4ede81eSAmarnath Valluri     Error *err = NULL;
53138ab74e7SStefan Berger     ptm_cap caps = PTM_CAP_GET_STATEBLOB | PTM_CAP_SET_STATEBLOB |
53238ab74e7SStefan Berger                    PTM_CAP_STOP;
533f4ede81eSAmarnath Valluri 
53438ab74e7SStefan Berger     if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) {
535f4ede81eSAmarnath Valluri         error_setg(&tpm_emu->migration_blocker,
53638ab74e7SStefan Berger                    "Migration disabled: TPM emulator does not support "
53738ab74e7SStefan Berger                    "migration");
538436c831aSMarkus Armbruster         if (migrate_add_blocker(tpm_emu->migration_blocker, &err) < 0) {
539f4ede81eSAmarnath Valluri             error_report_err(err);
540f4ede81eSAmarnath Valluri             error_free(tpm_emu->migration_blocker);
541f4ede81eSAmarnath Valluri             tpm_emu->migration_blocker = NULL;
542f4ede81eSAmarnath Valluri 
543f4ede81eSAmarnath Valluri             return -1;
544f4ede81eSAmarnath Valluri         }
54538ab74e7SStefan Berger     }
546f4ede81eSAmarnath Valluri 
547f4ede81eSAmarnath Valluri     return 0;
548f4ede81eSAmarnath Valluri }
549f4ede81eSAmarnath Valluri 
550f4ede81eSAmarnath Valluri static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu)
551f4ede81eSAmarnath Valluri {
552f4ede81eSAmarnath Valluri     ptm_res res;
553f4ede81eSAmarnath Valluri     Error *err = NULL;
554f4ede81eSAmarnath Valluri     int fds[2] = { -1, -1 };
555f4ede81eSAmarnath Valluri 
556f4ede81eSAmarnath Valluri     if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
557f4ede81eSAmarnath Valluri         error_report("tpm-emulator: Failed to create socketpair");
558f4ede81eSAmarnath Valluri         return -1;
559f4ede81eSAmarnath Valluri     }
560f4ede81eSAmarnath Valluri 
561f4ede81eSAmarnath Valluri     qemu_chr_fe_set_msgfds(&tpm_emu->ctrl_chr, fds + 1, 1);
562f4ede81eSAmarnath Valluri 
56317b1af77SMarc-André Lureau     if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_DATAFD, &res, 0,
56417b1af77SMarc-André Lureau                              sizeof(res)) < 0 || res != 0) {
565f4ede81eSAmarnath Valluri         error_report("tpm-emulator: Failed to send CMD_SET_DATAFD: %s",
566f4ede81eSAmarnath Valluri                      strerror(errno));
567f4ede81eSAmarnath Valluri         goto err_exit;
568f4ede81eSAmarnath Valluri     }
569f4ede81eSAmarnath Valluri 
570f4ede81eSAmarnath Valluri     tpm_emu->data_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err));
571f4ede81eSAmarnath Valluri     if (err) {
572f4ede81eSAmarnath Valluri         error_prepend(&err, "tpm-emulator: Failed to create io channel: ");
573f4ede81eSAmarnath Valluri         error_report_err(err);
574f4ede81eSAmarnath Valluri         goto err_exit;
575f4ede81eSAmarnath Valluri     }
576f4ede81eSAmarnath Valluri 
577f4ede81eSAmarnath Valluri     closesocket(fds[1]);
578f4ede81eSAmarnath Valluri 
579f4ede81eSAmarnath Valluri     return 0;
580f4ede81eSAmarnath Valluri 
581f4ede81eSAmarnath Valluri err_exit:
582f4ede81eSAmarnath Valluri     closesocket(fds[0]);
583f4ede81eSAmarnath Valluri     closesocket(fds[1]);
584f4ede81eSAmarnath Valluri     return -1;
585f4ede81eSAmarnath Valluri }
586f4ede81eSAmarnath Valluri 
587f4ede81eSAmarnath Valluri static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts)
588f4ede81eSAmarnath Valluri {
589f4ede81eSAmarnath Valluri     const char *value;
59088f83074SStefan Berger     Error *err = NULL;
59188f83074SStefan Berger     Chardev *dev;
592f4ede81eSAmarnath Valluri 
593f4ede81eSAmarnath Valluri     value = qemu_opt_get(opts, "chardev");
59488f83074SStefan Berger     if (!value) {
59588f83074SStefan Berger         error_report("tpm-emulator: parameter 'chardev' is missing");
59688f83074SStefan Berger         goto err;
59788f83074SStefan Berger     }
598f4ede81eSAmarnath Valluri 
59988f83074SStefan Berger     dev = qemu_chr_find(value);
600f4ede81eSAmarnath Valluri     if (!dev) {
60188f83074SStefan Berger         error_report("tpm-emulator: tpm chardev '%s' not found", value);
602f4ede81eSAmarnath Valluri         goto err;
603f4ede81eSAmarnath Valluri     }
604f4ede81eSAmarnath Valluri 
605f4ede81eSAmarnath Valluri     if (!qemu_chr_fe_init(&tpm_emu->ctrl_chr, dev, &err)) {
606f4ede81eSAmarnath Valluri         error_prepend(&err, "tpm-emulator: No valid chardev found at '%s':",
607f4ede81eSAmarnath Valluri                       value);
608f4ede81eSAmarnath Valluri         error_report_err(err);
609f4ede81eSAmarnath Valluri         goto err;
610f4ede81eSAmarnath Valluri     }
611f4ede81eSAmarnath Valluri 
612f4ede81eSAmarnath Valluri     tpm_emu->options->chardev = g_strdup(value);
613f4ede81eSAmarnath Valluri 
614f4ede81eSAmarnath Valluri     if (tpm_emulator_prepare_data_fd(tpm_emu) < 0) {
615f4ede81eSAmarnath Valluri         goto err;
616f4ede81eSAmarnath Valluri     }
617f4ede81eSAmarnath Valluri 
618f4ede81eSAmarnath Valluri     /* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as it also used
619f4ede81eSAmarnath Valluri      * by passthrough driver, which not yet using GIOChannel.
620f4ede81eSAmarnath Valluri      */
621f4ede81eSAmarnath Valluri     if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_emu->data_ioc)->fd,
622f4ede81eSAmarnath Valluri                              &tpm_emu->tpm_version)) {
623f4ede81eSAmarnath Valluri         error_report("'%s' is not emulating TPM device. Error: %s",
624f4ede81eSAmarnath Valluri                       tpm_emu->options->chardev, strerror(errno));
625f4ede81eSAmarnath Valluri         goto err;
626f4ede81eSAmarnath Valluri     }
627f4ede81eSAmarnath Valluri 
6289d9dcd96SStefan Berger     switch (tpm_emu->tpm_version) {
6299d9dcd96SStefan Berger     case TPM_VERSION_1_2:
6309d9dcd96SStefan Berger         trace_tpm_emulator_handle_device_opts_tpm12();
6319d9dcd96SStefan Berger         break;
6329d9dcd96SStefan Berger     case TPM_VERSION_2_0:
6339d9dcd96SStefan Berger         trace_tpm_emulator_handle_device_opts_tpm2();
6349d9dcd96SStefan Berger         break;
6359d9dcd96SStefan Berger     default:
6369d9dcd96SStefan Berger         trace_tpm_emulator_handle_device_opts_unspec();
6379d9dcd96SStefan Berger     }
638f4ede81eSAmarnath Valluri 
639f4ede81eSAmarnath Valluri     if (tpm_emulator_probe_caps(tpm_emu) ||
640f4ede81eSAmarnath Valluri         tpm_emulator_check_caps(tpm_emu)) {
641f4ede81eSAmarnath Valluri         goto err;
642f4ede81eSAmarnath Valluri     }
643f4ede81eSAmarnath Valluri 
644f4ede81eSAmarnath Valluri     return tpm_emulator_block_migration(tpm_emu);
645f4ede81eSAmarnath Valluri 
646f4ede81eSAmarnath Valluri err:
6479d9dcd96SStefan Berger     trace_tpm_emulator_handle_device_opts_startup_error();
6489d9dcd96SStefan Berger 
649f4ede81eSAmarnath Valluri     return -1;
650f4ede81eSAmarnath Valluri }
651f4ede81eSAmarnath Valluri 
6529f7c0ef2SMarc-André Lureau static TPMBackend *tpm_emulator_create(QemuOpts *opts)
653f4ede81eSAmarnath Valluri {
654f4ede81eSAmarnath Valluri     TPMBackend *tb = TPM_BACKEND(object_new(TYPE_TPM_EMULATOR));
655f4ede81eSAmarnath Valluri 
656f4ede81eSAmarnath Valluri     if (tpm_emulator_handle_device_opts(TPM_EMULATOR(tb), opts)) {
6579f7c0ef2SMarc-André Lureau         object_unref(OBJECT(tb));
6589f7c0ef2SMarc-André Lureau         return NULL;
659f4ede81eSAmarnath Valluri     }
660f4ede81eSAmarnath Valluri 
661f4ede81eSAmarnath Valluri     return tb;
662f4ede81eSAmarnath Valluri }
663f4ede81eSAmarnath Valluri 
664f4ede81eSAmarnath Valluri static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb)
665f4ede81eSAmarnath Valluri {
666f4ede81eSAmarnath Valluri     TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
667f4ede81eSAmarnath Valluri     TpmTypeOptions *options = g_new0(TpmTypeOptions, 1);
668f4ede81eSAmarnath Valluri 
66939dc3e4aSMarkus Armbruster     options->type = TPM_TYPE_EMULATOR;
670f4ede81eSAmarnath Valluri     options->u.emulator.data = QAPI_CLONE(TPMEmulatorOptions, tpm_emu->options);
671f4ede81eSAmarnath Valluri 
672f4ede81eSAmarnath Valluri     return options;
673f4ede81eSAmarnath Valluri }
674f4ede81eSAmarnath Valluri 
675f4ede81eSAmarnath Valluri static const QemuOptDesc tpm_emulator_cmdline_opts[] = {
676f4ede81eSAmarnath Valluri     TPM_STANDARD_CMDLINE_OPTS,
677f4ede81eSAmarnath Valluri     {
678f4ede81eSAmarnath Valluri         .name = "chardev",
679f4ede81eSAmarnath Valluri         .type = QEMU_OPT_STRING,
680f4ede81eSAmarnath Valluri         .help = "Character device to use for out-of-band control messages",
681f4ede81eSAmarnath Valluri     },
682f4ede81eSAmarnath Valluri     { /* end of list */ },
683f4ede81eSAmarnath Valluri };
684f4ede81eSAmarnath Valluri 
68538ab74e7SStefan Berger /*
68638ab74e7SStefan Berger  * Transfer a TPM state blob from the TPM into a provided buffer.
68738ab74e7SStefan Berger  *
68838ab74e7SStefan Berger  * @tpm_emu: TPMEmulator
68938ab74e7SStefan Berger  * @type: the type of blob to transfer
69038ab74e7SStefan Berger  * @tsb: the TPMSizeBuffer to fill with the blob
69138ab74e7SStefan Berger  * @flags: the flags to return to the caller
69238ab74e7SStefan Berger  */
69338ab74e7SStefan Berger static int tpm_emulator_get_state_blob(TPMEmulator *tpm_emu,
69438ab74e7SStefan Berger                                        uint8_t type,
69538ab74e7SStefan Berger                                        TPMSizedBuffer *tsb,
69638ab74e7SStefan Berger                                        uint32_t *flags)
69738ab74e7SStefan Berger {
69838ab74e7SStefan Berger     ptm_getstate pgs;
69938ab74e7SStefan Berger     ptm_res res;
70038ab74e7SStefan Berger     ssize_t n;
70138ab74e7SStefan Berger     uint32_t totlength, length;
70238ab74e7SStefan Berger 
70338ab74e7SStefan Berger     tpm_sized_buffer_reset(tsb);
70438ab74e7SStefan Berger 
70538ab74e7SStefan Berger     pgs.u.req.state_flags = cpu_to_be32(PTM_STATE_FLAG_DECRYPTED);
70638ab74e7SStefan Berger     pgs.u.req.type = cpu_to_be32(type);
70738ab74e7SStefan Berger     pgs.u.req.offset = 0;
70838ab74e7SStefan Berger 
70938ab74e7SStefan Berger     if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_STATEBLOB,
71038ab74e7SStefan Berger                              &pgs, sizeof(pgs.u.req),
71138ab74e7SStefan Berger                              offsetof(ptm_getstate, u.resp.data)) < 0) {
71238ab74e7SStefan Berger         error_report("tpm-emulator: could not get state blob type %d : %s",
71338ab74e7SStefan Berger                      type, strerror(errno));
71438ab74e7SStefan Berger         return -1;
71538ab74e7SStefan Berger     }
71638ab74e7SStefan Berger 
71738ab74e7SStefan Berger     res = be32_to_cpu(pgs.u.resp.tpm_result);
71838ab74e7SStefan Berger     if (res != 0 && (res & 0x800) == 0) {
71938ab74e7SStefan Berger         error_report("tpm-emulator: Getting the stateblob (type %d) failed "
7207e095e84SStefan Berger                      "with a TPM error 0x%x %s", type, res,
7217e095e84SStefan Berger                      tpm_emulator_strerror(res));
72238ab74e7SStefan Berger         return -1;
72338ab74e7SStefan Berger     }
72438ab74e7SStefan Berger 
72538ab74e7SStefan Berger     totlength = be32_to_cpu(pgs.u.resp.totlength);
72638ab74e7SStefan Berger     length = be32_to_cpu(pgs.u.resp.length);
72738ab74e7SStefan Berger     if (totlength != length) {
72838ab74e7SStefan Berger         error_report("tpm-emulator: Expecting to read %u bytes "
72938ab74e7SStefan Berger                      "but would get %u", totlength, length);
73038ab74e7SStefan Berger         return -1;
73138ab74e7SStefan Berger     }
73238ab74e7SStefan Berger 
73338ab74e7SStefan Berger     *flags = be32_to_cpu(pgs.u.resp.state_flags);
73438ab74e7SStefan Berger 
73538ab74e7SStefan Berger     if (totlength > 0) {
73638ab74e7SStefan Berger         tsb->buffer = g_try_malloc(totlength);
73738ab74e7SStefan Berger         if (!tsb->buffer) {
73838ab74e7SStefan Berger             error_report("tpm-emulator: Out of memory allocating %u bytes",
73938ab74e7SStefan Berger                          totlength);
74038ab74e7SStefan Berger             return -1;
74138ab74e7SStefan Berger         }
74238ab74e7SStefan Berger 
74338ab74e7SStefan Berger         n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr, tsb->buffer, totlength);
74438ab74e7SStefan Berger         if (n != totlength) {
74538ab74e7SStefan Berger             error_report("tpm-emulator: Could not read stateblob (type %d); "
74638ab74e7SStefan Berger                          "expected %u bytes, got %zd",
74738ab74e7SStefan Berger                          type, totlength, n);
74838ab74e7SStefan Berger             return -1;
74938ab74e7SStefan Berger         }
75038ab74e7SStefan Berger     }
75138ab74e7SStefan Berger     tsb->size = totlength;
75238ab74e7SStefan Berger 
75338ab74e7SStefan Berger     trace_tpm_emulator_get_state_blob(type, tsb->size, *flags);
75438ab74e7SStefan Berger 
75538ab74e7SStefan Berger     return 0;
75638ab74e7SStefan Berger }
75738ab74e7SStefan Berger 
75838ab74e7SStefan Berger static int tpm_emulator_get_state_blobs(TPMEmulator *tpm_emu)
75938ab74e7SStefan Berger {
76038ab74e7SStefan Berger     TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs;
76138ab74e7SStefan Berger 
76238ab74e7SStefan Berger     if (tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT,
76338ab74e7SStefan Berger                                     &state_blobs->permanent,
76438ab74e7SStefan Berger                                     &state_blobs->permanent_flags) < 0 ||
76538ab74e7SStefan Berger         tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE,
76638ab74e7SStefan Berger                                     &state_blobs->volatil,
76738ab74e7SStefan Berger                                     &state_blobs->volatil_flags) < 0 ||
76838ab74e7SStefan Berger         tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE,
76938ab74e7SStefan Berger                                     &state_blobs->savestate,
77038ab74e7SStefan Berger                                     &state_blobs->savestate_flags) < 0) {
77138ab74e7SStefan Berger         goto err_exit;
77238ab74e7SStefan Berger     }
77338ab74e7SStefan Berger 
77438ab74e7SStefan Berger     return 0;
77538ab74e7SStefan Berger 
77638ab74e7SStefan Berger  err_exit:
77738ab74e7SStefan Berger     tpm_sized_buffer_reset(&state_blobs->volatil);
77838ab74e7SStefan Berger     tpm_sized_buffer_reset(&state_blobs->permanent);
77938ab74e7SStefan Berger     tpm_sized_buffer_reset(&state_blobs->savestate);
78038ab74e7SStefan Berger 
78138ab74e7SStefan Berger     return -1;
78238ab74e7SStefan Berger }
78338ab74e7SStefan Berger 
78438ab74e7SStefan Berger /*
78538ab74e7SStefan Berger  * Transfer a TPM state blob to the TPM emulator.
78638ab74e7SStefan Berger  *
78738ab74e7SStefan Berger  * @tpm_emu: TPMEmulator
78838ab74e7SStefan Berger  * @type: the type of TPM state blob to transfer
78938ab74e7SStefan Berger  * @tsb: TPMSizedBuffer containing the TPM state blob
79038ab74e7SStefan Berger  * @flags: Flags describing the (encryption) state of the TPM state blob
79138ab74e7SStefan Berger  */
79238ab74e7SStefan Berger static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu,
79338ab74e7SStefan Berger                                        uint32_t type,
79438ab74e7SStefan Berger                                        TPMSizedBuffer *tsb,
79538ab74e7SStefan Berger                                        uint32_t flags)
79638ab74e7SStefan Berger {
79738ab74e7SStefan Berger     ssize_t n;
79838ab74e7SStefan Berger     ptm_setstate pss;
79938ab74e7SStefan Berger     ptm_res tpm_result;
80038ab74e7SStefan Berger 
80138ab74e7SStefan Berger     if (tsb->size == 0) {
80238ab74e7SStefan Berger         return 0;
80338ab74e7SStefan Berger     }
80438ab74e7SStefan Berger 
80538ab74e7SStefan Berger     pss = (ptm_setstate) {
80638ab74e7SStefan Berger         .u.req.state_flags = cpu_to_be32(flags),
80738ab74e7SStefan Berger         .u.req.type = cpu_to_be32(type),
80838ab74e7SStefan Berger         .u.req.length = cpu_to_be32(tsb->size),
80938ab74e7SStefan Berger     };
81038ab74e7SStefan Berger 
81138ab74e7SStefan Berger     /* write the header only */
81238ab74e7SStefan Berger     if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_STATEBLOB, &pss,
81338ab74e7SStefan Berger                              offsetof(ptm_setstate, u.req.data), 0) < 0) {
81438ab74e7SStefan Berger         error_report("tpm-emulator: could not set state blob type %d : %s",
81538ab74e7SStefan Berger                      type, strerror(errno));
81638ab74e7SStefan Berger         return -1;
81738ab74e7SStefan Berger     }
81838ab74e7SStefan Berger 
81938ab74e7SStefan Berger     /* now the body */
82038ab74e7SStefan Berger     n = qemu_chr_fe_write_all(&tpm_emu->ctrl_chr, tsb->buffer, tsb->size);
82138ab74e7SStefan Berger     if (n != tsb->size) {
82238ab74e7SStefan Berger         error_report("tpm-emulator: Writing the stateblob (type %d) "
82338ab74e7SStefan Berger                      "failed; could not write %u bytes, but only %zd",
82438ab74e7SStefan Berger                      type, tsb->size, n);
82538ab74e7SStefan Berger         return -1;
82638ab74e7SStefan Berger     }
82738ab74e7SStefan Berger 
82838ab74e7SStefan Berger     /* now get the result */
82938ab74e7SStefan Berger     n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr,
83038ab74e7SStefan Berger                              (uint8_t *)&pss, sizeof(pss.u.resp));
83138ab74e7SStefan Berger     if (n != sizeof(pss.u.resp)) {
83238ab74e7SStefan Berger         error_report("tpm-emulator: Reading response from writing stateblob "
83338ab74e7SStefan Berger                      "(type %d) failed; expected %zu bytes, got %zd", type,
83438ab74e7SStefan Berger                      sizeof(pss.u.resp), n);
83538ab74e7SStefan Berger         return -1;
83638ab74e7SStefan Berger     }
83738ab74e7SStefan Berger 
83838ab74e7SStefan Berger     tpm_result = be32_to_cpu(pss.u.resp.tpm_result);
83938ab74e7SStefan Berger     if (tpm_result != 0) {
84038ab74e7SStefan Berger         error_report("tpm-emulator: Setting the stateblob (type %d) failed "
8417e095e84SStefan Berger                      "with a TPM error 0x%x %s", type, tpm_result,
8427e095e84SStefan Berger                      tpm_emulator_strerror(tpm_result));
84338ab74e7SStefan Berger         return -1;
84438ab74e7SStefan Berger     }
84538ab74e7SStefan Berger 
84638ab74e7SStefan Berger     trace_tpm_emulator_set_state_blob(type, tsb->size, flags);
84738ab74e7SStefan Berger 
84838ab74e7SStefan Berger     return 0;
84938ab74e7SStefan Berger }
85038ab74e7SStefan Berger 
85138ab74e7SStefan Berger /*
85238ab74e7SStefan Berger  * Set all the TPM state blobs.
85338ab74e7SStefan Berger  *
85438ab74e7SStefan Berger  * Returns a negative errno code in case of error.
85538ab74e7SStefan Berger  */
85638ab74e7SStefan Berger static int tpm_emulator_set_state_blobs(TPMBackend *tb)
85738ab74e7SStefan Berger {
85838ab74e7SStefan Berger     TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
85938ab74e7SStefan Berger     TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs;
86038ab74e7SStefan Berger 
86138ab74e7SStefan Berger     trace_tpm_emulator_set_state_blobs();
86238ab74e7SStefan Berger 
86338ab74e7SStefan Berger     if (tpm_emulator_stop_tpm(tb) < 0) {
86438ab74e7SStefan Berger         trace_tpm_emulator_set_state_blobs_error("Could not stop TPM");
86538ab74e7SStefan Berger         return -EIO;
86638ab74e7SStefan Berger     }
86738ab74e7SStefan Berger 
86838ab74e7SStefan Berger     if (tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT,
86938ab74e7SStefan Berger                                     &state_blobs->permanent,
87038ab74e7SStefan Berger                                     state_blobs->permanent_flags) < 0 ||
87138ab74e7SStefan Berger         tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE,
87238ab74e7SStefan Berger                                     &state_blobs->volatil,
87338ab74e7SStefan Berger                                     state_blobs->volatil_flags) < 0 ||
87438ab74e7SStefan Berger         tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE,
87538ab74e7SStefan Berger                                     &state_blobs->savestate,
87638ab74e7SStefan Berger                                     state_blobs->savestate_flags) < 0) {
87738ab74e7SStefan Berger         return -EIO;
87838ab74e7SStefan Berger     }
87938ab74e7SStefan Berger 
88038ab74e7SStefan Berger     trace_tpm_emulator_set_state_blobs_done();
88138ab74e7SStefan Berger 
88238ab74e7SStefan Berger     return 0;
88338ab74e7SStefan Berger }
88438ab74e7SStefan Berger 
88538ab74e7SStefan Berger static int tpm_emulator_pre_save(void *opaque)
88638ab74e7SStefan Berger {
88738ab74e7SStefan Berger     TPMBackend *tb = opaque;
88838ab74e7SStefan Berger     TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
889*99bdcd2cSStefan Berger     int ret;
89038ab74e7SStefan Berger 
89138ab74e7SStefan Berger     trace_tpm_emulator_pre_save();
89238ab74e7SStefan Berger 
89338ab74e7SStefan Berger     tpm_backend_finish_sync(tb);
89438ab74e7SStefan Berger 
89538ab74e7SStefan Berger     /* get the state blobs from the TPM */
896*99bdcd2cSStefan Berger     ret = tpm_emulator_get_state_blobs(tpm_emu);
897*99bdcd2cSStefan Berger 
898*99bdcd2cSStefan Berger     tpm_emu->relock_storage = ret == 0;
899*99bdcd2cSStefan Berger 
900*99bdcd2cSStefan Berger     return ret;
901*99bdcd2cSStefan Berger }
902*99bdcd2cSStefan Berger 
903*99bdcd2cSStefan Berger static void tpm_emulator_vm_state_change(void *opaque, bool running,
904*99bdcd2cSStefan Berger                                          RunState state)
905*99bdcd2cSStefan Berger {
906*99bdcd2cSStefan Berger     TPMBackend *tb = opaque;
907*99bdcd2cSStefan Berger     TPMEmulator *tpm_emu = TPM_EMULATOR(tb);
908*99bdcd2cSStefan Berger 
909*99bdcd2cSStefan Berger     trace_tpm_emulator_vm_state_change(running, state);
910*99bdcd2cSStefan Berger 
911*99bdcd2cSStefan Berger     if (!running || state != RUN_STATE_RUNNING || !tpm_emu->relock_storage) {
912*99bdcd2cSStefan Berger         return;
913*99bdcd2cSStefan Berger     }
914*99bdcd2cSStefan Berger 
915*99bdcd2cSStefan Berger     /* lock storage after migration fall-back */
916*99bdcd2cSStefan Berger     tpm_emulator_lock_storage(tpm_emu);
91738ab74e7SStefan Berger }
91838ab74e7SStefan Berger 
91938ab74e7SStefan Berger /*
92038ab74e7SStefan Berger  * Load the TPM state blobs into the TPM.
92138ab74e7SStefan Berger  *
92238ab74e7SStefan Berger  * Returns negative errno codes in case of error.
92338ab74e7SStefan Berger  */
92438ab74e7SStefan Berger static int tpm_emulator_post_load(void *opaque, int version_id)
92538ab74e7SStefan Berger {
92638ab74e7SStefan Berger     TPMBackend *tb = opaque;
92738ab74e7SStefan Berger     int ret;
92838ab74e7SStefan Berger 
92938ab74e7SStefan Berger     ret = tpm_emulator_set_state_blobs(tb);
93038ab74e7SStefan Berger     if (ret < 0) {
93138ab74e7SStefan Berger         return ret;
93238ab74e7SStefan Berger     }
93338ab74e7SStefan Berger 
93438ab74e7SStefan Berger     if (tpm_emulator_startup_tpm_resume(tb, 0, true) < 0) {
93538ab74e7SStefan Berger         return -EIO;
93638ab74e7SStefan Berger     }
93738ab74e7SStefan Berger 
93838ab74e7SStefan Berger     return 0;
93938ab74e7SStefan Berger }
94038ab74e7SStefan Berger 
94138ab74e7SStefan Berger static const VMStateDescription vmstate_tpm_emulator = {
94238ab74e7SStefan Berger     .name = "tpm-emulator",
94338ab74e7SStefan Berger     .version_id = 0,
94438ab74e7SStefan Berger     .pre_save = tpm_emulator_pre_save,
94538ab74e7SStefan Berger     .post_load = tpm_emulator_post_load,
94638ab74e7SStefan Berger     .fields = (VMStateField[]) {
94738ab74e7SStefan Berger         VMSTATE_UINT32(state_blobs.permanent_flags, TPMEmulator),
94838ab74e7SStefan Berger         VMSTATE_UINT32(state_blobs.permanent.size, TPMEmulator),
94938ab74e7SStefan Berger         VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.permanent.buffer,
95038ab74e7SStefan Berger                                      TPMEmulator, 0, 0,
95138ab74e7SStefan Berger                                      state_blobs.permanent.size),
95238ab74e7SStefan Berger 
95338ab74e7SStefan Berger         VMSTATE_UINT32(state_blobs.volatil_flags, TPMEmulator),
95438ab74e7SStefan Berger         VMSTATE_UINT32(state_blobs.volatil.size, TPMEmulator),
95538ab74e7SStefan Berger         VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.volatil.buffer,
95638ab74e7SStefan Berger                                      TPMEmulator, 0, 0,
95738ab74e7SStefan Berger                                      state_blobs.volatil.size),
95838ab74e7SStefan Berger 
95938ab74e7SStefan Berger         VMSTATE_UINT32(state_blobs.savestate_flags, TPMEmulator),
96038ab74e7SStefan Berger         VMSTATE_UINT32(state_blobs.savestate.size, TPMEmulator),
96138ab74e7SStefan Berger         VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.savestate.buffer,
96238ab74e7SStefan Berger                                      TPMEmulator, 0, 0,
96338ab74e7SStefan Berger                                      state_blobs.savestate.size),
96438ab74e7SStefan Berger 
96538ab74e7SStefan Berger         VMSTATE_END_OF_LIST()
96638ab74e7SStefan Berger     }
96738ab74e7SStefan Berger };
96838ab74e7SStefan Berger 
969f4ede81eSAmarnath Valluri static void tpm_emulator_inst_init(Object *obj)
970f4ede81eSAmarnath Valluri {
971f4ede81eSAmarnath Valluri     TPMEmulator *tpm_emu = TPM_EMULATOR(obj);
972f4ede81eSAmarnath Valluri 
9739d9dcd96SStefan Berger     trace_tpm_emulator_inst_init();
9749d9dcd96SStefan Berger 
975f4ede81eSAmarnath Valluri     tpm_emu->options = g_new0(TPMEmulatorOptions, 1);
976f4ede81eSAmarnath Valluri     tpm_emu->cur_locty_number = ~0;
97717b1af77SMarc-André Lureau     qemu_mutex_init(&tpm_emu->mutex);
978*99bdcd2cSStefan Berger     tpm_emu->vmstate =
979*99bdcd2cSStefan Berger         qemu_add_vm_change_state_handler(tpm_emulator_vm_state_change,
980*99bdcd2cSStefan Berger                                          tpm_emu);
98138ab74e7SStefan Berger 
9821df2c9a2SPeter Xu     vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY,
9831df2c9a2SPeter Xu                      &vmstate_tpm_emulator, obj);
984f4ede81eSAmarnath Valluri }
985f4ede81eSAmarnath Valluri 
986f4ede81eSAmarnath Valluri /*
987f4ede81eSAmarnath Valluri  * Gracefully shut down the external TPM
988f4ede81eSAmarnath Valluri  */
989f4ede81eSAmarnath Valluri static void tpm_emulator_shutdown(TPMEmulator *tpm_emu)
990f4ede81eSAmarnath Valluri {
991f4ede81eSAmarnath Valluri     ptm_res res;
992f4ede81eSAmarnath Valluri 
99388f83074SStefan Berger     if (!tpm_emu->options->chardev) {
99488f83074SStefan Berger         /* was never properly initialized */
99588f83074SStefan Berger         return;
99688f83074SStefan Berger     }
99788f83074SStefan Berger 
99817b1af77SMarc-André Lureau     if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SHUTDOWN, &res, 0, sizeof(res)) < 0) {
999f4ede81eSAmarnath Valluri         error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s",
1000f4ede81eSAmarnath Valluri                      strerror(errno));
1001f4ede81eSAmarnath Valluri     } else if (res != 0) {
10027e095e84SStefan Berger         error_report("tpm-emulator: TPM result for shutdown: 0x%x %s",
10037e095e84SStefan Berger                      be32_to_cpu(res), tpm_emulator_strerror(be32_to_cpu(res)));
1004f4ede81eSAmarnath Valluri     }
1005f4ede81eSAmarnath Valluri }
1006f4ede81eSAmarnath Valluri 
1007f4ede81eSAmarnath Valluri static void tpm_emulator_inst_finalize(Object *obj)
1008f4ede81eSAmarnath Valluri {
1009f4ede81eSAmarnath Valluri     TPMEmulator *tpm_emu = TPM_EMULATOR(obj);
101038ab74e7SStefan Berger     TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs;
1011f4ede81eSAmarnath Valluri 
1012f4ede81eSAmarnath Valluri     tpm_emulator_shutdown(tpm_emu);
1013f4ede81eSAmarnath Valluri 
1014f4ede81eSAmarnath Valluri     object_unref(OBJECT(tpm_emu->data_ioc));
1015f4ede81eSAmarnath Valluri 
1016f4ede81eSAmarnath Valluri     qemu_chr_fe_deinit(&tpm_emu->ctrl_chr, false);
1017f4ede81eSAmarnath Valluri 
1018f4ede81eSAmarnath Valluri     qapi_free_TPMEmulatorOptions(tpm_emu->options);
1019f4ede81eSAmarnath Valluri 
1020f4ede81eSAmarnath Valluri     if (tpm_emu->migration_blocker) {
1021f4ede81eSAmarnath Valluri         migrate_del_blocker(tpm_emu->migration_blocker);
1022f4ede81eSAmarnath Valluri         error_free(tpm_emu->migration_blocker);
1023f4ede81eSAmarnath Valluri     }
102417b1af77SMarc-André Lureau 
102538ab74e7SStefan Berger     tpm_sized_buffer_reset(&state_blobs->volatil);
102638ab74e7SStefan Berger     tpm_sized_buffer_reset(&state_blobs->permanent);
102738ab74e7SStefan Berger     tpm_sized_buffer_reset(&state_blobs->savestate);
102838ab74e7SStefan Berger 
102917b1af77SMarc-André Lureau     qemu_mutex_destroy(&tpm_emu->mutex);
1030*99bdcd2cSStefan Berger     qemu_del_vm_change_state_handler(tpm_emu->vmstate);
103138ab74e7SStefan Berger 
103238ab74e7SStefan Berger     vmstate_unregister(NULL, &vmstate_tpm_emulator, obj);
1033f4ede81eSAmarnath Valluri }
1034f4ede81eSAmarnath Valluri 
1035f4ede81eSAmarnath Valluri static void tpm_emulator_class_init(ObjectClass *klass, void *data)
1036f4ede81eSAmarnath Valluri {
1037f4ede81eSAmarnath Valluri     TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass);
1038d31076baSMarc-André Lureau 
1039d31076baSMarc-André Lureau     tbc->type = TPM_TYPE_EMULATOR;
1040d31076baSMarc-André Lureau     tbc->opts = tpm_emulator_cmdline_opts;
1041d31076baSMarc-André Lureau     tbc->desc = "TPM emulator backend driver";
1042d31076baSMarc-André Lureau     tbc->create = tpm_emulator_create;
1043d31076baSMarc-André Lureau     tbc->startup_tpm = tpm_emulator_startup_tpm;
1044d31076baSMarc-André Lureau     tbc->cancel_cmd = tpm_emulator_cancel_cmd;
1045d31076baSMarc-André Lureau     tbc->get_tpm_established_flag = tpm_emulator_get_tpm_established_flag;
1046d31076baSMarc-André Lureau     tbc->reset_tpm_established_flag = tpm_emulator_reset_tpm_established_flag;
1047d31076baSMarc-André Lureau     tbc->get_tpm_version = tpm_emulator_get_tpm_version;
1048b21e6aafSStefan Berger     tbc->get_buffer_size = tpm_emulator_get_buffer_size;
1049d31076baSMarc-André Lureau     tbc->get_tpm_options = tpm_emulator_get_tpm_options;
1050d31076baSMarc-André Lureau 
1051f4ede81eSAmarnath Valluri     tbc->handle_request = tpm_emulator_handle_request;
1052f4ede81eSAmarnath Valluri }
1053f4ede81eSAmarnath Valluri 
1054f4ede81eSAmarnath Valluri static const TypeInfo tpm_emulator_info = {
1055f4ede81eSAmarnath Valluri     .name = TYPE_TPM_EMULATOR,
1056f4ede81eSAmarnath Valluri     .parent = TYPE_TPM_BACKEND,
1057f4ede81eSAmarnath Valluri     .instance_size = sizeof(TPMEmulator),
1058f4ede81eSAmarnath Valluri     .class_init = tpm_emulator_class_init,
1059f4ede81eSAmarnath Valluri     .instance_init = tpm_emulator_inst_init,
1060f4ede81eSAmarnath Valluri     .instance_finalize = tpm_emulator_inst_finalize,
1061f4ede81eSAmarnath Valluri };
1062f4ede81eSAmarnath Valluri 
1063f4ede81eSAmarnath Valluri static void tpm_emulator_register(void)
1064f4ede81eSAmarnath Valluri {
1065f4ede81eSAmarnath Valluri     type_register_static(&tpm_emulator_info);
1066f4ede81eSAmarnath Valluri }
1067f4ede81eSAmarnath Valluri 
1068f4ede81eSAmarnath Valluri type_init(tpm_emulator_register)
1069