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 17f4ede81eSAmarnath Valluri * version 2 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" 33f4ede81eSAmarnath Valluri #include "io/channel-socket.h" 34f4ede81eSAmarnath Valluri #include "sysemu/tpm_backend.h" 35*0f7d2148SPhilippe Mathieu-Daudé #include "sysemu/tpm_util.h" 36f4ede81eSAmarnath Valluri #include "tpm_int.h" 37f4ede81eSAmarnath Valluri #include "tpm_ioctl.h" 38f4ede81eSAmarnath Valluri #include "migration/blocker.h" 39d6454270SMarkus Armbruster #include "migration/vmstate.h" 40f4ede81eSAmarnath Valluri #include "qapi/error.h" 41f4ede81eSAmarnath Valluri #include "qapi/clone-visitor.h" 429af23989SMarkus Armbruster #include "qapi/qapi-visit-tpm.h" 43f4ede81eSAmarnath Valluri #include "chardev/char-fe.h" 449d9dcd96SStefan Berger #include "trace.h" 45f4ede81eSAmarnath Valluri 46f4ede81eSAmarnath Valluri #define TYPE_TPM_EMULATOR "tpm-emulator" 47f4ede81eSAmarnath Valluri #define TPM_EMULATOR(obj) \ 48f4ede81eSAmarnath Valluri OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR) 49f4ede81eSAmarnath Valluri 50f4ede81eSAmarnath Valluri #define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap)) 51f4ede81eSAmarnath Valluri 52f4ede81eSAmarnath Valluri /* data structures */ 5338ab74e7SStefan Berger 5438ab74e7SStefan Berger /* blobs from the TPM; part of VM state when migrating */ 5538ab74e7SStefan Berger typedef struct TPMBlobBuffers { 5638ab74e7SStefan Berger uint32_t permanent_flags; 5738ab74e7SStefan Berger TPMSizedBuffer permanent; 5838ab74e7SStefan Berger 5938ab74e7SStefan Berger uint32_t volatil_flags; 6038ab74e7SStefan Berger TPMSizedBuffer volatil; 6138ab74e7SStefan Berger 6238ab74e7SStefan Berger uint32_t savestate_flags; 6338ab74e7SStefan Berger TPMSizedBuffer savestate; 6438ab74e7SStefan Berger } TPMBlobBuffers; 6538ab74e7SStefan Berger 66f4ede81eSAmarnath Valluri typedef struct TPMEmulator { 67f4ede81eSAmarnath Valluri TPMBackend parent; 68f4ede81eSAmarnath Valluri 69f4ede81eSAmarnath Valluri TPMEmulatorOptions *options; 70f4ede81eSAmarnath Valluri CharBackend ctrl_chr; 71f4ede81eSAmarnath Valluri QIOChannel *data_ioc; 72f4ede81eSAmarnath Valluri TPMVersion tpm_version; 73f4ede81eSAmarnath Valluri ptm_cap caps; /* capabilities of the TPM */ 74f4ede81eSAmarnath Valluri uint8_t cur_locty_number; /* last set locality */ 75f4ede81eSAmarnath Valluri Error *migration_blocker; 7617b1af77SMarc-André Lureau 7717b1af77SMarc-André Lureau QemuMutex mutex; 780b4c7c65SStefan Berger 790b4c7c65SStefan Berger unsigned int established_flag:1; 800b4c7c65SStefan Berger unsigned int established_flag_cached:1; 8138ab74e7SStefan Berger 8238ab74e7SStefan Berger TPMBlobBuffers state_blobs; 83f4ede81eSAmarnath Valluri } TPMEmulator; 84f4ede81eSAmarnath Valluri 857e095e84SStefan Berger struct tpm_error { 867e095e84SStefan Berger uint32_t tpm_result; 877e095e84SStefan Berger const char *string; 887e095e84SStefan Berger }; 897e095e84SStefan Berger 907e095e84SStefan Berger static const struct tpm_error tpm_errors[] = { 917e095e84SStefan Berger /* TPM 1.2 error codes */ 927e095e84SStefan Berger { TPM_BAD_PARAMETER , "a parameter is bad" }, 937e095e84SStefan Berger { TPM_FAIL , "operation failed" }, 947e095e84SStefan Berger { TPM_KEYNOTFOUND , "key could not be found" }, 957e095e84SStefan Berger { TPM_BAD_PARAM_SIZE , "bad parameter size"}, 967e095e84SStefan Berger { TPM_ENCRYPT_ERROR , "encryption error" }, 977e095e84SStefan Berger { TPM_DECRYPT_ERROR , "decryption error" }, 987e095e84SStefan Berger { TPM_BAD_KEY_PROPERTY, "bad key property" }, 997e095e84SStefan Berger { TPM_BAD_MODE , "bad (encryption) mode" }, 1007e095e84SStefan Berger { TPM_BAD_VERSION , "bad version identifier" }, 1017e095e84SStefan Berger { TPM_BAD_LOCALITY , "bad locality" }, 1027e095e84SStefan Berger /* TPM 2 error codes */ 1037e095e84SStefan Berger { TPM_RC_FAILURE , "operation failed" }, 1047e095e84SStefan Berger { TPM_RC_LOCALITY , "bad locality" }, 1057e095e84SStefan Berger { TPM_RC_INSUFFICIENT, "insufficient amount of data" }, 1067e095e84SStefan Berger }; 1077e095e84SStefan Berger 1087e095e84SStefan Berger static const char *tpm_emulator_strerror(uint32_t tpm_result) 1097e095e84SStefan Berger { 1107e095e84SStefan Berger size_t i; 1117e095e84SStefan Berger 1127e095e84SStefan Berger for (i = 0; i < ARRAY_SIZE(tpm_errors); i++) { 1137e095e84SStefan Berger if (tpm_errors[i].tpm_result == tpm_result) { 1147e095e84SStefan Berger return tpm_errors[i].string; 1157e095e84SStefan Berger } 1167e095e84SStefan Berger } 1177e095e84SStefan Berger return ""; 1187e095e84SStefan Berger } 119f4ede81eSAmarnath Valluri 12017b1af77SMarc-André Lureau static int tpm_emulator_ctrlcmd(TPMEmulator *tpm, unsigned long cmd, void *msg, 121f4ede81eSAmarnath Valluri size_t msg_len_in, size_t msg_len_out) 122f4ede81eSAmarnath Valluri { 12317b1af77SMarc-André Lureau CharBackend *dev = &tpm->ctrl_chr; 124f4ede81eSAmarnath Valluri uint32_t cmd_no = cpu_to_be32(cmd); 125f4ede81eSAmarnath Valluri ssize_t n = sizeof(uint32_t) + msg_len_in; 126f4ede81eSAmarnath Valluri uint8_t *buf = NULL; 12717b1af77SMarc-André Lureau int ret = -1; 12817b1af77SMarc-André Lureau 12917b1af77SMarc-André Lureau qemu_mutex_lock(&tpm->mutex); 130f4ede81eSAmarnath Valluri 131f4ede81eSAmarnath Valluri buf = g_alloca(n); 132f4ede81eSAmarnath Valluri memcpy(buf, &cmd_no, sizeof(cmd_no)); 133f4ede81eSAmarnath Valluri memcpy(buf + sizeof(cmd_no), msg, msg_len_in); 134f4ede81eSAmarnath Valluri 135f4ede81eSAmarnath Valluri n = qemu_chr_fe_write_all(dev, buf, n); 136f4ede81eSAmarnath Valluri if (n <= 0) { 13717b1af77SMarc-André Lureau goto end; 138f4ede81eSAmarnath Valluri } 139f4ede81eSAmarnath Valluri 140f4ede81eSAmarnath Valluri if (msg_len_out != 0) { 141f4ede81eSAmarnath Valluri n = qemu_chr_fe_read_all(dev, msg, msg_len_out); 142f4ede81eSAmarnath Valluri if (n <= 0) { 14317b1af77SMarc-André Lureau goto end; 144f4ede81eSAmarnath Valluri } 145f4ede81eSAmarnath Valluri } 146f4ede81eSAmarnath Valluri 14717b1af77SMarc-André Lureau ret = 0; 14817b1af77SMarc-André Lureau 14917b1af77SMarc-André Lureau end: 15017b1af77SMarc-André Lureau qemu_mutex_unlock(&tpm->mutex); 15117b1af77SMarc-André Lureau return ret; 152f4ede81eSAmarnath Valluri } 153f4ede81eSAmarnath Valluri 154f4ede81eSAmarnath Valluri static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu, 155f4ede81eSAmarnath Valluri const uint8_t *in, uint32_t in_len, 156f4ede81eSAmarnath Valluri uint8_t *out, uint32_t out_len, 157f4ede81eSAmarnath Valluri bool *selftest_done, 158e04e3321SVladimir Sementsov-Ogievskiy Error **errp) 159f4ede81eSAmarnath Valluri { 160f4ede81eSAmarnath Valluri ssize_t ret; 161f4ede81eSAmarnath Valluri bool is_selftest = false; 162f4ede81eSAmarnath Valluri 163f4ede81eSAmarnath Valluri if (selftest_done) { 164f4ede81eSAmarnath Valluri *selftest_done = false; 165f4ede81eSAmarnath Valluri is_selftest = tpm_util_is_selftest(in, in_len); 166f4ede81eSAmarnath Valluri } 167f4ede81eSAmarnath Valluri 168e04e3321SVladimir Sementsov-Ogievskiy ret = qio_channel_write_all(tpm_emu->data_ioc, (char *)in, in_len, errp); 169f4ede81eSAmarnath Valluri if (ret != 0) { 170f4ede81eSAmarnath Valluri return -1; 171f4ede81eSAmarnath Valluri } 172f4ede81eSAmarnath Valluri 173cc1b6c55SMarc-André Lureau ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out, 174e04e3321SVladimir Sementsov-Ogievskiy sizeof(struct tpm_resp_hdr), errp); 175f4ede81eSAmarnath Valluri if (ret != 0) { 176f4ede81eSAmarnath Valluri return -1; 177f4ede81eSAmarnath Valluri } 178f4ede81eSAmarnath Valluri 179cc1b6c55SMarc-André Lureau ret = qio_channel_read_all(tpm_emu->data_ioc, 180cc1b6c55SMarc-André Lureau (char *)out + sizeof(struct tpm_resp_hdr), 181e04e3321SVladimir Sementsov-Ogievskiy tpm_cmd_get_size(out) - sizeof(struct tpm_resp_hdr), errp); 182f4ede81eSAmarnath Valluri if (ret != 0) { 183f4ede81eSAmarnath Valluri return -1; 184f4ede81eSAmarnath Valluri } 185f4ede81eSAmarnath Valluri 186f4ede81eSAmarnath Valluri if (is_selftest) { 187cc1b6c55SMarc-André Lureau *selftest_done = tpm_cmd_get_errcode(out) == 0; 188f4ede81eSAmarnath Valluri } 189f4ede81eSAmarnath Valluri 190f4ede81eSAmarnath Valluri return 0; 191f4ede81eSAmarnath Valluri } 192f4ede81eSAmarnath Valluri 193c106ede9SMarc-André Lureau static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number, 194c106ede9SMarc-André Lureau Error **errp) 195f4ede81eSAmarnath Valluri { 196f4ede81eSAmarnath Valluri ptm_loc loc; 197f4ede81eSAmarnath Valluri 198f4ede81eSAmarnath Valluri if (tpm_emu->cur_locty_number == locty_number) { 199f4ede81eSAmarnath Valluri return 0; 200f4ede81eSAmarnath Valluri } 201f4ede81eSAmarnath Valluri 2029d9dcd96SStefan Berger trace_tpm_emulator_set_locality(locty_number); 2039d9dcd96SStefan Berger 204eff1fe9fSStefan Berger memset(&loc, 0, sizeof(loc)); 205f4ede81eSAmarnath Valluri loc.u.req.loc = locty_number; 20617b1af77SMarc-André Lureau if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_LOCALITY, &loc, 207f4ede81eSAmarnath Valluri sizeof(loc), sizeof(loc)) < 0) { 208c106ede9SMarc-André Lureau error_setg(errp, "tpm-emulator: could not set locality : %s", 209f4ede81eSAmarnath Valluri strerror(errno)); 210f4ede81eSAmarnath Valluri return -1; 211f4ede81eSAmarnath Valluri } 212f4ede81eSAmarnath Valluri 213f4ede81eSAmarnath Valluri loc.u.resp.tpm_result = be32_to_cpu(loc.u.resp.tpm_result); 214f4ede81eSAmarnath Valluri if (loc.u.resp.tpm_result != 0) { 215c106ede9SMarc-André Lureau error_setg(errp, "tpm-emulator: TPM result for set locality : 0x%x", 216f4ede81eSAmarnath Valluri loc.u.resp.tpm_result); 217f4ede81eSAmarnath Valluri return -1; 218f4ede81eSAmarnath Valluri } 219f4ede81eSAmarnath Valluri 220f4ede81eSAmarnath Valluri tpm_emu->cur_locty_number = locty_number; 221f4ede81eSAmarnath Valluri 222f4ede81eSAmarnath Valluri return 0; 223f4ede81eSAmarnath Valluri } 224f4ede81eSAmarnath Valluri 2256a8a2354SMarc-André Lureau static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd *cmd, 2266a8a2354SMarc-André Lureau Error **errp) 227f4ede81eSAmarnath Valluri { 228f4ede81eSAmarnath Valluri TPMEmulator *tpm_emu = TPM_EMULATOR(tb); 229f4ede81eSAmarnath Valluri 2309d9dcd96SStefan Berger trace_tpm_emulator_handle_request(); 231f4ede81eSAmarnath Valluri 2326a8a2354SMarc-André Lureau if (tpm_emulator_set_locality(tpm_emu, cmd->locty, errp) < 0 || 2336a8a2354SMarc-André Lureau tpm_emulator_unix_tx_bufs(tpm_emu, cmd->in, cmd->in_len, 2340e43b7e6SMarc-André Lureau cmd->out, cmd->out_len, 2356a8a2354SMarc-André Lureau &cmd->selftest_done, errp) < 0) { 2360e43b7e6SMarc-André Lureau tpm_util_write_fatal_error_response(cmd->out, cmd->out_len); 2376a8a2354SMarc-André Lureau } 238f4ede81eSAmarnath Valluri } 239f4ede81eSAmarnath Valluri 240f4ede81eSAmarnath Valluri static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu) 241f4ede81eSAmarnath Valluri { 24217b1af77SMarc-André Lureau if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_CAPABILITY, 243f4ede81eSAmarnath Valluri &tpm_emu->caps, 0, sizeof(tpm_emu->caps)) < 0) { 244f4ede81eSAmarnath Valluri error_report("tpm-emulator: probing failed : %s", strerror(errno)); 245f4ede81eSAmarnath Valluri return -1; 246f4ede81eSAmarnath Valluri } 247f4ede81eSAmarnath Valluri 248f4ede81eSAmarnath Valluri tpm_emu->caps = be64_to_cpu(tpm_emu->caps); 249f4ede81eSAmarnath Valluri 2509d9dcd96SStefan Berger trace_tpm_emulator_probe_caps(tpm_emu->caps); 251f4ede81eSAmarnath Valluri 252f4ede81eSAmarnath Valluri return 0; 253f4ede81eSAmarnath Valluri } 254f4ede81eSAmarnath Valluri 255f4ede81eSAmarnath Valluri static int tpm_emulator_check_caps(TPMEmulator *tpm_emu) 256f4ede81eSAmarnath Valluri { 257f4ede81eSAmarnath Valluri ptm_cap caps = 0; 258f4ede81eSAmarnath Valluri const char *tpm = NULL; 259f4ede81eSAmarnath Valluri 260f4ede81eSAmarnath Valluri /* check for min. required capabilities */ 261f4ede81eSAmarnath Valluri switch (tpm_emu->tpm_version) { 262f4ede81eSAmarnath Valluri case TPM_VERSION_1_2: 263f4ede81eSAmarnath Valluri caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED | 2649375c44fSStefan Berger PTM_CAP_SET_LOCALITY | PTM_CAP_SET_DATAFD | PTM_CAP_STOP | 2659375c44fSStefan Berger PTM_CAP_SET_BUFFERSIZE; 266f4ede81eSAmarnath Valluri tpm = "1.2"; 267f4ede81eSAmarnath Valluri break; 268f4ede81eSAmarnath Valluri case TPM_VERSION_2_0: 269f4ede81eSAmarnath Valluri caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED | 270f4ede81eSAmarnath Valluri PTM_CAP_SET_LOCALITY | PTM_CAP_RESET_TPMESTABLISHED | 2719375c44fSStefan Berger PTM_CAP_SET_DATAFD | PTM_CAP_STOP | PTM_CAP_SET_BUFFERSIZE; 272f4ede81eSAmarnath Valluri tpm = "2"; 273f4ede81eSAmarnath Valluri break; 274f4ede81eSAmarnath Valluri case TPM_VERSION_UNSPEC: 275f4ede81eSAmarnath Valluri error_report("tpm-emulator: TPM version has not been set"); 276f4ede81eSAmarnath Valluri return -1; 277f4ede81eSAmarnath Valluri } 278f4ede81eSAmarnath Valluri 279f4ede81eSAmarnath Valluri if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) { 280f4ede81eSAmarnath Valluri error_report("tpm-emulator: TPM does not implement minimum set of " 281f4ede81eSAmarnath Valluri "required capabilities for TPM %s (0x%x)", tpm, (int)caps); 282f4ede81eSAmarnath Valluri return -1; 283f4ede81eSAmarnath Valluri } 284f4ede81eSAmarnath Valluri 285f4ede81eSAmarnath Valluri return 0; 286f4ede81eSAmarnath Valluri } 287f4ede81eSAmarnath Valluri 2889375c44fSStefan Berger static int tpm_emulator_stop_tpm(TPMBackend *tb) 2899375c44fSStefan Berger { 2909375c44fSStefan Berger TPMEmulator *tpm_emu = TPM_EMULATOR(tb); 2919375c44fSStefan Berger ptm_res res; 2929375c44fSStefan Berger 2939375c44fSStefan Berger if (tpm_emulator_ctrlcmd(tpm_emu, CMD_STOP, &res, 0, sizeof(res)) < 0) { 2949375c44fSStefan Berger error_report("tpm-emulator: Could not stop TPM: %s", 2959375c44fSStefan Berger strerror(errno)); 2969375c44fSStefan Berger return -1; 2979375c44fSStefan Berger } 2989375c44fSStefan Berger 2999375c44fSStefan Berger res = be32_to_cpu(res); 3009375c44fSStefan Berger if (res) { 3017e095e84SStefan Berger error_report("tpm-emulator: TPM result for CMD_STOP: 0x%x %s", res, 3027e095e84SStefan Berger tpm_emulator_strerror(res)); 3039375c44fSStefan Berger return -1; 3049375c44fSStefan Berger } 3059375c44fSStefan Berger 3069375c44fSStefan Berger return 0; 3079375c44fSStefan Berger } 3089375c44fSStefan Berger 3099375c44fSStefan Berger static int tpm_emulator_set_buffer_size(TPMBackend *tb, 3109375c44fSStefan Berger size_t wanted_size, 3119375c44fSStefan Berger size_t *actual_size) 3129375c44fSStefan Berger { 3139375c44fSStefan Berger TPMEmulator *tpm_emu = TPM_EMULATOR(tb); 3149375c44fSStefan Berger ptm_setbuffersize psbs; 3159375c44fSStefan Berger 3169375c44fSStefan Berger if (tpm_emulator_stop_tpm(tb) < 0) { 3179375c44fSStefan Berger return -1; 3189375c44fSStefan Berger } 3199375c44fSStefan Berger 3209375c44fSStefan Berger psbs.u.req.buffersize = cpu_to_be32(wanted_size); 3219375c44fSStefan Berger 3229375c44fSStefan Berger if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_BUFFERSIZE, &psbs, 3239375c44fSStefan Berger sizeof(psbs.u.req), sizeof(psbs.u.resp)) < 0) { 3249375c44fSStefan Berger error_report("tpm-emulator: Could not set buffer size: %s", 3259375c44fSStefan Berger strerror(errno)); 3269375c44fSStefan Berger return -1; 3279375c44fSStefan Berger } 3289375c44fSStefan Berger 3299375c44fSStefan Berger psbs.u.resp.tpm_result = be32_to_cpu(psbs.u.resp.tpm_result); 3309375c44fSStefan Berger if (psbs.u.resp.tpm_result != 0) { 3317e095e84SStefan Berger error_report("tpm-emulator: TPM result for set buffer size : 0x%x %s", 3327e095e84SStefan Berger psbs.u.resp.tpm_result, 3337e095e84SStefan Berger tpm_emulator_strerror(psbs.u.resp.tpm_result)); 3349375c44fSStefan Berger return -1; 3359375c44fSStefan Berger } 3369375c44fSStefan Berger 3379375c44fSStefan Berger if (actual_size) { 3389375c44fSStefan Berger *actual_size = be32_to_cpu(psbs.u.resp.buffersize); 3399375c44fSStefan Berger } 3409375c44fSStefan Berger 3419d9dcd96SStefan Berger trace_tpm_emulator_set_buffer_size( 3429375c44fSStefan Berger be32_to_cpu(psbs.u.resp.buffersize), 3439375c44fSStefan Berger be32_to_cpu(psbs.u.resp.minsize), 3449375c44fSStefan Berger be32_to_cpu(psbs.u.resp.maxsize)); 3459375c44fSStefan Berger 3469375c44fSStefan Berger return 0; 3479375c44fSStefan Berger } 3489375c44fSStefan Berger 34938ab74e7SStefan Berger static int tpm_emulator_startup_tpm_resume(TPMBackend *tb, size_t buffersize, 35038ab74e7SStefan Berger bool is_resume) 351f4ede81eSAmarnath Valluri { 352f4ede81eSAmarnath Valluri TPMEmulator *tpm_emu = TPM_EMULATOR(tb); 35330270587SStefan Berger ptm_init init = { 35430270587SStefan Berger .u.req.init_flags = 0, 35530270587SStefan Berger }; 356f4ede81eSAmarnath Valluri ptm_res res; 357f4ede81eSAmarnath Valluri 35838ab74e7SStefan Berger trace_tpm_emulator_startup_tpm_resume(is_resume, buffersize); 35938ab74e7SStefan Berger 3609375c44fSStefan Berger if (buffersize != 0 && 3619375c44fSStefan Berger tpm_emulator_set_buffer_size(tb, buffersize, NULL) < 0) { 3629375c44fSStefan Berger goto err_exit; 3639375c44fSStefan Berger } 3649375c44fSStefan Berger 36538ab74e7SStefan Berger if (is_resume) { 36638ab74e7SStefan Berger init.u.req.init_flags |= cpu_to_be32(PTM_INIT_FLAG_DELETE_VOLATILE); 36738ab74e7SStefan Berger } 36838ab74e7SStefan Berger 36917b1af77SMarc-André Lureau if (tpm_emulator_ctrlcmd(tpm_emu, CMD_INIT, &init, sizeof(init), 370f4ede81eSAmarnath Valluri sizeof(init)) < 0) { 371f4ede81eSAmarnath Valluri error_report("tpm-emulator: could not send INIT: %s", 372f4ede81eSAmarnath Valluri strerror(errno)); 373f4ede81eSAmarnath Valluri goto err_exit; 374f4ede81eSAmarnath Valluri } 375f4ede81eSAmarnath Valluri 376f4ede81eSAmarnath Valluri res = be32_to_cpu(init.u.resp.tpm_result); 377f4ede81eSAmarnath Valluri if (res) { 3787e095e84SStefan Berger error_report("tpm-emulator: TPM result for CMD_INIT: 0x%x %s", res, 3797e095e84SStefan Berger tpm_emulator_strerror(res)); 380f4ede81eSAmarnath Valluri goto err_exit; 381f4ede81eSAmarnath Valluri } 382f4ede81eSAmarnath Valluri return 0; 383f4ede81eSAmarnath Valluri 384f4ede81eSAmarnath Valluri err_exit: 385f4ede81eSAmarnath Valluri return -1; 386f4ede81eSAmarnath Valluri } 387f4ede81eSAmarnath Valluri 38838ab74e7SStefan Berger static int tpm_emulator_startup_tpm(TPMBackend *tb, size_t buffersize) 38938ab74e7SStefan Berger { 39038ab74e7SStefan Berger return tpm_emulator_startup_tpm_resume(tb, buffersize, false); 39138ab74e7SStefan Berger } 39238ab74e7SStefan Berger 393f4ede81eSAmarnath Valluri static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb) 394f4ede81eSAmarnath Valluri { 395f4ede81eSAmarnath Valluri TPMEmulator *tpm_emu = TPM_EMULATOR(tb); 396f4ede81eSAmarnath Valluri ptm_est est; 397f4ede81eSAmarnath Valluri 3980b4c7c65SStefan Berger if (tpm_emu->established_flag_cached) { 3990b4c7c65SStefan Berger return tpm_emu->established_flag; 4000b4c7c65SStefan Berger } 4010b4c7c65SStefan Berger 40217b1af77SMarc-André Lureau if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_TPMESTABLISHED, &est, 403f4ede81eSAmarnath Valluri 0, sizeof(est)) < 0) { 404f4ede81eSAmarnath Valluri error_report("tpm-emulator: Could not get the TPM established flag: %s", 405f4ede81eSAmarnath Valluri strerror(errno)); 406f4ede81eSAmarnath Valluri return false; 407f4ede81eSAmarnath Valluri } 4089d9dcd96SStefan Berger trace_tpm_emulator_get_tpm_established_flag(est.u.resp.bit); 409f4ede81eSAmarnath Valluri 4100b4c7c65SStefan Berger tpm_emu->established_flag_cached = 1; 4110b4c7c65SStefan Berger tpm_emu->established_flag = (est.u.resp.bit != 0); 4120b4c7c65SStefan Berger 4130b4c7c65SStefan Berger return tpm_emu->established_flag; 414f4ede81eSAmarnath Valluri } 415f4ede81eSAmarnath Valluri 416f4ede81eSAmarnath Valluri static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb, 417f4ede81eSAmarnath Valluri uint8_t locty) 418f4ede81eSAmarnath Valluri { 419f4ede81eSAmarnath Valluri TPMEmulator *tpm_emu = TPM_EMULATOR(tb); 420f4ede81eSAmarnath Valluri ptm_reset_est reset_est; 421f4ede81eSAmarnath Valluri ptm_res res; 422f4ede81eSAmarnath Valluri 423f4ede81eSAmarnath Valluri /* only a TPM 2.0 will support this */ 424f4ede81eSAmarnath Valluri if (tpm_emu->tpm_version != TPM_VERSION_2_0) { 425f4ede81eSAmarnath Valluri return 0; 426f4ede81eSAmarnath Valluri } 427f4ede81eSAmarnath Valluri 428f4ede81eSAmarnath Valluri reset_est.u.req.loc = tpm_emu->cur_locty_number; 42917b1af77SMarc-André Lureau if (tpm_emulator_ctrlcmd(tpm_emu, CMD_RESET_TPMESTABLISHED, 430f4ede81eSAmarnath Valluri &reset_est, sizeof(reset_est), 431f4ede81eSAmarnath Valluri sizeof(reset_est)) < 0) { 432f4ede81eSAmarnath Valluri error_report("tpm-emulator: Could not reset the establishment bit: %s", 433f4ede81eSAmarnath Valluri strerror(errno)); 434f4ede81eSAmarnath Valluri return -1; 435f4ede81eSAmarnath Valluri } 436f4ede81eSAmarnath Valluri 437f4ede81eSAmarnath Valluri res = be32_to_cpu(reset_est.u.resp.tpm_result); 438f4ede81eSAmarnath Valluri if (res) { 4397e095e84SStefan Berger error_report( 4407e095e84SStefan Berger "tpm-emulator: TPM result for rest established flag: 0x%x %s", 4417e095e84SStefan Berger res, tpm_emulator_strerror(res)); 442f4ede81eSAmarnath Valluri return -1; 443f4ede81eSAmarnath Valluri } 444f4ede81eSAmarnath Valluri 4450b4c7c65SStefan Berger tpm_emu->established_flag_cached = 0; 4460b4c7c65SStefan Berger 447f4ede81eSAmarnath Valluri return 0; 448f4ede81eSAmarnath Valluri } 449f4ede81eSAmarnath Valluri 450f4ede81eSAmarnath Valluri static void tpm_emulator_cancel_cmd(TPMBackend *tb) 451f4ede81eSAmarnath Valluri { 452f4ede81eSAmarnath Valluri TPMEmulator *tpm_emu = TPM_EMULATOR(tb); 453f4ede81eSAmarnath Valluri ptm_res res; 454f4ede81eSAmarnath Valluri 455f4ede81eSAmarnath Valluri if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, PTM_CAP_CANCEL_TPM_CMD)) { 4569d9dcd96SStefan Berger trace_tpm_emulator_cancel_cmd_not_supt(); 457f4ede81eSAmarnath Valluri return; 458f4ede81eSAmarnath Valluri } 459f4ede81eSAmarnath Valluri 4603d011411SMarc-André Lureau /* FIXME: make the function non-blocking, or it may block a VCPU */ 46117b1af77SMarc-André Lureau if (tpm_emulator_ctrlcmd(tpm_emu, CMD_CANCEL_TPM_CMD, &res, 0, 462f4ede81eSAmarnath Valluri sizeof(res)) < 0) { 463f4ede81eSAmarnath Valluri error_report("tpm-emulator: Could not cancel command: %s", 464f4ede81eSAmarnath Valluri strerror(errno)); 465f4ede81eSAmarnath Valluri } else if (res != 0) { 466f4ede81eSAmarnath Valluri error_report("tpm-emulator: Failed to cancel TPM: 0x%x", 467f4ede81eSAmarnath Valluri be32_to_cpu(res)); 468f4ede81eSAmarnath Valluri } 469f4ede81eSAmarnath Valluri } 470f4ede81eSAmarnath Valluri 471f4ede81eSAmarnath Valluri static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb) 472f4ede81eSAmarnath Valluri { 473f4ede81eSAmarnath Valluri TPMEmulator *tpm_emu = TPM_EMULATOR(tb); 474f4ede81eSAmarnath Valluri 475f4ede81eSAmarnath Valluri return tpm_emu->tpm_version; 476f4ede81eSAmarnath Valluri } 477f4ede81eSAmarnath Valluri 478b21e6aafSStefan Berger static size_t tpm_emulator_get_buffer_size(TPMBackend *tb) 479b21e6aafSStefan Berger { 4809375c44fSStefan Berger size_t actual_size; 4819375c44fSStefan Berger 4829375c44fSStefan Berger if (tpm_emulator_set_buffer_size(tb, 0, &actual_size) < 0) { 483b21e6aafSStefan Berger return 4096; 484b21e6aafSStefan Berger } 485b21e6aafSStefan Berger 4869375c44fSStefan Berger return actual_size; 4879375c44fSStefan Berger } 4889375c44fSStefan Berger 489f4ede81eSAmarnath Valluri static int tpm_emulator_block_migration(TPMEmulator *tpm_emu) 490f4ede81eSAmarnath Valluri { 491f4ede81eSAmarnath Valluri Error *err = NULL; 49238ab74e7SStefan Berger ptm_cap caps = PTM_CAP_GET_STATEBLOB | PTM_CAP_SET_STATEBLOB | 49338ab74e7SStefan Berger PTM_CAP_STOP; 494f4ede81eSAmarnath Valluri 49538ab74e7SStefan Berger if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) { 496f4ede81eSAmarnath Valluri error_setg(&tpm_emu->migration_blocker, 49738ab74e7SStefan Berger "Migration disabled: TPM emulator does not support " 49838ab74e7SStefan Berger "migration"); 499f4ede81eSAmarnath Valluri migrate_add_blocker(tpm_emu->migration_blocker, &err); 500f4ede81eSAmarnath Valluri if (err) { 501f4ede81eSAmarnath Valluri error_report_err(err); 502f4ede81eSAmarnath Valluri error_free(tpm_emu->migration_blocker); 503f4ede81eSAmarnath Valluri tpm_emu->migration_blocker = NULL; 504f4ede81eSAmarnath Valluri 505f4ede81eSAmarnath Valluri return -1; 506f4ede81eSAmarnath Valluri } 50738ab74e7SStefan Berger } 508f4ede81eSAmarnath Valluri 509f4ede81eSAmarnath Valluri return 0; 510f4ede81eSAmarnath Valluri } 511f4ede81eSAmarnath Valluri 512f4ede81eSAmarnath Valluri static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu) 513f4ede81eSAmarnath Valluri { 514f4ede81eSAmarnath Valluri ptm_res res; 515f4ede81eSAmarnath Valluri Error *err = NULL; 516f4ede81eSAmarnath Valluri int fds[2] = { -1, -1 }; 517f4ede81eSAmarnath Valluri 518f4ede81eSAmarnath Valluri if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { 519f4ede81eSAmarnath Valluri error_report("tpm-emulator: Failed to create socketpair"); 520f4ede81eSAmarnath Valluri return -1; 521f4ede81eSAmarnath Valluri } 522f4ede81eSAmarnath Valluri 523f4ede81eSAmarnath Valluri qemu_chr_fe_set_msgfds(&tpm_emu->ctrl_chr, fds + 1, 1); 524f4ede81eSAmarnath Valluri 52517b1af77SMarc-André Lureau if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_DATAFD, &res, 0, 52617b1af77SMarc-André Lureau sizeof(res)) < 0 || res != 0) { 527f4ede81eSAmarnath Valluri error_report("tpm-emulator: Failed to send CMD_SET_DATAFD: %s", 528f4ede81eSAmarnath Valluri strerror(errno)); 529f4ede81eSAmarnath Valluri goto err_exit; 530f4ede81eSAmarnath Valluri } 531f4ede81eSAmarnath Valluri 532f4ede81eSAmarnath Valluri tpm_emu->data_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err)); 533f4ede81eSAmarnath Valluri if (err) { 534f4ede81eSAmarnath Valluri error_prepend(&err, "tpm-emulator: Failed to create io channel: "); 535f4ede81eSAmarnath Valluri error_report_err(err); 536f4ede81eSAmarnath Valluri goto err_exit; 537f4ede81eSAmarnath Valluri } 538f4ede81eSAmarnath Valluri 539f4ede81eSAmarnath Valluri closesocket(fds[1]); 540f4ede81eSAmarnath Valluri 541f4ede81eSAmarnath Valluri return 0; 542f4ede81eSAmarnath Valluri 543f4ede81eSAmarnath Valluri err_exit: 544f4ede81eSAmarnath Valluri closesocket(fds[0]); 545f4ede81eSAmarnath Valluri closesocket(fds[1]); 546f4ede81eSAmarnath Valluri return -1; 547f4ede81eSAmarnath Valluri } 548f4ede81eSAmarnath Valluri 549f4ede81eSAmarnath Valluri static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts) 550f4ede81eSAmarnath Valluri { 551f4ede81eSAmarnath Valluri const char *value; 552f4ede81eSAmarnath Valluri 553f4ede81eSAmarnath Valluri value = qemu_opt_get(opts, "chardev"); 554f4ede81eSAmarnath Valluri if (value) { 555f4ede81eSAmarnath Valluri Error *err = NULL; 556f4ede81eSAmarnath Valluri Chardev *dev = qemu_chr_find(value); 557f4ede81eSAmarnath Valluri 558f4ede81eSAmarnath Valluri if (!dev) { 559f4ede81eSAmarnath Valluri error_report("tpm-emulator: tpm chardev '%s' not found.", value); 560f4ede81eSAmarnath Valluri goto err; 561f4ede81eSAmarnath Valluri } 562f4ede81eSAmarnath Valluri 563f4ede81eSAmarnath Valluri if (!qemu_chr_fe_init(&tpm_emu->ctrl_chr, dev, &err)) { 564f4ede81eSAmarnath Valluri error_prepend(&err, "tpm-emulator: No valid chardev found at '%s':", 565f4ede81eSAmarnath Valluri value); 566f4ede81eSAmarnath Valluri error_report_err(err); 567f4ede81eSAmarnath Valluri goto err; 568f4ede81eSAmarnath Valluri } 569f4ede81eSAmarnath Valluri 570f4ede81eSAmarnath Valluri tpm_emu->options->chardev = g_strdup(value); 571f4ede81eSAmarnath Valluri } 572f4ede81eSAmarnath Valluri 573f4ede81eSAmarnath Valluri if (tpm_emulator_prepare_data_fd(tpm_emu) < 0) { 574f4ede81eSAmarnath Valluri goto err; 575f4ede81eSAmarnath Valluri } 576f4ede81eSAmarnath Valluri 577f4ede81eSAmarnath Valluri /* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as it also used 578f4ede81eSAmarnath Valluri * by passthrough driver, which not yet using GIOChannel. 579f4ede81eSAmarnath Valluri */ 580f4ede81eSAmarnath Valluri if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_emu->data_ioc)->fd, 581f4ede81eSAmarnath Valluri &tpm_emu->tpm_version)) { 582f4ede81eSAmarnath Valluri error_report("'%s' is not emulating TPM device. Error: %s", 583f4ede81eSAmarnath Valluri tpm_emu->options->chardev, strerror(errno)); 584f4ede81eSAmarnath Valluri goto err; 585f4ede81eSAmarnath Valluri } 586f4ede81eSAmarnath Valluri 5879d9dcd96SStefan Berger switch (tpm_emu->tpm_version) { 5889d9dcd96SStefan Berger case TPM_VERSION_1_2: 5899d9dcd96SStefan Berger trace_tpm_emulator_handle_device_opts_tpm12(); 5909d9dcd96SStefan Berger break; 5919d9dcd96SStefan Berger case TPM_VERSION_2_0: 5929d9dcd96SStefan Berger trace_tpm_emulator_handle_device_opts_tpm2(); 5939d9dcd96SStefan Berger break; 5949d9dcd96SStefan Berger default: 5959d9dcd96SStefan Berger trace_tpm_emulator_handle_device_opts_unspec(); 5969d9dcd96SStefan Berger } 597f4ede81eSAmarnath Valluri 598f4ede81eSAmarnath Valluri if (tpm_emulator_probe_caps(tpm_emu) || 599f4ede81eSAmarnath Valluri tpm_emulator_check_caps(tpm_emu)) { 600f4ede81eSAmarnath Valluri goto err; 601f4ede81eSAmarnath Valluri } 602f4ede81eSAmarnath Valluri 603f4ede81eSAmarnath Valluri return tpm_emulator_block_migration(tpm_emu); 604f4ede81eSAmarnath Valluri 605f4ede81eSAmarnath Valluri err: 6069d9dcd96SStefan Berger trace_tpm_emulator_handle_device_opts_startup_error(); 6079d9dcd96SStefan Berger 608f4ede81eSAmarnath Valluri return -1; 609f4ede81eSAmarnath Valluri } 610f4ede81eSAmarnath Valluri 6119f7c0ef2SMarc-André Lureau static TPMBackend *tpm_emulator_create(QemuOpts *opts) 612f4ede81eSAmarnath Valluri { 613f4ede81eSAmarnath Valluri TPMBackend *tb = TPM_BACKEND(object_new(TYPE_TPM_EMULATOR)); 614f4ede81eSAmarnath Valluri 615f4ede81eSAmarnath Valluri if (tpm_emulator_handle_device_opts(TPM_EMULATOR(tb), opts)) { 6169f7c0ef2SMarc-André Lureau object_unref(OBJECT(tb)); 6179f7c0ef2SMarc-André Lureau return NULL; 618f4ede81eSAmarnath Valluri } 619f4ede81eSAmarnath Valluri 620f4ede81eSAmarnath Valluri return tb; 621f4ede81eSAmarnath Valluri } 622f4ede81eSAmarnath Valluri 623f4ede81eSAmarnath Valluri static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb) 624f4ede81eSAmarnath Valluri { 625f4ede81eSAmarnath Valluri TPMEmulator *tpm_emu = TPM_EMULATOR(tb); 626f4ede81eSAmarnath Valluri TpmTypeOptions *options = g_new0(TpmTypeOptions, 1); 627f4ede81eSAmarnath Valluri 628f4ede81eSAmarnath Valluri options->type = TPM_TYPE_OPTIONS_KIND_EMULATOR; 629f4ede81eSAmarnath Valluri options->u.emulator.data = QAPI_CLONE(TPMEmulatorOptions, tpm_emu->options); 630f4ede81eSAmarnath Valluri 631f4ede81eSAmarnath Valluri return options; 632f4ede81eSAmarnath Valluri } 633f4ede81eSAmarnath Valluri 634f4ede81eSAmarnath Valluri static const QemuOptDesc tpm_emulator_cmdline_opts[] = { 635f4ede81eSAmarnath Valluri TPM_STANDARD_CMDLINE_OPTS, 636f4ede81eSAmarnath Valluri { 637f4ede81eSAmarnath Valluri .name = "chardev", 638f4ede81eSAmarnath Valluri .type = QEMU_OPT_STRING, 639f4ede81eSAmarnath Valluri .help = "Character device to use for out-of-band control messages", 640f4ede81eSAmarnath Valluri }, 641f4ede81eSAmarnath Valluri { /* end of list */ }, 642f4ede81eSAmarnath Valluri }; 643f4ede81eSAmarnath Valluri 64438ab74e7SStefan Berger /* 64538ab74e7SStefan Berger * Transfer a TPM state blob from the TPM into a provided buffer. 64638ab74e7SStefan Berger * 64738ab74e7SStefan Berger * @tpm_emu: TPMEmulator 64838ab74e7SStefan Berger * @type: the type of blob to transfer 64938ab74e7SStefan Berger * @tsb: the TPMSizeBuffer to fill with the blob 65038ab74e7SStefan Berger * @flags: the flags to return to the caller 65138ab74e7SStefan Berger */ 65238ab74e7SStefan Berger static int tpm_emulator_get_state_blob(TPMEmulator *tpm_emu, 65338ab74e7SStefan Berger uint8_t type, 65438ab74e7SStefan Berger TPMSizedBuffer *tsb, 65538ab74e7SStefan Berger uint32_t *flags) 65638ab74e7SStefan Berger { 65738ab74e7SStefan Berger ptm_getstate pgs; 65838ab74e7SStefan Berger ptm_res res; 65938ab74e7SStefan Berger ssize_t n; 66038ab74e7SStefan Berger uint32_t totlength, length; 66138ab74e7SStefan Berger 66238ab74e7SStefan Berger tpm_sized_buffer_reset(tsb); 66338ab74e7SStefan Berger 66438ab74e7SStefan Berger pgs.u.req.state_flags = cpu_to_be32(PTM_STATE_FLAG_DECRYPTED); 66538ab74e7SStefan Berger pgs.u.req.type = cpu_to_be32(type); 66638ab74e7SStefan Berger pgs.u.req.offset = 0; 66738ab74e7SStefan Berger 66838ab74e7SStefan Berger if (tpm_emulator_ctrlcmd(tpm_emu, CMD_GET_STATEBLOB, 66938ab74e7SStefan Berger &pgs, sizeof(pgs.u.req), 67038ab74e7SStefan Berger offsetof(ptm_getstate, u.resp.data)) < 0) { 67138ab74e7SStefan Berger error_report("tpm-emulator: could not get state blob type %d : %s", 67238ab74e7SStefan Berger type, strerror(errno)); 67338ab74e7SStefan Berger return -1; 67438ab74e7SStefan Berger } 67538ab74e7SStefan Berger 67638ab74e7SStefan Berger res = be32_to_cpu(pgs.u.resp.tpm_result); 67738ab74e7SStefan Berger if (res != 0 && (res & 0x800) == 0) { 67838ab74e7SStefan Berger error_report("tpm-emulator: Getting the stateblob (type %d) failed " 6797e095e84SStefan Berger "with a TPM error 0x%x %s", type, res, 6807e095e84SStefan Berger tpm_emulator_strerror(res)); 68138ab74e7SStefan Berger return -1; 68238ab74e7SStefan Berger } 68338ab74e7SStefan Berger 68438ab74e7SStefan Berger totlength = be32_to_cpu(pgs.u.resp.totlength); 68538ab74e7SStefan Berger length = be32_to_cpu(pgs.u.resp.length); 68638ab74e7SStefan Berger if (totlength != length) { 68738ab74e7SStefan Berger error_report("tpm-emulator: Expecting to read %u bytes " 68838ab74e7SStefan Berger "but would get %u", totlength, length); 68938ab74e7SStefan Berger return -1; 69038ab74e7SStefan Berger } 69138ab74e7SStefan Berger 69238ab74e7SStefan Berger *flags = be32_to_cpu(pgs.u.resp.state_flags); 69338ab74e7SStefan Berger 69438ab74e7SStefan Berger if (totlength > 0) { 69538ab74e7SStefan Berger tsb->buffer = g_try_malloc(totlength); 69638ab74e7SStefan Berger if (!tsb->buffer) { 69738ab74e7SStefan Berger error_report("tpm-emulator: Out of memory allocating %u bytes", 69838ab74e7SStefan Berger totlength); 69938ab74e7SStefan Berger return -1; 70038ab74e7SStefan Berger } 70138ab74e7SStefan Berger 70238ab74e7SStefan Berger n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr, tsb->buffer, totlength); 70338ab74e7SStefan Berger if (n != totlength) { 70438ab74e7SStefan Berger error_report("tpm-emulator: Could not read stateblob (type %d); " 70538ab74e7SStefan Berger "expected %u bytes, got %zd", 70638ab74e7SStefan Berger type, totlength, n); 70738ab74e7SStefan Berger return -1; 70838ab74e7SStefan Berger } 70938ab74e7SStefan Berger } 71038ab74e7SStefan Berger tsb->size = totlength; 71138ab74e7SStefan Berger 71238ab74e7SStefan Berger trace_tpm_emulator_get_state_blob(type, tsb->size, *flags); 71338ab74e7SStefan Berger 71438ab74e7SStefan Berger return 0; 71538ab74e7SStefan Berger } 71638ab74e7SStefan Berger 71738ab74e7SStefan Berger static int tpm_emulator_get_state_blobs(TPMEmulator *tpm_emu) 71838ab74e7SStefan Berger { 71938ab74e7SStefan Berger TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; 72038ab74e7SStefan Berger 72138ab74e7SStefan Berger if (tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT, 72238ab74e7SStefan Berger &state_blobs->permanent, 72338ab74e7SStefan Berger &state_blobs->permanent_flags) < 0 || 72438ab74e7SStefan Berger tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE, 72538ab74e7SStefan Berger &state_blobs->volatil, 72638ab74e7SStefan Berger &state_blobs->volatil_flags) < 0 || 72738ab74e7SStefan Berger tpm_emulator_get_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE, 72838ab74e7SStefan Berger &state_blobs->savestate, 72938ab74e7SStefan Berger &state_blobs->savestate_flags) < 0) { 73038ab74e7SStefan Berger goto err_exit; 73138ab74e7SStefan Berger } 73238ab74e7SStefan Berger 73338ab74e7SStefan Berger return 0; 73438ab74e7SStefan Berger 73538ab74e7SStefan Berger err_exit: 73638ab74e7SStefan Berger tpm_sized_buffer_reset(&state_blobs->volatil); 73738ab74e7SStefan Berger tpm_sized_buffer_reset(&state_blobs->permanent); 73838ab74e7SStefan Berger tpm_sized_buffer_reset(&state_blobs->savestate); 73938ab74e7SStefan Berger 74038ab74e7SStefan Berger return -1; 74138ab74e7SStefan Berger } 74238ab74e7SStefan Berger 74338ab74e7SStefan Berger /* 74438ab74e7SStefan Berger * Transfer a TPM state blob to the TPM emulator. 74538ab74e7SStefan Berger * 74638ab74e7SStefan Berger * @tpm_emu: TPMEmulator 74738ab74e7SStefan Berger * @type: the type of TPM state blob to transfer 74838ab74e7SStefan Berger * @tsb: TPMSizedBuffer containing the TPM state blob 74938ab74e7SStefan Berger * @flags: Flags describing the (encryption) state of the TPM state blob 75038ab74e7SStefan Berger */ 75138ab74e7SStefan Berger static int tpm_emulator_set_state_blob(TPMEmulator *tpm_emu, 75238ab74e7SStefan Berger uint32_t type, 75338ab74e7SStefan Berger TPMSizedBuffer *tsb, 75438ab74e7SStefan Berger uint32_t flags) 75538ab74e7SStefan Berger { 75638ab74e7SStefan Berger ssize_t n; 75738ab74e7SStefan Berger ptm_setstate pss; 75838ab74e7SStefan Berger ptm_res tpm_result; 75938ab74e7SStefan Berger 76038ab74e7SStefan Berger if (tsb->size == 0) { 76138ab74e7SStefan Berger return 0; 76238ab74e7SStefan Berger } 76338ab74e7SStefan Berger 76438ab74e7SStefan Berger pss = (ptm_setstate) { 76538ab74e7SStefan Berger .u.req.state_flags = cpu_to_be32(flags), 76638ab74e7SStefan Berger .u.req.type = cpu_to_be32(type), 76738ab74e7SStefan Berger .u.req.length = cpu_to_be32(tsb->size), 76838ab74e7SStefan Berger }; 76938ab74e7SStefan Berger 77038ab74e7SStefan Berger /* write the header only */ 77138ab74e7SStefan Berger if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_STATEBLOB, &pss, 77238ab74e7SStefan Berger offsetof(ptm_setstate, u.req.data), 0) < 0) { 77338ab74e7SStefan Berger error_report("tpm-emulator: could not set state blob type %d : %s", 77438ab74e7SStefan Berger type, strerror(errno)); 77538ab74e7SStefan Berger return -1; 77638ab74e7SStefan Berger } 77738ab74e7SStefan Berger 77838ab74e7SStefan Berger /* now the body */ 77938ab74e7SStefan Berger n = qemu_chr_fe_write_all(&tpm_emu->ctrl_chr, tsb->buffer, tsb->size); 78038ab74e7SStefan Berger if (n != tsb->size) { 78138ab74e7SStefan Berger error_report("tpm-emulator: Writing the stateblob (type %d) " 78238ab74e7SStefan Berger "failed; could not write %u bytes, but only %zd", 78338ab74e7SStefan Berger type, tsb->size, n); 78438ab74e7SStefan Berger return -1; 78538ab74e7SStefan Berger } 78638ab74e7SStefan Berger 78738ab74e7SStefan Berger /* now get the result */ 78838ab74e7SStefan Berger n = qemu_chr_fe_read_all(&tpm_emu->ctrl_chr, 78938ab74e7SStefan Berger (uint8_t *)&pss, sizeof(pss.u.resp)); 79038ab74e7SStefan Berger if (n != sizeof(pss.u.resp)) { 79138ab74e7SStefan Berger error_report("tpm-emulator: Reading response from writing stateblob " 79238ab74e7SStefan Berger "(type %d) failed; expected %zu bytes, got %zd", type, 79338ab74e7SStefan Berger sizeof(pss.u.resp), n); 79438ab74e7SStefan Berger return -1; 79538ab74e7SStefan Berger } 79638ab74e7SStefan Berger 79738ab74e7SStefan Berger tpm_result = be32_to_cpu(pss.u.resp.tpm_result); 79838ab74e7SStefan Berger if (tpm_result != 0) { 79938ab74e7SStefan Berger error_report("tpm-emulator: Setting the stateblob (type %d) failed " 8007e095e84SStefan Berger "with a TPM error 0x%x %s", type, tpm_result, 8017e095e84SStefan Berger tpm_emulator_strerror(tpm_result)); 80238ab74e7SStefan Berger return -1; 80338ab74e7SStefan Berger } 80438ab74e7SStefan Berger 80538ab74e7SStefan Berger trace_tpm_emulator_set_state_blob(type, tsb->size, flags); 80638ab74e7SStefan Berger 80738ab74e7SStefan Berger return 0; 80838ab74e7SStefan Berger } 80938ab74e7SStefan Berger 81038ab74e7SStefan Berger /* 81138ab74e7SStefan Berger * Set all the TPM state blobs. 81238ab74e7SStefan Berger * 81338ab74e7SStefan Berger * Returns a negative errno code in case of error. 81438ab74e7SStefan Berger */ 81538ab74e7SStefan Berger static int tpm_emulator_set_state_blobs(TPMBackend *tb) 81638ab74e7SStefan Berger { 81738ab74e7SStefan Berger TPMEmulator *tpm_emu = TPM_EMULATOR(tb); 81838ab74e7SStefan Berger TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; 81938ab74e7SStefan Berger 82038ab74e7SStefan Berger trace_tpm_emulator_set_state_blobs(); 82138ab74e7SStefan Berger 82238ab74e7SStefan Berger if (tpm_emulator_stop_tpm(tb) < 0) { 82338ab74e7SStefan Berger trace_tpm_emulator_set_state_blobs_error("Could not stop TPM"); 82438ab74e7SStefan Berger return -EIO; 82538ab74e7SStefan Berger } 82638ab74e7SStefan Berger 82738ab74e7SStefan Berger if (tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_PERMANENT, 82838ab74e7SStefan Berger &state_blobs->permanent, 82938ab74e7SStefan Berger state_blobs->permanent_flags) < 0 || 83038ab74e7SStefan Berger tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_VOLATILE, 83138ab74e7SStefan Berger &state_blobs->volatil, 83238ab74e7SStefan Berger state_blobs->volatil_flags) < 0 || 83338ab74e7SStefan Berger tpm_emulator_set_state_blob(tpm_emu, PTM_BLOB_TYPE_SAVESTATE, 83438ab74e7SStefan Berger &state_blobs->savestate, 83538ab74e7SStefan Berger state_blobs->savestate_flags) < 0) { 83638ab74e7SStefan Berger return -EIO; 83738ab74e7SStefan Berger } 83838ab74e7SStefan Berger 83938ab74e7SStefan Berger trace_tpm_emulator_set_state_blobs_done(); 84038ab74e7SStefan Berger 84138ab74e7SStefan Berger return 0; 84238ab74e7SStefan Berger } 84338ab74e7SStefan Berger 84438ab74e7SStefan Berger static int tpm_emulator_pre_save(void *opaque) 84538ab74e7SStefan Berger { 84638ab74e7SStefan Berger TPMBackend *tb = opaque; 84738ab74e7SStefan Berger TPMEmulator *tpm_emu = TPM_EMULATOR(tb); 84838ab74e7SStefan Berger 84938ab74e7SStefan Berger trace_tpm_emulator_pre_save(); 85038ab74e7SStefan Berger 85138ab74e7SStefan Berger tpm_backend_finish_sync(tb); 85238ab74e7SStefan Berger 85338ab74e7SStefan Berger /* get the state blobs from the TPM */ 85438ab74e7SStefan Berger return tpm_emulator_get_state_blobs(tpm_emu); 85538ab74e7SStefan Berger } 85638ab74e7SStefan Berger 85738ab74e7SStefan Berger /* 85838ab74e7SStefan Berger * Load the TPM state blobs into the TPM. 85938ab74e7SStefan Berger * 86038ab74e7SStefan Berger * Returns negative errno codes in case of error. 86138ab74e7SStefan Berger */ 86238ab74e7SStefan Berger static int tpm_emulator_post_load(void *opaque, int version_id) 86338ab74e7SStefan Berger { 86438ab74e7SStefan Berger TPMBackend *tb = opaque; 86538ab74e7SStefan Berger int ret; 86638ab74e7SStefan Berger 86738ab74e7SStefan Berger ret = tpm_emulator_set_state_blobs(tb); 86838ab74e7SStefan Berger if (ret < 0) { 86938ab74e7SStefan Berger return ret; 87038ab74e7SStefan Berger } 87138ab74e7SStefan Berger 87238ab74e7SStefan Berger if (tpm_emulator_startup_tpm_resume(tb, 0, true) < 0) { 87338ab74e7SStefan Berger return -EIO; 87438ab74e7SStefan Berger } 87538ab74e7SStefan Berger 87638ab74e7SStefan Berger return 0; 87738ab74e7SStefan Berger } 87838ab74e7SStefan Berger 87938ab74e7SStefan Berger static const VMStateDescription vmstate_tpm_emulator = { 88038ab74e7SStefan Berger .name = "tpm-emulator", 88138ab74e7SStefan Berger .version_id = 0, 88238ab74e7SStefan Berger .pre_save = tpm_emulator_pre_save, 88338ab74e7SStefan Berger .post_load = tpm_emulator_post_load, 88438ab74e7SStefan Berger .fields = (VMStateField[]) { 88538ab74e7SStefan Berger VMSTATE_UINT32(state_blobs.permanent_flags, TPMEmulator), 88638ab74e7SStefan Berger VMSTATE_UINT32(state_blobs.permanent.size, TPMEmulator), 88738ab74e7SStefan Berger VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.permanent.buffer, 88838ab74e7SStefan Berger TPMEmulator, 0, 0, 88938ab74e7SStefan Berger state_blobs.permanent.size), 89038ab74e7SStefan Berger 89138ab74e7SStefan Berger VMSTATE_UINT32(state_blobs.volatil_flags, TPMEmulator), 89238ab74e7SStefan Berger VMSTATE_UINT32(state_blobs.volatil.size, TPMEmulator), 89338ab74e7SStefan Berger VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.volatil.buffer, 89438ab74e7SStefan Berger TPMEmulator, 0, 0, 89538ab74e7SStefan Berger state_blobs.volatil.size), 89638ab74e7SStefan Berger 89738ab74e7SStefan Berger VMSTATE_UINT32(state_blobs.savestate_flags, TPMEmulator), 89838ab74e7SStefan Berger VMSTATE_UINT32(state_blobs.savestate.size, TPMEmulator), 89938ab74e7SStefan Berger VMSTATE_VBUFFER_ALLOC_UINT32(state_blobs.savestate.buffer, 90038ab74e7SStefan Berger TPMEmulator, 0, 0, 90138ab74e7SStefan Berger state_blobs.savestate.size), 90238ab74e7SStefan Berger 90338ab74e7SStefan Berger VMSTATE_END_OF_LIST() 90438ab74e7SStefan Berger } 90538ab74e7SStefan Berger }; 90638ab74e7SStefan Berger 907f4ede81eSAmarnath Valluri static void tpm_emulator_inst_init(Object *obj) 908f4ede81eSAmarnath Valluri { 909f4ede81eSAmarnath Valluri TPMEmulator *tpm_emu = TPM_EMULATOR(obj); 910f4ede81eSAmarnath Valluri 9119d9dcd96SStefan Berger trace_tpm_emulator_inst_init(); 9129d9dcd96SStefan Berger 913f4ede81eSAmarnath Valluri tpm_emu->options = g_new0(TPMEmulatorOptions, 1); 914f4ede81eSAmarnath Valluri tpm_emu->cur_locty_number = ~0; 91517b1af77SMarc-André Lureau qemu_mutex_init(&tpm_emu->mutex); 91638ab74e7SStefan Berger 9171df2c9a2SPeter Xu vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, 9181df2c9a2SPeter Xu &vmstate_tpm_emulator, obj); 919f4ede81eSAmarnath Valluri } 920f4ede81eSAmarnath Valluri 921f4ede81eSAmarnath Valluri /* 922f4ede81eSAmarnath Valluri * Gracefully shut down the external TPM 923f4ede81eSAmarnath Valluri */ 924f4ede81eSAmarnath Valluri static void tpm_emulator_shutdown(TPMEmulator *tpm_emu) 925f4ede81eSAmarnath Valluri { 926f4ede81eSAmarnath Valluri ptm_res res; 927f4ede81eSAmarnath Valluri 92817b1af77SMarc-André Lureau if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SHUTDOWN, &res, 0, sizeof(res)) < 0) { 929f4ede81eSAmarnath Valluri error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s", 930f4ede81eSAmarnath Valluri strerror(errno)); 931f4ede81eSAmarnath Valluri } else if (res != 0) { 9327e095e84SStefan Berger error_report("tpm-emulator: TPM result for shutdown: 0x%x %s", 9337e095e84SStefan Berger be32_to_cpu(res), tpm_emulator_strerror(be32_to_cpu(res))); 934f4ede81eSAmarnath Valluri } 935f4ede81eSAmarnath Valluri } 936f4ede81eSAmarnath Valluri 937f4ede81eSAmarnath Valluri static void tpm_emulator_inst_finalize(Object *obj) 938f4ede81eSAmarnath Valluri { 939f4ede81eSAmarnath Valluri TPMEmulator *tpm_emu = TPM_EMULATOR(obj); 94038ab74e7SStefan Berger TPMBlobBuffers *state_blobs = &tpm_emu->state_blobs; 941f4ede81eSAmarnath Valluri 942f4ede81eSAmarnath Valluri tpm_emulator_shutdown(tpm_emu); 943f4ede81eSAmarnath Valluri 944f4ede81eSAmarnath Valluri object_unref(OBJECT(tpm_emu->data_ioc)); 945f4ede81eSAmarnath Valluri 946f4ede81eSAmarnath Valluri qemu_chr_fe_deinit(&tpm_emu->ctrl_chr, false); 947f4ede81eSAmarnath Valluri 948f4ede81eSAmarnath Valluri qapi_free_TPMEmulatorOptions(tpm_emu->options); 949f4ede81eSAmarnath Valluri 950f4ede81eSAmarnath Valluri if (tpm_emu->migration_blocker) { 951f4ede81eSAmarnath Valluri migrate_del_blocker(tpm_emu->migration_blocker); 952f4ede81eSAmarnath Valluri error_free(tpm_emu->migration_blocker); 953f4ede81eSAmarnath Valluri } 95417b1af77SMarc-André Lureau 95538ab74e7SStefan Berger tpm_sized_buffer_reset(&state_blobs->volatil); 95638ab74e7SStefan Berger tpm_sized_buffer_reset(&state_blobs->permanent); 95738ab74e7SStefan Berger tpm_sized_buffer_reset(&state_blobs->savestate); 95838ab74e7SStefan Berger 95917b1af77SMarc-André Lureau qemu_mutex_destroy(&tpm_emu->mutex); 96038ab74e7SStefan Berger 96138ab74e7SStefan Berger vmstate_unregister(NULL, &vmstate_tpm_emulator, obj); 962f4ede81eSAmarnath Valluri } 963f4ede81eSAmarnath Valluri 964f4ede81eSAmarnath Valluri static void tpm_emulator_class_init(ObjectClass *klass, void *data) 965f4ede81eSAmarnath Valluri { 966f4ede81eSAmarnath Valluri TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); 967d31076baSMarc-André Lureau 968d31076baSMarc-André Lureau tbc->type = TPM_TYPE_EMULATOR; 969d31076baSMarc-André Lureau tbc->opts = tpm_emulator_cmdline_opts; 970d31076baSMarc-André Lureau tbc->desc = "TPM emulator backend driver"; 971d31076baSMarc-André Lureau tbc->create = tpm_emulator_create; 972d31076baSMarc-André Lureau tbc->startup_tpm = tpm_emulator_startup_tpm; 973d31076baSMarc-André Lureau tbc->cancel_cmd = tpm_emulator_cancel_cmd; 974d31076baSMarc-André Lureau tbc->get_tpm_established_flag = tpm_emulator_get_tpm_established_flag; 975d31076baSMarc-André Lureau tbc->reset_tpm_established_flag = tpm_emulator_reset_tpm_established_flag; 976d31076baSMarc-André Lureau tbc->get_tpm_version = tpm_emulator_get_tpm_version; 977b21e6aafSStefan Berger tbc->get_buffer_size = tpm_emulator_get_buffer_size; 978d31076baSMarc-André Lureau tbc->get_tpm_options = tpm_emulator_get_tpm_options; 979d31076baSMarc-André Lureau 980f4ede81eSAmarnath Valluri tbc->handle_request = tpm_emulator_handle_request; 981f4ede81eSAmarnath Valluri } 982f4ede81eSAmarnath Valluri 983f4ede81eSAmarnath Valluri static const TypeInfo tpm_emulator_info = { 984f4ede81eSAmarnath Valluri .name = TYPE_TPM_EMULATOR, 985f4ede81eSAmarnath Valluri .parent = TYPE_TPM_BACKEND, 986f4ede81eSAmarnath Valluri .instance_size = sizeof(TPMEmulator), 987f4ede81eSAmarnath Valluri .class_init = tpm_emulator_class_init, 988f4ede81eSAmarnath Valluri .instance_init = tpm_emulator_inst_init, 989f4ede81eSAmarnath Valluri .instance_finalize = tpm_emulator_inst_finalize, 990f4ede81eSAmarnath Valluri }; 991f4ede81eSAmarnath Valluri 992f4ede81eSAmarnath Valluri static void tpm_emulator_register(void) 993f4ede81eSAmarnath Valluri { 994f4ede81eSAmarnath Valluri type_register_static(&tpm_emulator_info); 995f4ede81eSAmarnath Valluri } 996f4ede81eSAmarnath Valluri 997f4ede81eSAmarnath Valluri type_init(tpm_emulator_register) 998