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 #include <glib/gstdio.h> 17 18 #include "hw/acpi/tpm.h" 19 #include "libqtest.h" 20 #include "tpm-util.h" 21 #include "qapi/qmp/qdict.h" 22 23 void tpm_util_crb_transfer(QTestState *s, 24 const unsigned char *req, size_t req_size, 25 unsigned char *rsp, size_t rsp_size) 26 { 27 uint64_t caddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR); 28 uint64_t raddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR); 29 30 qtest_writeb(s, TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 1); 31 32 qtest_memwrite(s, caddr, req, req_size); 33 34 uint32_t sts, start = 1; 35 uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; 36 qtest_writel(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start); 37 while (true) { 38 start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); 39 if ((start & 1) == 0) { 40 break; 41 } 42 if (g_get_monotonic_time() >= end_time) { 43 break; 44 } 45 }; 46 start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START); 47 g_assert_cmpint(start & 1, ==, 0); 48 sts = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS); 49 g_assert_cmpint(sts & 1, ==, 0); 50 51 qtest_memread(s, raddr, rsp, rsp_size); 52 } 53 54 void tpm_util_startup(QTestState *s, tx_func *tx) 55 { 56 unsigned char buffer[1024]; 57 static const unsigned char tpm_startup[] = 58 "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00"; 59 static const unsigned char tpm_startup_resp[] = 60 "\x80\x01\x00\x00\x00\x0a\x00\x00\x00\x00"; 61 62 tx(s, tpm_startup, sizeof(tpm_startup), buffer, sizeof(buffer)); 63 64 g_assert_cmpmem(buffer, sizeof(tpm_startup_resp), 65 tpm_startup_resp, sizeof(tpm_startup_resp)); 66 } 67 68 void tpm_util_pcrextend(QTestState *s, tx_func *tx) 69 { 70 unsigned char buffer[1024]; 71 static const unsigned char tpm_pcrextend[] = 72 "\x80\x02\x00\x00\x00\x41\x00\x00\x01\x82\x00\x00\x00\x0a\x00\x00" 73 "\x00\x09\x40\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" 74 "\x0b\x74\x65\x73\x74\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 75 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 76 "\x00"; 77 78 static const unsigned char tpm_pcrextend_resp[] = 79 "\x80\x02\x00\x00\x00\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 80 "\x01\x00\x00"; 81 82 tx(s, tpm_pcrextend, sizeof(tpm_pcrextend), buffer, sizeof(buffer)); 83 84 g_assert_cmpmem(buffer, sizeof(tpm_pcrextend_resp), 85 tpm_pcrextend_resp, sizeof(tpm_pcrextend_resp)); 86 } 87 88 void tpm_util_pcrread(QTestState *s, tx_func *tx, 89 const unsigned char *exp_resp, size_t exp_resp_size) 90 { 91 unsigned char buffer[1024]; 92 static const unsigned char tpm_pcrread[] = 93 "\x80\x01\x00\x00\x00\x14\x00\x00\x01\x7e\x00\x00\x00\x01\x00\x0b" 94 "\x03\x00\x04\x00"; 95 96 tx(s, tpm_pcrread, sizeof(tpm_pcrread), buffer, sizeof(buffer)); 97 98 /* skip pcrUpdateCounter (14th byte) in comparison */ 99 g_assert(exp_resp_size >= 15); 100 g_assert_cmpmem(buffer, 13, exp_resp, 13); 101 g_assert_cmpmem(&buffer[14], exp_resp_size - 14, 102 &exp_resp[14], exp_resp_size - 14); 103 } 104 105 bool tpm_util_swtpm_has_tpm2(void) 106 { 107 bool has_tpm2 = false; 108 char *out = NULL; 109 static const char *argv[] = { 110 "swtpm", "socket", "--help", NULL 111 }; 112 113 if (!g_spawn_sync(NULL /* working_dir */, 114 (char **)argv, 115 NULL /* envp */, 116 G_SPAWN_SEARCH_PATH, 117 NULL /* child_setup */, 118 NULL /* user_data */, 119 &out, 120 NULL /* err */, 121 NULL /* exit_status */, 122 NULL)) { 123 return false; 124 } 125 126 if (strstr(out, "--tpm2")) { 127 has_tpm2 = true; 128 } 129 130 g_free(out); 131 return has_tpm2; 132 } 133 134 gboolean tpm_util_swtpm_start(const char *path, GPid *pid, 135 SocketAddress **addr, GError **error) 136 { 137 char *swtpm_argv_tpmstate = g_strdup_printf("dir=%s", path); 138 char *swtpm_argv_ctrl = g_strdup_printf("type=unixio,path=%s/sock", 139 path); 140 gchar *swtpm_argv[] = { 141 g_strdup("swtpm"), g_strdup("socket"), 142 g_strdup("--tpmstate"), swtpm_argv_tpmstate, 143 g_strdup("--ctrl"), swtpm_argv_ctrl, 144 g_strdup("--tpm2"), 145 NULL 146 }; 147 gboolean succ; 148 unsigned i; 149 150 *addr = g_new0(SocketAddress, 1); 151 (*addr)->type = SOCKET_ADDRESS_TYPE_UNIX; 152 (*addr)->u.q_unix.path = g_build_filename(path, "sock", NULL); 153 154 succ = g_spawn_async(NULL, swtpm_argv, NULL, G_SPAWN_SEARCH_PATH, 155 NULL, NULL, pid, error); 156 157 for (i = 0; swtpm_argv[i]; i++) { 158 g_free(swtpm_argv[i]); 159 } 160 161 return succ; 162 } 163 164 void tpm_util_swtpm_kill(GPid pid) 165 { 166 int n; 167 168 if (!pid) { 169 return; 170 } 171 172 g_spawn_close_pid(pid); 173 174 n = kill(pid, 0); 175 if (n < 0) { 176 return; 177 } 178 179 kill(pid, SIGKILL); 180 } 181 182 void tpm_util_migrate(QTestState *who, const char *uri) 183 { 184 QDict *rsp; 185 186 rsp = qtest_qmp(who, 187 "{ 'execute': 'migrate', 'arguments': { 'uri': %s } }", 188 uri); 189 g_assert(qdict_haskey(rsp, "return")); 190 qobject_unref(rsp); 191 } 192 193 void tpm_util_wait_for_migration_complete(QTestState *who) 194 { 195 while (true) { 196 QDict *rsp; 197 QDict *rsp_return; 198 bool completed; 199 const char *status; 200 201 rsp = qtest_qmp(who, "{ 'execute': 'query-migrate' }"); 202 g_assert(qdict_haskey(rsp, "return")); 203 rsp_return = qdict_get_qdict(rsp, "return"); 204 205 g_assert(!qdict_haskey(rsp_return, "error")); 206 status = qdict_get_str(rsp_return, "status"); 207 completed = strcmp(status, "completed") == 0; 208 g_assert_cmpstr(status, !=, "failed"); 209 qobject_unref(rsp); 210 if (completed) { 211 return; 212 } 213 usleep(1000); 214 } 215 } 216 217 void tpm_util_migration_start_qemu(QTestState **src_qemu, 218 QTestState **dst_qemu, 219 SocketAddress *src_tpm_addr, 220 SocketAddress *dst_tpm_addr, 221 const char *miguri, 222 const char *ifmodel, 223 const char *machine_options) 224 { 225 char *src_qemu_args, *dst_qemu_args; 226 227 src_qemu_args = g_strdup_printf( 228 "%s " 229 "-chardev socket,id=chr,path=%s " 230 "-tpmdev emulator,id=dev,chardev=chr " 231 "-device %s,tpmdev=dev ", 232 machine_options ? : "", src_tpm_addr->u.q_unix.path, ifmodel); 233 234 *src_qemu = qtest_init(src_qemu_args); 235 236 dst_qemu_args = g_strdup_printf( 237 "%s " 238 "-chardev socket,id=chr,path=%s " 239 "-tpmdev emulator,id=dev,chardev=chr " 240 "-device %s,tpmdev=dev " 241 "-incoming %s", 242 machine_options ? : "", 243 dst_tpm_addr->u.q_unix.path, 244 ifmodel, miguri); 245 246 *dst_qemu = qtest_init(dst_qemu_args); 247 248 g_free(src_qemu_args); 249 g_free(dst_qemu_args); 250 } 251 252 /* Remove directory with remainders of swtpm */ 253 void tpm_util_rmdir(const char *path) 254 { 255 char *filename; 256 int ret; 257 258 filename = g_strdup_printf("%s/tpm2-00.permall", path); 259 g_unlink(filename); 260 g_free(filename); 261 262 filename = g_strdup_printf("%s/.lock", path); 263 g_unlink(filename); 264 g_free(filename); 265 266 ret = g_rmdir(path); 267 g_assert(!ret); 268 } 269