11e8a1faeSThomas Huth /* 21e8a1faeSThomas Huth * QTest testcase for TPM CRB 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 161e8a1faeSThomas Huth #include "hw/acpi/tpm.h" 171e8a1faeSThomas Huth #include "io/channel-socket.h" 181e8a1faeSThomas Huth #include "libqtest-single.h" 191e8a1faeSThomas Huth #include "qemu/module.h" 201e8a1faeSThomas Huth #include "tpm-emu.h" 211e8a1faeSThomas Huth 225166c326SEric Auger /* Not used but needed for linking */ 235166c326SEric Auger uint64_t tpm_tis_base_addr = TPM_TIS_ADDR_BASE; 245166c326SEric Auger 251e8a1faeSThomas Huth #define TPM_CMD "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00" 261e8a1faeSThomas Huth 271e8a1faeSThomas Huth static void tpm_crb_test(const void *data) 281e8a1faeSThomas Huth { 29*9bd0e32aSStefan Berger const TPMTestState *s = data; 301e8a1faeSThomas Huth uint32_t intfid = readl(TPM_CRB_ADDR_BASE + A_CRB_INTF_ID); 311e8a1faeSThomas Huth uint32_t csize = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_SIZE); 321e8a1faeSThomas Huth uint64_t caddr = readq(TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR); 331e8a1faeSThomas Huth uint32_t rsize = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_SIZE); 341e8a1faeSThomas Huth uint64_t raddr = readq(TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR); 351e8a1faeSThomas Huth uint8_t locstate = readb(TPM_CRB_ADDR_BASE + A_CRB_LOC_STATE); 361e8a1faeSThomas Huth uint32_t locctrl = readl(TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL); 371e8a1faeSThomas Huth uint32_t locsts = readl(TPM_CRB_ADDR_BASE + A_CRB_LOC_STS); 381e8a1faeSThomas Huth uint32_t sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); 391e8a1faeSThomas Huth 401e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceType), ==, 1); 411e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceVersion), ==, 1); 421e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapLocality), ==, 0); 431e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapCRBIdleBypass), ==, 0); 441e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapDataXferSizeSupport), 451e8a1faeSThomas Huth ==, 3); 461e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapFIFO), ==, 0); 471e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, CapCRB), ==, 1); 481e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, InterfaceSelector), ==, 1); 491e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(intfid, CRB_INTF_ID, RID), ==, 0); 501e8a1faeSThomas Huth 511e8a1faeSThomas Huth g_assert_cmpint(csize, >=, 128); 521e8a1faeSThomas Huth g_assert_cmpint(rsize, >=, 128); 531e8a1faeSThomas Huth g_assert_cmpint(caddr, >, TPM_CRB_ADDR_BASE); 541e8a1faeSThomas Huth g_assert_cmpint(raddr, >, TPM_CRB_ADDR_BASE); 551e8a1faeSThomas Huth 561e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmEstablished), ==, 1); 571e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, locAssigned), ==, 0); 581e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, activeLocality), ==, 0); 591e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, reserved), ==, 0); 601e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmRegValidSts), ==, 1); 611e8a1faeSThomas Huth 621e8a1faeSThomas Huth g_assert_cmpint(locctrl, ==, 0); 631e8a1faeSThomas Huth 641e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(locsts, CRB_LOC_STS, Granted), ==, 0); 651e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(locsts, CRB_LOC_STS, beenSeized), ==, 0); 661e8a1faeSThomas Huth 671e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmIdle), ==, 1); 681e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmSts), ==, 0); 691e8a1faeSThomas Huth 701e8a1faeSThomas Huth /* request access to locality 0 */ 711e8a1faeSThomas Huth writeb(TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 1); 721e8a1faeSThomas Huth 731e8a1faeSThomas Huth /* granted bit must be set now */ 741e8a1faeSThomas Huth locsts = readl(TPM_CRB_ADDR_BASE + A_CRB_LOC_STS); 751e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(locsts, CRB_LOC_STS, Granted), ==, 1); 761e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(locsts, CRB_LOC_STS, beenSeized), ==, 0); 771e8a1faeSThomas Huth 781e8a1faeSThomas Huth /* we must have an assigned locality */ 791e8a1faeSThomas Huth locstate = readb(TPM_CRB_ADDR_BASE + A_CRB_LOC_STATE); 801e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmEstablished), ==, 1); 811e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, locAssigned), ==, 1); 821e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, activeLocality), ==, 0); 831e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, reserved), ==, 0); 841e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmRegValidSts), ==, 1); 851e8a1faeSThomas Huth 861e8a1faeSThomas Huth /* set into ready state */ 871e8a1faeSThomas Huth writel(TPM_CRB_ADDR_BASE + A_CRB_CTRL_REQ, 1); 881e8a1faeSThomas Huth 891e8a1faeSThomas Huth /* TPM must not be in the idle state */ 901e8a1faeSThomas Huth sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); 911e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmIdle), ==, 0); 921e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmSts), ==, 0); 931e8a1faeSThomas Huth 941e8a1faeSThomas Huth memwrite(caddr, TPM_CMD, sizeof(TPM_CMD)); 951e8a1faeSThomas Huth 961e8a1faeSThomas Huth uint32_t start = 1; 971e8a1faeSThomas Huth uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; 981e8a1faeSThomas Huth writel(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start); 991e8a1faeSThomas Huth do { 1001e8a1faeSThomas Huth start = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); 1011e8a1faeSThomas Huth if ((start & 1) == 0) { 1021e8a1faeSThomas Huth break; 1031e8a1faeSThomas Huth } 1041e8a1faeSThomas Huth } while (g_get_monotonic_time() < end_time); 1051e8a1faeSThomas Huth start = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); 1061e8a1faeSThomas Huth g_assert_cmpint(start & 1, ==, 0); 1071e8a1faeSThomas Huth 1081e8a1faeSThomas Huth /* TPM must still not be in the idle state */ 1091e8a1faeSThomas Huth sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); 1101e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmIdle), ==, 0); 1111e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmSts), ==, 0); 1121e8a1faeSThomas Huth 1131e8a1faeSThomas Huth struct tpm_hdr tpm_msg; 1141e8a1faeSThomas Huth memread(raddr, &tpm_msg, sizeof(tpm_msg)); 1151e8a1faeSThomas Huth g_assert_cmpmem(&tpm_msg, sizeof(tpm_msg), s->tpm_msg, sizeof(*s->tpm_msg)); 1161e8a1faeSThomas Huth 1171e8a1faeSThomas Huth /* set TPM into idle state */ 1181e8a1faeSThomas Huth writel(TPM_CRB_ADDR_BASE + A_CRB_CTRL_REQ, 2); 1191e8a1faeSThomas Huth 1201e8a1faeSThomas Huth /* idle state must be indicated now */ 1211e8a1faeSThomas Huth sts = readl(TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); 1221e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmIdle), ==, 1); 1231e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(sts, CRB_CTRL_STS, tpmSts), ==, 0); 1241e8a1faeSThomas Huth 1251e8a1faeSThomas Huth /* relinquish locality */ 1261e8a1faeSThomas Huth writel(TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 2); 1271e8a1faeSThomas Huth 1281e8a1faeSThomas Huth /* Granted flag must be cleared */ 1291e8a1faeSThomas Huth sts = readl(TPM_CRB_ADDR_BASE + A_CRB_LOC_STS); 1301e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(sts, CRB_LOC_STS, Granted), ==, 0); 1311e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(sts, CRB_LOC_STS, beenSeized), ==, 0); 1321e8a1faeSThomas Huth 1331e8a1faeSThomas Huth /* no locality may be assigned */ 1341e8a1faeSThomas Huth locstate = readb(TPM_CRB_ADDR_BASE + A_CRB_LOC_STATE); 1351e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmEstablished), ==, 1); 1361e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, locAssigned), ==, 0); 1371e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, activeLocality), ==, 0); 1381e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, reserved), ==, 0); 1391e8a1faeSThomas Huth g_assert_cmpint(FIELD_EX32(locstate, CRB_LOC_STATE, tpmRegValidSts), ==, 1); 1401e8a1faeSThomas Huth 1411e8a1faeSThomas Huth } 1421e8a1faeSThomas Huth 1431e8a1faeSThomas Huth int main(int argc, char **argv) 1441e8a1faeSThomas Huth { 1451e8a1faeSThomas Huth int ret; 1461e8a1faeSThomas Huth char *args, *tmp_path = g_dir_make_tmp("qemu-tpm-crb-test.XXXXXX", NULL); 1471e8a1faeSThomas Huth GThread *thread; 148*9bd0e32aSStefan Berger TPMTestState test; 1491e8a1faeSThomas Huth 1501e8a1faeSThomas Huth module_call_init(MODULE_INIT_QOM); 1511e8a1faeSThomas Huth g_test_init(&argc, &argv, NULL); 1521e8a1faeSThomas Huth 1531e8a1faeSThomas Huth test.addr = g_new0(SocketAddress, 1); 1541e8a1faeSThomas Huth test.addr->type = SOCKET_ADDRESS_TYPE_UNIX; 1551e8a1faeSThomas Huth test.addr->u.q_unix.path = g_build_filename(tmp_path, "sock", NULL); 1561e8a1faeSThomas Huth g_mutex_init(&test.data_mutex); 1571e8a1faeSThomas Huth g_cond_init(&test.data_cond); 1581e8a1faeSThomas Huth test.data_cond_signal = false; 1591e8a1faeSThomas Huth 1601e8a1faeSThomas Huth thread = g_thread_new(NULL, tpm_emu_ctrl_thread, &test); 1611e8a1faeSThomas Huth tpm_emu_test_wait_cond(&test); 1621e8a1faeSThomas Huth 1631e8a1faeSThomas Huth args = g_strdup_printf( 1641e8a1faeSThomas Huth "-chardev socket,id=chr,path=%s " 1651e8a1faeSThomas Huth "-tpmdev emulator,id=dev,chardev=chr " 1661e8a1faeSThomas Huth "-device tpm-crb,tpmdev=dev", 1671e8a1faeSThomas Huth test.addr->u.q_unix.path); 1681e8a1faeSThomas Huth qtest_start(args); 1691e8a1faeSThomas Huth 1701e8a1faeSThomas Huth qtest_add_data_func("/tpm-crb/test", &test, tpm_crb_test); 1711e8a1faeSThomas Huth ret = g_test_run(); 1721e8a1faeSThomas Huth 1731e8a1faeSThomas Huth qtest_end(); 1741e8a1faeSThomas Huth 1751e8a1faeSThomas Huth g_thread_join(thread); 1761e8a1faeSThomas Huth g_unlink(test.addr->u.q_unix.path); 1771e8a1faeSThomas Huth qapi_free_SocketAddress(test.addr); 1781e8a1faeSThomas Huth g_rmdir(tmp_path); 1791e8a1faeSThomas Huth g_free(tmp_path); 1801e8a1faeSThomas Huth g_free(args); 1811e8a1faeSThomas Huth return ret; 1821e8a1faeSThomas Huth } 183