11e8a1faeSThomas Huth /* 21e8a1faeSThomas Huth * Minimal TPM emulator for TPM test cases 31e8a1faeSThomas Huth * 41e8a1faeSThomas Huth * Copyright (c) 2018 Red Hat, Inc. 51e8a1faeSThomas Huth * 61e8a1faeSThomas Huth * Authors: 71e8a1faeSThomas Huth * Marc-André Lureau <marcandre.lureau@redhat.com> 81e8a1faeSThomas Huth * 91e8a1faeSThomas Huth * This work is licensed under the terms of the GNU GPL, version 2 or later. 101e8a1faeSThomas Huth * See the COPYING file in the top-level directory. 111e8a1faeSThomas Huth */ 121e8a1faeSThomas Huth 131e8a1faeSThomas Huth #include "qemu/osdep.h" 141e8a1faeSThomas Huth #include <glib/gstdio.h> 151e8a1faeSThomas Huth 16ca64b086SPhilippe Mathieu-Daudé #include "backends/tpm/tpm_ioctl.h" 171e8a1faeSThomas Huth #include "io/channel-socket.h" 181e8a1faeSThomas Huth #include "qapi/error.h" 19*58edc32cSStefan Berger #include "qapi/qmp/qlist.h" 20*58edc32cSStefan Berger #include "qapi/qmp/qstring.h" 211e8a1faeSThomas Huth #include "tpm-emu.h" 221e8a1faeSThomas Huth 239bd0e32aSStefan Berger void tpm_emu_test_wait_cond(TPMTestState *s) 241e8a1faeSThomas Huth { 251e8a1faeSThomas Huth gint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; 261e8a1faeSThomas Huth 271e8a1faeSThomas Huth g_mutex_lock(&s->data_mutex); 281e8a1faeSThomas Huth 291e8a1faeSThomas Huth if (!s->data_cond_signal && 301e8a1faeSThomas Huth !g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) { 311e8a1faeSThomas Huth g_assert_not_reached(); 321e8a1faeSThomas Huth } 331e8a1faeSThomas Huth 341e8a1faeSThomas Huth s->data_cond_signal = false; 351e8a1faeSThomas Huth 361e8a1faeSThomas Huth g_mutex_unlock(&s->data_mutex); 371e8a1faeSThomas Huth } 381e8a1faeSThomas Huth 391e8a1faeSThomas Huth static void *tpm_emu_tpm_thread(void *data) 401e8a1faeSThomas Huth { 419bd0e32aSStefan Berger TPMTestState *s = data; 421e8a1faeSThomas Huth QIOChannel *ioc = s->tpm_ioc; 431e8a1faeSThomas Huth 441e8a1faeSThomas Huth s->tpm_msg = g_new(struct tpm_hdr, 1); 451e8a1faeSThomas Huth while (true) { 461e8a1faeSThomas Huth int minhlen = sizeof(s->tpm_msg->tag) + sizeof(s->tpm_msg->len); 471e8a1faeSThomas Huth 481e8a1faeSThomas Huth if (!qio_channel_read(ioc, (char *)s->tpm_msg, minhlen, &error_abort)) { 491e8a1faeSThomas Huth break; 501e8a1faeSThomas Huth } 511e8a1faeSThomas Huth s->tpm_msg->tag = be16_to_cpu(s->tpm_msg->tag); 521e8a1faeSThomas Huth s->tpm_msg->len = be32_to_cpu(s->tpm_msg->len); 531e8a1faeSThomas Huth g_assert_cmpint(s->tpm_msg->len, >=, minhlen); 541e8a1faeSThomas Huth 551e8a1faeSThomas Huth s->tpm_msg = g_realloc(s->tpm_msg, s->tpm_msg->len); 561e8a1faeSThomas Huth qio_channel_read(ioc, (char *)&s->tpm_msg->code, 571e8a1faeSThomas Huth s->tpm_msg->len - minhlen, &error_abort); 581e8a1faeSThomas Huth s->tpm_msg->code = be32_to_cpu(s->tpm_msg->code); 591e8a1faeSThomas Huth 601e8a1faeSThomas Huth /* reply error */ 6109b20a14SStefan Berger switch (s->tpm_version) { 6209b20a14SStefan Berger case TPM_VERSION_2_0: 631e8a1faeSThomas Huth s->tpm_msg->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS); 641e8a1faeSThomas Huth s->tpm_msg->len = cpu_to_be32(sizeof(struct tpm_hdr)); 651e8a1faeSThomas Huth s->tpm_msg->code = cpu_to_be32(TPM_RC_FAILURE); 6609b20a14SStefan Berger break; 67044d55dcSStefan Berger case TPM_VERSION_1_2: 68044d55dcSStefan Berger s->tpm_msg->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND); 69044d55dcSStefan Berger s->tpm_msg->len = cpu_to_be32(sizeof(struct tpm_hdr)); 70044d55dcSStefan Berger s->tpm_msg->code = cpu_to_be32(TPM_FAIL); 71044d55dcSStefan Berger break; 7209b20a14SStefan Berger default: 7309b20a14SStefan Berger g_debug("unsupport TPM version %u", s->tpm_version); 7409b20a14SStefan Berger g_assert_not_reached(); 7509b20a14SStefan Berger } 761e8a1faeSThomas Huth qio_channel_write(ioc, (char *)s->tpm_msg, be32_to_cpu(s->tpm_msg->len), 771e8a1faeSThomas Huth &error_abort); 781e8a1faeSThomas Huth } 791e8a1faeSThomas Huth 801e8a1faeSThomas Huth g_free(s->tpm_msg); 811e8a1faeSThomas Huth s->tpm_msg = NULL; 821e8a1faeSThomas Huth object_unref(OBJECT(s->tpm_ioc)); 831e8a1faeSThomas Huth return NULL; 841e8a1faeSThomas Huth } 851e8a1faeSThomas Huth 861e8a1faeSThomas Huth void *tpm_emu_ctrl_thread(void *data) 871e8a1faeSThomas Huth { 889bd0e32aSStefan Berger TPMTestState *s = data; 891e8a1faeSThomas Huth QIOChannelSocket *lioc = qio_channel_socket_new(); 901e8a1faeSThomas Huth QIOChannel *ioc; 911e8a1faeSThomas Huth 921e8a1faeSThomas Huth qio_channel_socket_listen_sync(lioc, s->addr, 1, &error_abort); 931e8a1faeSThomas Huth 941e8a1faeSThomas Huth g_mutex_lock(&s->data_mutex); 951e8a1faeSThomas Huth s->data_cond_signal = true; 961e8a1faeSThomas Huth g_mutex_unlock(&s->data_mutex); 971e8a1faeSThomas Huth g_cond_signal(&s->data_cond); 981e8a1faeSThomas Huth 991e8a1faeSThomas Huth qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN); 1001e8a1faeSThomas Huth ioc = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort)); 1011e8a1faeSThomas Huth g_assert(ioc); 1021e8a1faeSThomas Huth 1031e8a1faeSThomas Huth { 1041e8a1faeSThomas Huth uint32_t cmd = 0; 1051e8a1faeSThomas Huth struct iovec iov = { .iov_base = &cmd, .iov_len = sizeof(cmd) }; 1061e8a1faeSThomas Huth int *pfd = NULL; 1071e8a1faeSThomas Huth size_t nfd = 0; 1081e8a1faeSThomas Huth 1091e8a1faeSThomas Huth qio_channel_readv_full(ioc, &iov, 1, &pfd, &nfd, &error_abort); 1101e8a1faeSThomas Huth cmd = be32_to_cpu(cmd); 1111e8a1faeSThomas Huth g_assert_cmpint(cmd, ==, CMD_SET_DATAFD); 1121e8a1faeSThomas Huth g_assert_cmpint(nfd, ==, 1); 1131e8a1faeSThomas Huth s->tpm_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(*pfd, &error_abort)); 1141e8a1faeSThomas Huth g_free(pfd); 1151e8a1faeSThomas Huth 1161e8a1faeSThomas Huth cmd = 0; 1171e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&cmd, sizeof(cmd), &error_abort); 1181e8a1faeSThomas Huth 1191e8a1faeSThomas Huth s->emu_tpm_thread = g_thread_new(NULL, tpm_emu_tpm_thread, s); 1201e8a1faeSThomas Huth } 1211e8a1faeSThomas Huth 1221e8a1faeSThomas Huth while (true) { 1231e8a1faeSThomas Huth uint32_t cmd; 1241e8a1faeSThomas Huth ssize_t ret; 1251e8a1faeSThomas Huth 1261e8a1faeSThomas Huth ret = qio_channel_read(ioc, (char *)&cmd, sizeof(cmd), NULL); 1271e8a1faeSThomas Huth if (ret <= 0) { 1281e8a1faeSThomas Huth break; 1291e8a1faeSThomas Huth } 1301e8a1faeSThomas Huth 1311e8a1faeSThomas Huth cmd = be32_to_cpu(cmd); 1321e8a1faeSThomas Huth switch (cmd) { 1331e8a1faeSThomas Huth case CMD_GET_CAPABILITY: { 1341e8a1faeSThomas Huth ptm_cap cap = cpu_to_be64(0x3fff); 1351e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&cap, sizeof(cap), &error_abort); 1361e8a1faeSThomas Huth break; 1371e8a1faeSThomas Huth } 1381e8a1faeSThomas Huth case CMD_INIT: { 1391e8a1faeSThomas Huth ptm_init init; 1401e8a1faeSThomas Huth qio_channel_read(ioc, (char *)&init.u.req, sizeof(init.u.req), 1411e8a1faeSThomas Huth &error_abort); 1421e8a1faeSThomas Huth init.u.resp.tpm_result = 0; 1431e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&init.u.resp, sizeof(init.u.resp), 1441e8a1faeSThomas Huth &error_abort); 1451e8a1faeSThomas Huth break; 1461e8a1faeSThomas Huth } 1471e8a1faeSThomas Huth case CMD_SHUTDOWN: { 1481e8a1faeSThomas Huth ptm_res res = 0; 1491e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort); 1501e8a1faeSThomas Huth /* the tpm data thread is expected to finish now */ 1511e8a1faeSThomas Huth g_thread_join(s->emu_tpm_thread); 1521e8a1faeSThomas Huth break; 1531e8a1faeSThomas Huth } 1541e8a1faeSThomas Huth case CMD_STOP: { 1551e8a1faeSThomas Huth ptm_res res = 0; 1561e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort); 1571e8a1faeSThomas Huth break; 1581e8a1faeSThomas Huth } 1591e8a1faeSThomas Huth case CMD_SET_BUFFERSIZE: { 1601e8a1faeSThomas Huth ptm_setbuffersize sbs; 1611e8a1faeSThomas Huth qio_channel_read(ioc, (char *)&sbs.u.req, sizeof(sbs.u.req), 1621e8a1faeSThomas Huth &error_abort); 1631e8a1faeSThomas Huth sbs.u.resp.buffersize = sbs.u.req.buffersize ?: cpu_to_be32(4096); 1641e8a1faeSThomas Huth sbs.u.resp.tpm_result = 0; 1651e8a1faeSThomas Huth sbs.u.resp.minsize = cpu_to_be32(128); 1661e8a1faeSThomas Huth sbs.u.resp.maxsize = cpu_to_be32(4096); 1671e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&sbs.u.resp, sizeof(sbs.u.resp), 1681e8a1faeSThomas Huth &error_abort); 1691e8a1faeSThomas Huth break; 1701e8a1faeSThomas Huth } 1711e8a1faeSThomas Huth case CMD_SET_LOCALITY: { 1721e8a1faeSThomas Huth ptm_loc loc; 1731e8a1faeSThomas Huth /* Note: this time it's not u.req / u.resp... */ 1741e8a1faeSThomas Huth qio_channel_read(ioc, (char *)&loc, sizeof(loc), &error_abort); 1751e8a1faeSThomas Huth g_assert_cmpint(loc.u.req.loc, ==, 0); 1761e8a1faeSThomas Huth loc.u.resp.tpm_result = 0; 1771e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&loc, sizeof(loc), &error_abort); 1781e8a1faeSThomas Huth break; 1791e8a1faeSThomas Huth } 1801e8a1faeSThomas Huth case CMD_GET_TPMESTABLISHED: { 1811e8a1faeSThomas Huth ptm_est est = { 1821e8a1faeSThomas Huth .u.resp.bit = 0, 1831e8a1faeSThomas Huth }; 1841e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&est, sizeof(est), &error_abort); 1851e8a1faeSThomas Huth break; 1861e8a1faeSThomas Huth } 1871e8a1faeSThomas Huth default: 1881e8a1faeSThomas Huth g_debug("unimplemented %u", cmd); 1891e8a1faeSThomas Huth g_assert_not_reached(); 1901e8a1faeSThomas Huth } 1911e8a1faeSThomas Huth } 1921e8a1faeSThomas Huth 1931e8a1faeSThomas Huth object_unref(OBJECT(ioc)); 1941e8a1faeSThomas Huth object_unref(OBJECT(lioc)); 1951e8a1faeSThomas Huth return NULL; 1961e8a1faeSThomas Huth } 197*58edc32cSStefan Berger 198*58edc32cSStefan Berger bool tpm_model_is_available(const char *args, const char *tpm_if) 199*58edc32cSStefan Berger { 200*58edc32cSStefan Berger QTestState *qts; 201*58edc32cSStefan Berger QDict *rsp_tpm; 202*58edc32cSStefan Berger bool ret = false; 203*58edc32cSStefan Berger 204*58edc32cSStefan Berger qts = qtest_init(args); 205*58edc32cSStefan Berger if (!qts) { 206*58edc32cSStefan Berger return false; 207*58edc32cSStefan Berger } 208*58edc32cSStefan Berger 209*58edc32cSStefan Berger rsp_tpm = qtest_qmp(qts, "{ 'execute': 'query-tpm'}"); 210*58edc32cSStefan Berger if (!qdict_haskey(rsp_tpm, "error")) { 211*58edc32cSStefan Berger QDict *rsp_models = qtest_qmp(qts, 212*58edc32cSStefan Berger "{ 'execute': 'query-tpm-models'}"); 213*58edc32cSStefan Berger if (qdict_haskey(rsp_models, "return")) { 214*58edc32cSStefan Berger QList *models = qdict_get_qlist(rsp_models, "return"); 215*58edc32cSStefan Berger QListEntry *e; 216*58edc32cSStefan Berger 217*58edc32cSStefan Berger QLIST_FOREACH_ENTRY(models, e) { 218*58edc32cSStefan Berger QString *s = qobject_to(QString, qlist_entry_obj(e)); 219*58edc32cSStefan Berger const char *ename = qstring_get_str(s); 220*58edc32cSStefan Berger if (!strcmp(ename, tpm_if)) { 221*58edc32cSStefan Berger ret = true; 222*58edc32cSStefan Berger break; 223*58edc32cSStefan Berger } 224*58edc32cSStefan Berger } 225*58edc32cSStefan Berger } 226*58edc32cSStefan Berger qobject_unref(rsp_models); 227*58edc32cSStefan Berger } 228*58edc32cSStefan Berger qobject_unref(rsp_tpm); 229*58edc32cSStefan Berger qtest_quit(qts); 230*58edc32cSStefan Berger 231*58edc32cSStefan Berger return ret; 232*58edc32cSStefan Berger } 233