master
c 360 lines 10.4 KB
Raw
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 #include "qemu/bswap.h"
18
19 #include "hw/acpi/tpm.h"
20 #include "libqtest.h"
21 #include "tpm-util.h"
22 #include "qobject/qdict.h"
23
24 #define CRB_ADDR_START (TPM_CRB_ADDR_BASE + A_CRB_CTRL_START)
25 #define CRB_ADDR_CTRL_STS (TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS)
26 #define CRB_ADDR_CTRL_CMD_SIZE \
27 (TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_SIZE)
28
29 #define CRB_START_INVOKE (1 << 0)
30 #define CRB_START_RSP_RETRY (1 << 1)
31 #define CRB_START_NEXT_CHUNK (1 << 2)
32
33 void tpm_wait_till_bit_clear(QTestState *s, uint64_t addr, uint32_t mask)
34 {
35 uint32_t sts;
36 uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
37
38 while (true) {
39 sts = qtest_readl(s, addr);
40 if ((sts & mask) == 0) {
41 break;
42 }
43 if (g_get_monotonic_time() >= end_time) {
44 g_assert_cmphex(sts & mask, ==, 0);
45 break;
46 }
47 }
48 }
49
50 void tpm_util_crb_transfer(QTestState *s,
51 const unsigned char *req, size_t req_size,
52 unsigned char *rsp, size_t rsp_size)
53 {
54 uint32_t tpm_sts;
55 uint64_t caddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR);
56 uint64_t raddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR);
57
58 qtest_writeb(s, TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 1);
59
60 qtest_memwrite(s, caddr, req, req_size);
61
62 qtest_writel(s, CRB_ADDR_START, CRB_START_INVOKE);
63 tpm_wait_till_bit_clear(s, CRB_ADDR_START, CRB_START_INVOKE);
64
65 tpm_sts = qtest_readl(s, CRB_ADDR_CTRL_STS);
66 g_assert_cmpint(tpm_sts & 1, ==, 0);
67
68 qtest_memread(s, raddr, rsp, rsp_size);
69 }
70
71 void tpm_util_crb_chunk_transfer(QTestState *s,
72 const unsigned char *req, size_t req_size,
73 unsigned char *rsp, size_t rsp_size)
74 {
75 uint32_t tpm_sts;
76 size_t chunk_size;
77 unsigned char header[10];
78 uint32_t actual_response_size = 0;
79
80 uint64_t caddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR);
81 uint64_t raddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR);
82 uint32_t crb_ctrl_cmd_size = qtest_readl(s, CRB_ADDR_CTRL_CMD_SIZE);
83
84 chunk_size = crb_ctrl_cmd_size;
85
86 qtest_writeb(s, TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 1);
87
88 for (size_t i = 0; i < req_size; i += chunk_size) {
89 bool last_chunk = false;
90 size_t current_chunk_size = chunk_size;
91
92 if (i + chunk_size > req_size) {
93 last_chunk = true;
94 current_chunk_size = req_size - i;
95 }
96
97 qtest_memwrite(s, caddr, req + i, current_chunk_size);
98
99 if (last_chunk) {
100 qtest_writel(s, CRB_ADDR_START, CRB_START_INVOKE);
101 tpm_wait_till_bit_clear(s, CRB_ADDR_START, CRB_START_INVOKE);
102 } else {
103 qtest_writel(s, CRB_ADDR_START, CRB_START_NEXT_CHUNK);
104 tpm_wait_till_bit_clear(s, CRB_ADDR_START, CRB_START_NEXT_CHUNK);
105 }
106 }
107 tpm_sts = qtest_readl(s, CRB_ADDR_CTRL_STS);
108 g_assert_cmpint(tpm_sts & 1, ==, 0);
109
110 /*
111 * Read response in chunks
112 */
113
114 qtest_memread(s, raddr, header, sizeof(header));
115 actual_response_size = ldl_be_p(&header[2]);
116
117 if (actual_response_size > rsp_size) {
118 actual_response_size = rsp_size;
119 }
120
121 for (size_t i = 0; i < actual_response_size; i += chunk_size) {
122 size_t to_read = i + chunk_size > actual_response_size
123 ? actual_response_size - i
124 : chunk_size;
125 if (i > 0) {
126 qtest_writel(s, CRB_ADDR_START, CRB_START_NEXT_CHUNK);
127 tpm_wait_till_bit_clear(s, CRB_ADDR_START, CRB_START_NEXT_CHUNK);
128 }
129 qtest_memread(s, raddr, rsp + i, to_read);
130 }
131 }
132
133 void tpm_util_startup(QTestState *s, tx_func *tx)
134 {
135 unsigned char buffer[1024];
136 static const unsigned char tpm_startup[] = {
137 0x80, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00,
138 0x01, 0x44, 0x00, 0x00
139 };
140 static const unsigned char tpm_startup_resp[] = {
141 0x80, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00,
142 0x00, 0x00
143 };
144
145 tx(s, tpm_startup, sizeof(tpm_startup), buffer, sizeof(buffer));
146
147 g_assert_cmpmem(buffer, sizeof(tpm_startup_resp),
148 tpm_startup_resp, sizeof(tpm_startup_resp));
149 }
150
151 void tpm_util_pcrextend(QTestState *s, tx_func *tx)
152 {
153 unsigned char buffer[1024];
154 static const unsigned char tpm_pcrextend[] = {
155 0x80, 0x02, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00,
156 0x01, 0x82, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00,
157 0x00, 0x09, 0x40, 0x00, 0x00, 0x09, 0x00, 0x00,
158 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
159 0x0b, 0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00,
160 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
161 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
162 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
163 0x00
164 };
165
166 static const unsigned char tpm_pcrextend_resp[] = {
167 0x80, 0x02, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00,
168 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
169 0x01, 0x00, 0x00
170 };
171
172 tx(s, tpm_pcrextend, sizeof(tpm_pcrextend), buffer, sizeof(buffer));
173
174 g_assert_cmpmem(buffer, sizeof(tpm_pcrextend_resp),
175 tpm_pcrextend_resp, sizeof(tpm_pcrextend_resp));
176 }
177
178 void tpm_util_pcrread(QTestState *s, tx_func *tx,
179 const unsigned char *exp_resp, size_t exp_resp_size)
180 {
181 unsigned char buffer[1024];
182 static const unsigned char tpm_pcrread[] = {
183 0x80, 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00,
184 0x01, 0x7e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0b,
185 0x03, 0x00, 0x04, 0x00
186 };
187
188 tx(s, tpm_pcrread, sizeof(tpm_pcrread), buffer, sizeof(buffer));
189
190 /* skip pcrUpdateCounter (14th byte) in comparison */
191 g_assert(exp_resp_size >= 15);
192 g_assert_cmpmem(buffer, 13, exp_resp, 13);
193 g_assert_cmpmem(&buffer[14], exp_resp_size - 14,
194 &exp_resp[14], exp_resp_size - 14);
195 }
196
197 bool tpm_util_swtpm_has_tpm2(void)
198 {
199 bool has_tpm2 = false;
200 char *out = NULL;
201 static const char *argv[] = {
202 "swtpm", "socket", "--help", NULL
203 };
204
205 if (!g_spawn_sync(NULL /* working_dir */,
206 (char **)argv,
207 NULL /* envp */,
208 G_SPAWN_SEARCH_PATH,
209 NULL /* child_setup */,
210 NULL /* user_data */,
211 &out,
212 NULL /* err */,
213 NULL /* exit_status */,
214 NULL)) {
215 return false;
216 }
217
218 if (strstr(out, "--tpm2")) {
219 has_tpm2 = true;
220 }
221
222 g_free(out);
223 return has_tpm2;
224 }
225
226 gboolean tpm_util_swtpm_start(const char *path, GPid *pid,
227 SocketAddress **addr, GError **error)
228 {
229 char *swtpm_argv_tpmstate = g_strdup_printf("dir=%s", path);
230 char *swtpm_argv_ctrl = g_strdup_printf("type=unixio,path=%s/sock",
231 path);
232 gchar *swtpm_argv[] = {
233 g_strdup("swtpm"), g_strdup("socket"),
234 g_strdup("--tpmstate"), swtpm_argv_tpmstate,
235 g_strdup("--ctrl"), swtpm_argv_ctrl,
236 g_strdup("--tpm2"),
237 NULL
238 };
239 gboolean succ;
240 unsigned i;
241
242 *addr = g_new0(SocketAddress, 1);
243 (*addr)->type = SOCKET_ADDRESS_TYPE_UNIX;
244 (*addr)->u.q_unix.path = g_build_filename(path, "sock", NULL);
245
246 succ = g_spawn_async(NULL, swtpm_argv, NULL, G_SPAWN_SEARCH_PATH,
247 NULL, NULL, pid, error);
248
249 for (i = 0; swtpm_argv[i]; i++) {
250 g_free(swtpm_argv[i]);
251 }
252
253 return succ;
254 }
255
256 void tpm_util_swtpm_kill(GPid pid)
257 {
258 int n;
259
260 if (!pid) {
261 return;
262 }
263
264 g_spawn_close_pid(pid);
265
266 n = kill(pid, 0);
267 if (n < 0) {
268 return;
269 }
270
271 kill(pid, SIGKILL);
272 }
273
274 void tpm_util_migrate(QTestState *who, const char *uri)
275 {
276 QDict *rsp;
277
278 rsp = qtest_qmp(who,
279 "{ 'execute': 'migrate', 'arguments': { 'uri': %s } }",
280 uri);
281 g_assert(qdict_haskey(rsp, "return"));
282 qobject_unref(rsp);
283 }
284
285 void tpm_util_wait_for_migration_complete(QTestState *who)
286 {
287 while (true) {
288 QDict *rsp;
289 QDict *rsp_return;
290 bool completed;
291 const char *status;
292
293 rsp = qtest_qmp(who, "{ 'execute': 'query-migrate' }");
294 g_assert(qdict_haskey(rsp, "return"));
295 rsp_return = qdict_get_qdict(rsp, "return");
296
297 g_assert(!qdict_haskey(rsp_return, "error"));
298 status = qdict_get_str(rsp_return, "status");
299 completed = strcmp(status, "completed") == 0;
300 g_assert_cmpstr(status, !=, "failed");
301 qobject_unref(rsp);
302 if (completed) {
303 return;
304 }
305 usleep(1000);
306 }
307 }
308
309 void tpm_util_migration_start_qemu(QTestState **src_qemu,
310 QTestState **dst_qemu,
311 SocketAddress *src_tpm_addr,
312 SocketAddress *dst_tpm_addr,
313 const char *miguri,
314 const char *ifmodel,
315 const char *machine_options)
316 {
317 char *src_qemu_args, *dst_qemu_args;
318
319 src_qemu_args = g_strdup_printf(
320 "%s "
321 "-chardev socket,id=chr,path=%s "
322 "-tpmdev emulator,id=tpm0,chardev=chr "
323 "-device %s,tpmdev=tpm0 ",
324 machine_options ? : "", src_tpm_addr->u.q_unix.path, ifmodel);
325
326 *src_qemu = qtest_init(src_qemu_args);
327
328 dst_qemu_args = g_strdup_printf(
329 "%s "
330 "-chardev socket,id=chr,path=%s "
331 "-tpmdev emulator,id=tpm0,chardev=chr "
332 "-device %s,tpmdev=tpm0 "
333 "-incoming %s",
334 machine_options ? : "",
335 dst_tpm_addr->u.q_unix.path,
336 ifmodel, miguri);
337
338 *dst_qemu = qtest_init(dst_qemu_args);
339
340 g_free(src_qemu_args);
341 g_free(dst_qemu_args);
342 }
343
344 /* Remove directory with remainders of swtpm */
345 void tpm_util_rmdir(const char *path)
346 {
347 char *filename;
348 int ret;
349
350 filename = g_strdup_printf("%s/tpm2-00.permall", path);
351 g_unlink(filename);
352 g_free(filename);
353
354 filename = g_strdup_printf("%s/.lock", path);
355 g_unlink(filename);
356 g_free(filename);
357
358 ret = g_rmdir(path);
359 g_assert(!ret);
360 }