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" 191e8a1faeSThomas Huth #include "tpm-emu.h" 201e8a1faeSThomas Huth 21*9bd0e32aSStefan Berger void tpm_emu_test_wait_cond(TPMTestState *s) 221e8a1faeSThomas Huth { 231e8a1faeSThomas Huth gint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; 241e8a1faeSThomas Huth 251e8a1faeSThomas Huth g_mutex_lock(&s->data_mutex); 261e8a1faeSThomas Huth 271e8a1faeSThomas Huth if (!s->data_cond_signal && 281e8a1faeSThomas Huth !g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) { 291e8a1faeSThomas Huth g_assert_not_reached(); 301e8a1faeSThomas Huth } 311e8a1faeSThomas Huth 321e8a1faeSThomas Huth s->data_cond_signal = false; 331e8a1faeSThomas Huth 341e8a1faeSThomas Huth g_mutex_unlock(&s->data_mutex); 351e8a1faeSThomas Huth } 361e8a1faeSThomas Huth 371e8a1faeSThomas Huth static void *tpm_emu_tpm_thread(void *data) 381e8a1faeSThomas Huth { 39*9bd0e32aSStefan Berger TPMTestState *s = data; 401e8a1faeSThomas Huth QIOChannel *ioc = s->tpm_ioc; 411e8a1faeSThomas Huth 421e8a1faeSThomas Huth s->tpm_msg = g_new(struct tpm_hdr, 1); 431e8a1faeSThomas Huth while (true) { 441e8a1faeSThomas Huth int minhlen = sizeof(s->tpm_msg->tag) + sizeof(s->tpm_msg->len); 451e8a1faeSThomas Huth 461e8a1faeSThomas Huth if (!qio_channel_read(ioc, (char *)s->tpm_msg, minhlen, &error_abort)) { 471e8a1faeSThomas Huth break; 481e8a1faeSThomas Huth } 491e8a1faeSThomas Huth s->tpm_msg->tag = be16_to_cpu(s->tpm_msg->tag); 501e8a1faeSThomas Huth s->tpm_msg->len = be32_to_cpu(s->tpm_msg->len); 511e8a1faeSThomas Huth g_assert_cmpint(s->tpm_msg->len, >=, minhlen); 521e8a1faeSThomas Huth 531e8a1faeSThomas Huth s->tpm_msg = g_realloc(s->tpm_msg, s->tpm_msg->len); 541e8a1faeSThomas Huth qio_channel_read(ioc, (char *)&s->tpm_msg->code, 551e8a1faeSThomas Huth s->tpm_msg->len - minhlen, &error_abort); 561e8a1faeSThomas Huth s->tpm_msg->code = be32_to_cpu(s->tpm_msg->code); 571e8a1faeSThomas Huth 581e8a1faeSThomas Huth /* reply error */ 591e8a1faeSThomas Huth s->tpm_msg->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS); 601e8a1faeSThomas Huth s->tpm_msg->len = cpu_to_be32(sizeof(struct tpm_hdr)); 611e8a1faeSThomas Huth s->tpm_msg->code = cpu_to_be32(TPM_RC_FAILURE); 621e8a1faeSThomas Huth qio_channel_write(ioc, (char *)s->tpm_msg, be32_to_cpu(s->tpm_msg->len), 631e8a1faeSThomas Huth &error_abort); 641e8a1faeSThomas Huth } 651e8a1faeSThomas Huth 661e8a1faeSThomas Huth g_free(s->tpm_msg); 671e8a1faeSThomas Huth s->tpm_msg = NULL; 681e8a1faeSThomas Huth object_unref(OBJECT(s->tpm_ioc)); 691e8a1faeSThomas Huth return NULL; 701e8a1faeSThomas Huth } 711e8a1faeSThomas Huth 721e8a1faeSThomas Huth void *tpm_emu_ctrl_thread(void *data) 731e8a1faeSThomas Huth { 74*9bd0e32aSStefan Berger TPMTestState *s = data; 751e8a1faeSThomas Huth QIOChannelSocket *lioc = qio_channel_socket_new(); 761e8a1faeSThomas Huth QIOChannel *ioc; 771e8a1faeSThomas Huth 781e8a1faeSThomas Huth qio_channel_socket_listen_sync(lioc, s->addr, 1, &error_abort); 791e8a1faeSThomas Huth 801e8a1faeSThomas Huth g_mutex_lock(&s->data_mutex); 811e8a1faeSThomas Huth s->data_cond_signal = true; 821e8a1faeSThomas Huth g_mutex_unlock(&s->data_mutex); 831e8a1faeSThomas Huth g_cond_signal(&s->data_cond); 841e8a1faeSThomas Huth 851e8a1faeSThomas Huth qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN); 861e8a1faeSThomas Huth ioc = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort)); 871e8a1faeSThomas Huth g_assert(ioc); 881e8a1faeSThomas Huth 891e8a1faeSThomas Huth { 901e8a1faeSThomas Huth uint32_t cmd = 0; 911e8a1faeSThomas Huth struct iovec iov = { .iov_base = &cmd, .iov_len = sizeof(cmd) }; 921e8a1faeSThomas Huth int *pfd = NULL; 931e8a1faeSThomas Huth size_t nfd = 0; 941e8a1faeSThomas Huth 951e8a1faeSThomas Huth qio_channel_readv_full(ioc, &iov, 1, &pfd, &nfd, &error_abort); 961e8a1faeSThomas Huth cmd = be32_to_cpu(cmd); 971e8a1faeSThomas Huth g_assert_cmpint(cmd, ==, CMD_SET_DATAFD); 981e8a1faeSThomas Huth g_assert_cmpint(nfd, ==, 1); 991e8a1faeSThomas Huth s->tpm_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(*pfd, &error_abort)); 1001e8a1faeSThomas Huth g_free(pfd); 1011e8a1faeSThomas Huth 1021e8a1faeSThomas Huth cmd = 0; 1031e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&cmd, sizeof(cmd), &error_abort); 1041e8a1faeSThomas Huth 1051e8a1faeSThomas Huth s->emu_tpm_thread = g_thread_new(NULL, tpm_emu_tpm_thread, s); 1061e8a1faeSThomas Huth } 1071e8a1faeSThomas Huth 1081e8a1faeSThomas Huth while (true) { 1091e8a1faeSThomas Huth uint32_t cmd; 1101e8a1faeSThomas Huth ssize_t ret; 1111e8a1faeSThomas Huth 1121e8a1faeSThomas Huth ret = qio_channel_read(ioc, (char *)&cmd, sizeof(cmd), NULL); 1131e8a1faeSThomas Huth if (ret <= 0) { 1141e8a1faeSThomas Huth break; 1151e8a1faeSThomas Huth } 1161e8a1faeSThomas Huth 1171e8a1faeSThomas Huth cmd = be32_to_cpu(cmd); 1181e8a1faeSThomas Huth switch (cmd) { 1191e8a1faeSThomas Huth case CMD_GET_CAPABILITY: { 1201e8a1faeSThomas Huth ptm_cap cap = cpu_to_be64(0x3fff); 1211e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&cap, sizeof(cap), &error_abort); 1221e8a1faeSThomas Huth break; 1231e8a1faeSThomas Huth } 1241e8a1faeSThomas Huth case CMD_INIT: { 1251e8a1faeSThomas Huth ptm_init init; 1261e8a1faeSThomas Huth qio_channel_read(ioc, (char *)&init.u.req, sizeof(init.u.req), 1271e8a1faeSThomas Huth &error_abort); 1281e8a1faeSThomas Huth init.u.resp.tpm_result = 0; 1291e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&init.u.resp, sizeof(init.u.resp), 1301e8a1faeSThomas Huth &error_abort); 1311e8a1faeSThomas Huth break; 1321e8a1faeSThomas Huth } 1331e8a1faeSThomas Huth case CMD_SHUTDOWN: { 1341e8a1faeSThomas Huth ptm_res res = 0; 1351e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort); 1361e8a1faeSThomas Huth /* the tpm data thread is expected to finish now */ 1371e8a1faeSThomas Huth g_thread_join(s->emu_tpm_thread); 1381e8a1faeSThomas Huth break; 1391e8a1faeSThomas Huth } 1401e8a1faeSThomas Huth case CMD_STOP: { 1411e8a1faeSThomas Huth ptm_res res = 0; 1421e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&res, sizeof(res), &error_abort); 1431e8a1faeSThomas Huth break; 1441e8a1faeSThomas Huth } 1451e8a1faeSThomas Huth case CMD_SET_BUFFERSIZE: { 1461e8a1faeSThomas Huth ptm_setbuffersize sbs; 1471e8a1faeSThomas Huth qio_channel_read(ioc, (char *)&sbs.u.req, sizeof(sbs.u.req), 1481e8a1faeSThomas Huth &error_abort); 1491e8a1faeSThomas Huth sbs.u.resp.buffersize = sbs.u.req.buffersize ?: cpu_to_be32(4096); 1501e8a1faeSThomas Huth sbs.u.resp.tpm_result = 0; 1511e8a1faeSThomas Huth sbs.u.resp.minsize = cpu_to_be32(128); 1521e8a1faeSThomas Huth sbs.u.resp.maxsize = cpu_to_be32(4096); 1531e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&sbs.u.resp, sizeof(sbs.u.resp), 1541e8a1faeSThomas Huth &error_abort); 1551e8a1faeSThomas Huth break; 1561e8a1faeSThomas Huth } 1571e8a1faeSThomas Huth case CMD_SET_LOCALITY: { 1581e8a1faeSThomas Huth ptm_loc loc; 1591e8a1faeSThomas Huth /* Note: this time it's not u.req / u.resp... */ 1601e8a1faeSThomas Huth qio_channel_read(ioc, (char *)&loc, sizeof(loc), &error_abort); 1611e8a1faeSThomas Huth g_assert_cmpint(loc.u.req.loc, ==, 0); 1621e8a1faeSThomas Huth loc.u.resp.tpm_result = 0; 1631e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&loc, sizeof(loc), &error_abort); 1641e8a1faeSThomas Huth break; 1651e8a1faeSThomas Huth } 1661e8a1faeSThomas Huth case CMD_GET_TPMESTABLISHED: { 1671e8a1faeSThomas Huth ptm_est est = { 1681e8a1faeSThomas Huth .u.resp.bit = 0, 1691e8a1faeSThomas Huth }; 1701e8a1faeSThomas Huth qio_channel_write(ioc, (char *)&est, sizeof(est), &error_abort); 1711e8a1faeSThomas Huth break; 1721e8a1faeSThomas Huth } 1731e8a1faeSThomas Huth default: 1741e8a1faeSThomas Huth g_debug("unimplemented %u", cmd); 1751e8a1faeSThomas Huth g_assert_not_reached(); 1761e8a1faeSThomas Huth } 1771e8a1faeSThomas Huth } 1781e8a1faeSThomas Huth 1791e8a1faeSThomas Huth object_unref(OBJECT(ioc)); 1801e8a1faeSThomas Huth object_unref(OBJECT(lioc)); 1811e8a1faeSThomas Huth return NULL; 1821e8a1faeSThomas Huth } 183