1 /* 2 * QTest TPM utilities 3 * 4 * Copyright (c) 2018 IBM Corporation 5 * Copyright (c) 2018 Red Hat, Inc. 6 * 7 * Authors: 8 * Stefan Berger <stefanb@linux.vnet.ibm.com> 9 * Marc-André Lureau <marcandre.lureau@redhat.com> 10 * 11 * This work is licensed under the terms of the GNU GPL, version 2 or later. 12 * See the COPYING file in the top-level directory. 13 */ 14 15 #include "qemu/osdep.h" 16 17 #include "hw/acpi/tpm.h" 18 #include "libqtest.h" 19 #include "tpm-util.h" 20 #include "qapi/qmp/qdict.h" 21 22 void tpm_util_crb_transfer(QTestState *s, 23 const unsigned char *req, size_t req_size, 24 unsigned char *rsp, size_t rsp_size) 25 { 26 uint64_t caddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR); 27 uint64_t raddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR); 28 29 qtest_writeb(s, TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 1); 30 31 qtest_memwrite(s, caddr, req, req_size); 32 33 uint32_t sts, start = 1; 34 uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; 35 qtest_writel(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start); 36 while (true) { 37 start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); 38 if ((start & 1) == 0) { 39 break; 40 } 41 if (g_get_monotonic_time() >= end_time) { 42 break; 43 } 44 }; 45 start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); 46 g_assert_cmpint(start & 1, ==, 0); 47 sts = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); 48 g_assert_cmpint(sts & 1, ==, 0); 49 50 qtest_memread(s, raddr, rsp, rsp_size); 51 } 52 53 void tpm_util_tis_transfer(QTestState *s, 54 const unsigned char *req, size_t req_size, 55 unsigned char *rsp, size_t rsp_size) 56 { 57 uint32_t sts; 58 uint16_t bcount; 59 size_t i; 60 61 /* request use of locality 0 */ 62 qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE); 63 qtest_writel(s, TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_COMMAND_READY); 64 65 sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS)); 66 bcount = (sts >> 8) & 0xffff; 67 g_assert_cmpint(bcount, >=, req_size); 68 69 /* transmit command */ 70 for (i = 0; i < req_size; i++) { 71 qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_DATA_FIFO), req[i]); 72 } 73 74 /* start processing */ 75 qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_TPM_GO); 76 77 uint64_t end_time = g_get_monotonic_time() + 50 * G_TIME_SPAN_SECOND; 78 do { 79 sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS)); 80 if ((sts & TPM_TIS_STS_DATA_AVAILABLE) != 0) { 81 break; 82 } 83 } while (g_get_monotonic_time() < end_time); 84 85 sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS)); 86 bcount = (sts >> 8) & 0xffff; 87 88 memset(rsp, 0, rsp_size); 89 for (i = 0; i < bcount; i++) { 90 rsp[i] = qtest_readb(s, TIS_REG(0, TPM_TIS_REG_DATA_FIFO)); 91 } 92 93 /* relinquish use of locality 0 */ 94 qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_ACCESS), 95 TPM_TIS_ACCESS_ACTIVE_LOCALITY); 96 } 97 98 void tpm_util_startup(QTestState *s, tx_func *tx) 99 { 100 unsigned char buffer[1024]; 101 unsigned char tpm_startup[] = 102 "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00"; 103 unsigned char tpm_startup_resp[] = 104 "\x80\x01\x00\x00\x00\x0a\x00\x00\x00\x00"; 105 106 tx(s, tpm_startup, sizeof(tpm_startup), buffer, sizeof(buffer)); 107 108 g_assert_cmpmem(buffer, sizeof(tpm_startup_resp), 109 tpm_startup_resp, sizeof(tpm_startup_resp)); 110 } 111 112 void tpm_util_pcrextend(QTestState *s, tx_func *tx) 113 { 114 unsigned char buffer[1024]; 115 unsigned char tpm_pcrextend[] = 116 "\x80\x02\x00\x00\x00\x41\x00\x00\x01\x82\x00\x00\x00\x0a\x00\x00" 117 "\x00\x09\x40\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" 118 "\x0b\x74\x65\x73\x74\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 119 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 120 "\x00"; 121 122 unsigned char tpm_pcrextend_resp[] = 123 "\x80\x02\x00\x00\x00\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 124 "\x01\x00\x00"; 125 126 tx(s, tpm_pcrextend, sizeof(tpm_pcrextend), buffer, sizeof(buffer)); 127 128 g_assert_cmpmem(buffer, sizeof(tpm_pcrextend_resp), 129 tpm_pcrextend_resp, sizeof(tpm_pcrextend_resp)); 130 } 131 132 void tpm_util_pcrread(QTestState *s, tx_func *tx, 133 const unsigned char *exp_resp, size_t exp_resp_size) 134 { 135 unsigned char buffer[1024]; 136 unsigned char tpm_pcrread[] = 137 "\x80\x01\x00\x00\x00\x14\x00\x00\x01\x7e\x00\x00\x00\x01\x00\x0b" 138 "\x03\x00\x04\x00"; 139 140 tx(s, tpm_pcrread, sizeof(tpm_pcrread), buffer, sizeof(buffer)); 141 142 g_assert_cmpmem(buffer, exp_resp_size, exp_resp, exp_resp_size); 143 } 144 145 bool tpm_util_swtpm_has_tpm2(void) 146 { 147 bool has_tpm2 = false; 148 char *out = NULL; 149 static const char *argv[] = { 150 "swtpm", "socket", "--help", NULL 151 }; 152 153 if (!g_spawn_sync(NULL /* working_dir */, 154 (char **)argv, 155 NULL /* envp */, 156 G_SPAWN_SEARCH_PATH, 157 NULL /* child_setup */, 158 NULL /* user_data */, 159 &out, 160 NULL /* err */, 161 NULL /* exit_status */, 162 NULL)) { 163 return false; 164 } 165 166 if (strstr(out, "--tpm2")) { 167 has_tpm2 = true; 168 } 169 170 g_free(out); 171 return has_tpm2; 172 } 173 174 gboolean tpm_util_swtpm_start(const char *path, GPid *pid, 175 SocketAddress **addr, GError **error) 176 { 177 char *swtpm_argv_tpmstate = g_strdup_printf("dir=%s", path); 178 char *swtpm_argv_ctrl = g_strdup_printf("type=unixio,path=%s/sock", 179 path); 180 gchar *swtpm_argv[] = { 181 g_strdup("swtpm"), g_strdup("socket"), 182 g_strdup("--tpmstate"), swtpm_argv_tpmstate, 183 g_strdup("--ctrl"), swtpm_argv_ctrl, 184 g_strdup("--tpm2"), 185 NULL 186 }; 187 gboolean succ; 188 unsigned i; 189 190 *addr = g_new0(SocketAddress, 1); 191 (*addr)->type = SOCKET_ADDRESS_TYPE_UNIX; 192 (*addr)->u.q_unix.path = g_build_filename(path, "sock", NULL); 193 194 succ = g_spawn_async(NULL, swtpm_argv, NULL, G_SPAWN_SEARCH_PATH, 195 NULL, NULL, pid, error); 196 197 for (i = 0; swtpm_argv[i]; i++) { 198 g_free(swtpm_argv[i]); 199 } 200 201 return succ; 202 } 203 204 void tpm_util_swtpm_kill(GPid pid) 205 { 206 int n; 207 208 if (!pid) { 209 return; 210 } 211 212 g_spawn_close_pid(pid); 213 214 n = kill(pid, 0); 215 if (n < 0) { 216 return; 217 } 218 219 kill(pid, SIGKILL); 220 } 221 222 void tpm_util_migrate(QTestState *who, const char *uri) 223 { 224 QDict *rsp; 225 226 rsp = qtest_qmp(who, 227 "{ 'execute': 'migrate', 'arguments': { 'uri': %s } }", 228 uri); 229 g_assert(qdict_haskey(rsp, "return")); 230 qobject_unref(rsp); 231 } 232 233 void tpm_util_wait_for_migration_complete(QTestState *who) 234 { 235 while (true) { 236 QDict *rsp_return; 237 bool completed; 238 const char *status; 239 240 qtest_qmp_send(who, "{ 'execute': 'query-migrate' }"); 241 rsp_return = qtest_qmp_receive_success(who, NULL, NULL); 242 status = qdict_get_str(rsp_return, "status"); 243 completed = strcmp(status, "completed") == 0; 244 g_assert_cmpstr(status, !=, "failed"); 245 qobject_unref(rsp_return); 246 if (completed) { 247 return; 248 } 249 usleep(1000); 250 } 251 } 252 253 void tpm_util_migration_start_qemu(QTestState **src_qemu, 254 QTestState **dst_qemu, 255 SocketAddress *src_tpm_addr, 256 SocketAddress *dst_tpm_addr, 257 const char *miguri, 258 const char *ifmodel, 259 const char *machine_options) 260 { 261 char *src_qemu_args, *dst_qemu_args; 262 263 src_qemu_args = g_strdup_printf( 264 "%s " 265 "-chardev socket,id=chr,path=%s " 266 "-tpmdev emulator,id=dev,chardev=chr " 267 "-device %s,tpmdev=dev ", 268 machine_options ? : "", src_tpm_addr->u.q_unix.path, ifmodel); 269 270 *src_qemu = qtest_init(src_qemu_args); 271 272 dst_qemu_args = g_strdup_printf( 273 "%s " 274 "-chardev socket,id=chr,path=%s " 275 "-tpmdev emulator,id=dev,chardev=chr " 276 "-device %s,tpmdev=dev " 277 "-incoming %s", 278 machine_options ? : "", 279 dst_tpm_addr->u.q_unix.path, 280 ifmodel, miguri); 281 282 *dst_qemu = qtest_init(dst_qemu_args); 283 284 free(src_qemu_args); 285 free(dst_qemu_args); 286 } 287