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" 1958edc32cSStefan Berger #include "qapi/qmp/qlist.h" 2058edc32cSStefan 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 39255b00b4SStefan Berger static void tpm_emu_close_ioc(void *ioc) 40255b00b4SStefan Berger { 41255b00b4SStefan Berger qio_channel_close(ioc, NULL); 42255b00b4SStefan Berger } 43255b00b4SStefan Berger 441e8a1faeSThomas Huth static void *tpm_emu_tpm_thread(void *data) 451e8a1faeSThomas Huth { 469bd0e32aSStefan Berger TPMTestState *s = data; 471e8a1faeSThomas Huth QIOChannel *ioc = s->tpm_ioc; 481e8a1faeSThomas Huth 49255b00b4SStefan Berger qtest_add_abrt_handler(tpm_emu_close_ioc, ioc); 50255b00b4SStefan Berger 511e8a1faeSThomas Huth s->tpm_msg = g_new(struct tpm_hdr, 1); 521e8a1faeSThomas Huth while (true) { 531e8a1faeSThomas Huth int minhlen = sizeof(s->tpm_msg->tag) + sizeof(s->tpm_msg->len); 541e8a1faeSThomas Huth 551e8a1faeSThomas Huth if (!qio_channel_read(ioc, (char *)s->tpm_msg, minhlen, &error_abort)) { 561e8a1faeSThomas Huth break; 571e8a1faeSThomas Huth } 581e8a1faeSThomas Huth s->tpm_msg->tag = be16_to_cpu(s->tpm_msg->tag); 591e8a1faeSThomas Huth s->tpm_msg->len = be32_to_cpu(s->tpm_msg->len); 601e8a1faeSThomas Huth g_assert_cmpint(s->tpm_msg->len, >=, minhlen); 611e8a1faeSThomas Huth 621e8a1faeSThomas Huth s->tpm_msg = g_realloc(s->tpm_msg, s->tpm_msg->len); 631e8a1faeSThomas Huth qio_channel_read(ioc, (char *)&s->tpm_msg->code, 641e8a1faeSThomas Huth s->tpm_msg->len - minhlen, &error_abort); 651e8a1faeSThomas Huth s->tpm_msg->code = be32_to_cpu(s->tpm_msg->code); 661e8a1faeSThomas Huth 671e8a1faeSThomas Huth /* reply error */ 6809b20a14SStefan Berger switch (s->tpm_version) { 6909b20a14SStefan Berger case TPM_VERSION_2_0: 701e8a1faeSThomas Huth s->tpm_msg->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS); 711e8a1faeSThomas Huth s->tpm_msg->len = cpu_to_be32(sizeof(struct tpm_hdr)); 721e8a1faeSThomas Huth s->tpm_msg->code = cpu_to_be32(TPM_RC_FAILURE); 7309b20a14SStefan Berger break; 74044d55dcSStefan Berger case TPM_VERSION_1_2: 75044d55dcSStefan Berger s->tpm_msg->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND); 76044d55dcSStefan Berger s->tpm_msg->len = cpu_to_be32(sizeof(struct tpm_hdr)); 77044d55dcSStefan Berger s->tpm_msg->code = cpu_to_be32(TPM_FAIL); 78044d55dcSStefan Berger break; 7909b20a14SStefan Berger default: 8009b20a14SStefan Berger g_debug("unsupport TPM version %u", s->tpm_version); 8109b20a14SStefan Berger g_assert_not_reached(); 8209b20a14SStefan Berger } 831e8a1faeSThomas Huth qio_channel_write(ioc, (char *)s->tpm_msg, be32_to_cpu(s->tpm_msg->len), 841e8a1faeSThomas Huth &error_abort); 851e8a1faeSThomas Huth } 861e8a1faeSThomas Huth 87255b00b4SStefan Berger qtest_remove_abrt_handler(ioc); 881e8a1faeSThomas Huth g_free(s->tpm_msg); 891e8a1faeSThomas Huth s->tpm_msg = NULL; 901e8a1faeSThomas Huth object_unref(OBJECT(s->tpm_ioc)); 911e8a1faeSThomas Huth return NULL; 921e8a1faeSThomas Huth } 931e8a1faeSThomas Huth 941e8a1faeSThomas Huth void *tpm_emu_ctrl_thread(void *data) 951e8a1faeSThomas Huth { 969bd0e32aSStefan Berger TPMTestState *s = data; 971e8a1faeSThomas Huth QIOChannelSocket *lioc = qio_channel_socket_new(); 981e8a1faeSThomas Huth QIOChannel *ioc; 991e8a1faeSThomas Huth 1001e8a1faeSThomas Huth qio_channel_socket_listen_sync(lioc, s->addr, 1, &error_abort); 1011e8a1faeSThomas Huth 1021e8a1faeSThomas Huth g_mutex_lock(&s->data_mutex); 1031e8a1faeSThomas Huth s->data_cond_signal = true; 1041e8a1faeSThomas Huth g_mutex_unlock(&s->data_mutex); 1051e8a1faeSThomas Huth g_cond_signal(&s->data_cond); 1061e8a1faeSThomas Huth 1071e8a1faeSThomas Huth qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN); 1081e8a1faeSThomas Huth ioc = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort)); 1091e8a1faeSThomas Huth g_assert(ioc); 110255b00b4SStefan Berger qtest_add_abrt_handler(tpm_emu_close_ioc, ioc); 1111e8a1faeSThomas Huth 1121e8a1faeSThomas Huth { 1131e8a1faeSThomas Huth uint32_t cmd = 0; 1141e8a1faeSThomas Huth struct iovec iov = { .iov_base = &cmd, .iov_len = sizeof(cmd) }; 1151e8a1faeSThomas Huth int *pfd = NULL; 1161e8a1faeSThomas Huth size_t nfd = 0; 1171e8a1faeSThomas Huth 11884615a19Smanish.mishra qio_channel_readv_full(ioc, &iov, 1, &pfd, &nfd, 0, &error_abort); 1191e8a1faeSThomas Huth cmd = be32_to_cpu(cmd); 1201e8a1faeSThomas Huth g_assert_cmpint(cmd, ==, CMD_SET_DATAFD); 1211e8a1faeSThomas Huth g_assert_cmpint(nfd, ==, 1); 1221e8a1faeSThomas Huth s->tpm_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(*pfd, &error_abort)); 1231e8a1faeSThomas Huth g_free(pfd); 1241e8a1faeSThomas Huth 1251e8a1faeSThomas Huth cmd = 0; 1261e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&cmd, sizeof(cmd), &error_abort); 1271e8a1faeSThomas Huth 1281e8a1faeSThomas Huth s->emu_tpm_thread = g_thread_new(NULL, tpm_emu_tpm_thread, s); 1291e8a1faeSThomas Huth } 1301e8a1faeSThomas Huth 1311e8a1faeSThomas Huth while (true) { 1321e8a1faeSThomas Huth uint32_t cmd; 1331e8a1faeSThomas Huth ssize_t ret; 1341e8a1faeSThomas Huth 1351e8a1faeSThomas Huth ret = qio_channel_read(ioc, (char *)&cmd, sizeof(cmd), NULL); 1361e8a1faeSThomas Huth if (ret <= 0) { 1371e8a1faeSThomas Huth break; 1381e8a1faeSThomas Huth } 1391e8a1faeSThomas Huth 1401e8a1faeSThomas Huth cmd = be32_to_cpu(cmd); 1411e8a1faeSThomas Huth switch (cmd) { 1421e8a1faeSThomas Huth case CMD_GET_CAPABILITY: { 1431e8a1faeSThomas Huth ptm_cap cap = cpu_to_be64(0x3fff); 1441e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&cap, sizeof(cap), &error_abort); 1451e8a1faeSThomas Huth break; 1461e8a1faeSThomas Huth } 1471e8a1faeSThomas Huth case CMD_INIT: { 1481e8a1faeSThomas Huth ptm_init init; 1491e8a1faeSThomas Huth qio_channel_read(ioc, (char *)&init.u.req, sizeof(init.u.req), 1501e8a1faeSThomas Huth &error_abort); 1511e8a1faeSThomas Huth init.u.resp.tpm_result = 0; 1521e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&init.u.resp, sizeof(init.u.resp), 1531e8a1faeSThomas Huth &error_abort); 1541e8a1faeSThomas Huth break; 1551e8a1faeSThomas Huth } 1561e8a1faeSThomas Huth case CMD_SHUTDOWN: { 1571e8a1faeSThomas Huth ptm_res res = 0; 1581e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort); 1591e8a1faeSThomas Huth /* the tpm data thread is expected to finish now */ 1601e8a1faeSThomas Huth g_thread_join(s->emu_tpm_thread); 1611e8a1faeSThomas Huth break; 1621e8a1faeSThomas Huth } 1631e8a1faeSThomas Huth case CMD_STOP: { 1641e8a1faeSThomas Huth ptm_res res = 0; 1651e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort); 1661e8a1faeSThomas Huth break; 1671e8a1faeSThomas Huth } 1681e8a1faeSThomas Huth case CMD_SET_BUFFERSIZE: { 1691e8a1faeSThomas Huth ptm_setbuffersize sbs; 1701e8a1faeSThomas Huth qio_channel_read(ioc, (char *)&sbs.u.req, sizeof(sbs.u.req), 1711e8a1faeSThomas Huth &error_abort); 1721e8a1faeSThomas Huth sbs.u.resp.buffersize = sbs.u.req.buffersize ?: cpu_to_be32(4096); 1731e8a1faeSThomas Huth sbs.u.resp.tpm_result = 0; 1741e8a1faeSThomas Huth sbs.u.resp.minsize = cpu_to_be32(128); 1751e8a1faeSThomas Huth sbs.u.resp.maxsize = cpu_to_be32(4096); 1761e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&sbs.u.resp, sizeof(sbs.u.resp), 1771e8a1faeSThomas Huth &error_abort); 1781e8a1faeSThomas Huth break; 1791e8a1faeSThomas Huth } 1801e8a1faeSThomas Huth case CMD_SET_LOCALITY: { 1811e8a1faeSThomas Huth ptm_loc loc; 1821e8a1faeSThomas Huth /* Note: this time it's not u.req / u.resp... */ 1831e8a1faeSThomas Huth qio_channel_read(ioc, (char *)&loc, sizeof(loc), &error_abort); 1841e8a1faeSThomas Huth g_assert_cmpint(loc.u.req.loc, ==, 0); 1851e8a1faeSThomas Huth loc.u.resp.tpm_result = 0; 1861e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&loc, sizeof(loc), &error_abort); 1871e8a1faeSThomas Huth break; 1881e8a1faeSThomas Huth } 1891e8a1faeSThomas Huth case CMD_GET_TPMESTABLISHED: { 1901e8a1faeSThomas Huth ptm_est est = { 1911e8a1faeSThomas Huth .u.resp.bit = 0, 1921e8a1faeSThomas Huth }; 1931e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&est, sizeof(est), &error_abort); 1941e8a1faeSThomas Huth break; 1951e8a1faeSThomas Huth } 1961e8a1faeSThomas Huth default: 1971e8a1faeSThomas Huth g_debug("unimplemented %u", cmd); 1981e8a1faeSThomas Huth g_assert_not_reached(); 1991e8a1faeSThomas Huth } 2001e8a1faeSThomas Huth } 2011e8a1faeSThomas Huth 202255b00b4SStefan Berger qtest_remove_abrt_handler(ioc); 2031e8a1faeSThomas Huth object_unref(OBJECT(ioc)); 2041e8a1faeSThomas Huth object_unref(OBJECT(lioc)); 2051e8a1faeSThomas Huth return NULL; 2061e8a1faeSThomas Huth } 20758edc32cSStefan Berger 20858edc32cSStefan Berger bool tpm_model_is_available(const char *args, const char *tpm_if) 20958edc32cSStefan Berger { 21058edc32cSStefan Berger QTestState *qts; 21158edc32cSStefan Berger QDict *rsp_tpm; 21258edc32cSStefan Berger bool ret = false; 21358edc32cSStefan Berger 21458edc32cSStefan Berger qts = qtest_init(args); 21558edc32cSStefan Berger if (!qts) { 21658edc32cSStefan Berger return false; 21758edc32cSStefan Berger } 21858edc32cSStefan Berger 21958edc32cSStefan Berger rsp_tpm = qtest_qmp(qts, "{ 'execute': 'query-tpm'}"); 22058edc32cSStefan Berger if (!qdict_haskey(rsp_tpm, "error")) { 22158edc32cSStefan Berger QDict *rsp_models = qtest_qmp(qts, 22258edc32cSStefan Berger "{ 'execute': 'query-tpm-models'}"); 22358edc32cSStefan Berger if (qdict_haskey(rsp_models, "return")) { 22458edc32cSStefan Berger QList *models = qdict_get_qlist(rsp_models, "return"); 22558edc32cSStefan Berger QListEntry *e; 22658edc32cSStefan Berger 22758edc32cSStefan Berger QLIST_FOREACH_ENTRY(models, e) { 22858edc32cSStefan Berger QString *s = qobject_to(QString, qlist_entry_obj(e)); 22958edc32cSStefan Berger const char *ename = qstring_get_str(s); 23058edc32cSStefan Berger if (!strcmp(ename, tpm_if)) { 23158edc32cSStefan Berger ret = true; 23258edc32cSStefan Berger break; 23358edc32cSStefan Berger } 23458edc32cSStefan Berger } 23558edc32cSStefan Berger } 23658edc32cSStefan Berger qobject_unref(rsp_models); 23758edc32cSStefan Berger } 23858edc32cSStefan Berger qobject_unref(rsp_tpm); 23958edc32cSStefan Berger qtest_quit(qts); 24058edc32cSStefan Berger 24158edc32cSStefan Berger return ret; 24258edc32cSStefan Berger } 243