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 /* skip pcrUpdateCounter (14th byte) in comparison */ 143 g_assert(exp_resp_size >= 15); 144 g_assert_cmpmem(buffer, 13, exp_resp, 13); 145 g_assert_cmpmem(&buffer[14], exp_resp_size - 14, 146 &exp_resp[14], exp_resp_size - 14); 147 } 148 149 bool tpm_util_swtpm_has_tpm2(void) 150 { 151 bool has_tpm2 = false; 152 char *out = NULL; 153 static const char *argv[] = { 154 "swtpm", "socket", "--help", NULL 155 }; 156 157 if (!g_spawn_sync(NULL /* working_dir */, 158 (char **)argv, 159 NULL /* envp */, 160 G_SPAWN_SEARCH_PATH, 161 NULL /* child_setup */, 162 NULL /* user_data */, 163 &out, 164 NULL /* err */, 165 NULL /* exit_status */, 166 NULL)) { 167 return false; 168 } 169 170 if (strstr(out, "--tpm2")) { 171 has_tpm2 = true; 172 } 173 174 g_free(out); 175 return has_tpm2; 176 } 177 178 gboolean tpm_util_swtpm_start(const char *path, GPid *pid, 179 SocketAddress **addr, GError **error) 180 { 181 char *swtpm_argv_tpmstate = g_strdup_printf("dir=%s", path); 182 char *swtpm_argv_ctrl = g_strdup_printf("type=unixio,path=%s/sock", 183 path); 184 gchar *swtpm_argv[] = { 185 g_strdup("swtpm"), g_strdup("socket"), 186 g_strdup("--tpmstate"), swtpm_argv_tpmstate, 187 g_strdup("--ctrl"), swtpm_argv_ctrl, 188 g_strdup("--tpm2"), 189 NULL 190 }; 191 gboolean succ; 192 unsigned i; 193 194 *addr = g_new0(SocketAddress, 1); 195 (*addr)->type = SOCKET_ADDRESS_TYPE_UNIX; 196 (*addr)->u.q_unix.path = g_build_filename(path, "sock", NULL); 197 198 succ = g_spawn_async(NULL, swtpm_argv, NULL, G_SPAWN_SEARCH_PATH, 199 NULL, NULL, pid, error); 200 201 for (i = 0; swtpm_argv[i]; i++) { 202 g_free(swtpm_argv[i]); 203 } 204 205 return succ; 206 } 207 208 void tpm_util_swtpm_kill(GPid pid) 209 { 210 int n; 211 212 if (!pid) { 213 return; 214 } 215 216 g_spawn_close_pid(pid); 217 218 n = kill(pid, 0); 219 if (n < 0) { 220 return; 221 } 222 223 kill(pid, SIGKILL); 224 } 225 226 void tpm_util_migrate(QTestState *who, const char *uri) 227 { 228 QDict *rsp; 229 230 rsp = qtest_qmp(who, 231 "{ 'execute': 'migrate', 'arguments': { 'uri': %s } }", 232 uri); 233 g_assert(qdict_haskey(rsp, "return")); 234 qobject_unref(rsp); 235 } 236 237 void tpm_util_wait_for_migration_complete(QTestState *who) 238 { 239 while (true) { 240 QDict *rsp_return; 241 bool completed; 242 const char *status; 243 244 qtest_qmp_send(who, "{ 'execute': 'query-migrate' }"); 245 rsp_return = qtest_qmp_receive_success(who, NULL, NULL); 246 status = qdict_get_str(rsp_return, "status"); 247 completed = strcmp(status, "completed") == 0; 248 g_assert_cmpstr(status, !=, "failed"); 249 qobject_unref(rsp_return); 250 if (completed) { 251 return; 252 } 253 usleep(1000); 254 } 255 } 256 257 void tpm_util_migration_start_qemu(QTestState **src_qemu, 258 QTestState **dst_qemu, 259 SocketAddress *src_tpm_addr, 260 SocketAddress *dst_tpm_addr, 261 const char *miguri, 262 const char *ifmodel, 263 const char *machine_options) 264 { 265 char *src_qemu_args, *dst_qemu_args; 266 267 src_qemu_args = g_strdup_printf( 268 "%s " 269 "-chardev socket,id=chr,path=%s " 270 "-tpmdev emulator,id=dev,chardev=chr " 271 "-device %s,tpmdev=dev ", 272 machine_options ? : "", src_tpm_addr->u.q_unix.path, ifmodel); 273 274 *src_qemu = qtest_init(src_qemu_args); 275 276 dst_qemu_args = g_strdup_printf( 277 "%s " 278 "-chardev socket,id=chr,path=%s " 279 "-tpmdev emulator,id=dev,chardev=chr " 280 "-device %s,tpmdev=dev " 281 "-incoming %s", 282 machine_options ? : "", 283 dst_tpm_addr->u.q_unix.path, 284 ifmodel, miguri); 285 286 *dst_qemu = qtest_init(dst_qemu_args); 287 288 free(src_qemu_args); 289 free(dst_qemu_args); 290 } 291