]>
Commit | Line | Data |
---|---|---|
b21373d0 SB |
1 | /* |
2 | * QTest testcase for TPM CRB talking to external swtpm and swtpm migration | |
3 | * | |
4 | * Copyright (c) 2018 IBM Corporation | |
5 | * with parts borrowed from migration-test.c that is: | |
6 | * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates | |
7 | * | |
8 | * Authors: | |
9 | * Stefan Berger <stefanb@linux.vnet.ibm.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 "io/channel-socket.h" | |
20 | #include "libqtest.h" | |
21 | #include "tpm-util.h" | |
22 | #include "sysemu/tpm.h" | |
23 | #include "qapi/qmp/qdict.h" | |
24 | ||
25 | typedef struct TestState { | |
26 | char *src_tpm_path; | |
27 | char *dst_tpm_path; | |
28 | char *uri; | |
29 | } TestState; | |
30 | ||
31 | bool got_stop; | |
32 | ||
33 | static void migrate(QTestState *who, const char *uri) | |
34 | { | |
35 | QDict *rsp; | |
36 | gchar *cmd; | |
37 | ||
38 | cmd = g_strdup_printf("{ 'execute': 'migrate'," | |
39 | "'arguments': { 'uri': '%s' } }", | |
40 | uri); | |
41 | rsp = qtest_qmp(who, cmd); | |
42 | g_free(cmd); | |
43 | g_assert(qdict_haskey(rsp, "return")); | |
44 | qobject_unref(rsp); | |
45 | } | |
46 | ||
47 | /* | |
48 | * Events can get in the way of responses we are actually waiting for. | |
49 | */ | |
50 | static QDict *wait_command(QTestState *who, const char *command) | |
51 | { | |
52 | const char *event_string; | |
53 | QDict *response; | |
54 | ||
55 | response = qtest_qmp(who, command); | |
56 | ||
57 | while (qdict_haskey(response, "event")) { | |
58 | /* OK, it was an event */ | |
59 | event_string = qdict_get_str(response, "event"); | |
60 | if (!strcmp(event_string, "STOP")) { | |
61 | got_stop = true; | |
62 | } | |
63 | qobject_unref(response); | |
64 | response = qtest_qmp_receive(who); | |
65 | } | |
66 | return response; | |
67 | } | |
68 | ||
69 | static void wait_for_migration_complete(QTestState *who) | |
70 | { | |
71 | while (true) { | |
72 | QDict *rsp, *rsp_return; | |
73 | bool completed; | |
74 | const char *status; | |
75 | ||
76 | rsp = wait_command(who, "{ 'execute': 'query-migrate' }"); | |
77 | rsp_return = qdict_get_qdict(rsp, "return"); | |
78 | status = qdict_get_str(rsp_return, "status"); | |
79 | completed = strcmp(status, "completed") == 0; | |
80 | g_assert_cmpstr(status, !=, "failed"); | |
81 | qobject_unref(rsp); | |
82 | if (completed) { | |
83 | return; | |
84 | } | |
85 | usleep(1000); | |
86 | } | |
87 | } | |
88 | ||
89 | static void migration_start_qemu(QTestState **src_qemu, QTestState **dst_qemu, | |
90 | SocketAddress *src_tpm_addr, | |
91 | SocketAddress *dst_tpm_addr, | |
92 | const char *miguri) | |
93 | { | |
94 | char *src_qemu_args, *dst_qemu_args; | |
95 | ||
96 | src_qemu_args = g_strdup_printf( | |
97 | "-chardev socket,id=chr,path=%s " | |
98 | "-tpmdev emulator,id=dev,chardev=chr " | |
99 | "-device tpm-crb,tpmdev=dev ", | |
100 | src_tpm_addr->u.q_unix.path); | |
101 | ||
102 | *src_qemu = qtest_init(src_qemu_args); | |
103 | ||
104 | dst_qemu_args = g_strdup_printf( | |
105 | "-chardev socket,id=chr,path=%s " | |
106 | "-tpmdev emulator,id=dev,chardev=chr " | |
107 | "-device tpm-crb,tpmdev=dev " | |
108 | "-incoming %s", | |
109 | dst_tpm_addr->u.q_unix.path, | |
110 | miguri); | |
111 | ||
112 | *dst_qemu = qtest_init(dst_qemu_args); | |
113 | ||
114 | free(src_qemu_args); | |
115 | free(dst_qemu_args); | |
116 | } | |
117 | ||
118 | static void tpm_crb_swtpm_test(const void *data) | |
119 | { | |
120 | char *args = NULL; | |
121 | QTestState *s; | |
122 | SocketAddress *addr = NULL; | |
123 | gboolean succ; | |
124 | GPid swtpm_pid; | |
125 | GError *error = NULL; | |
126 | const TestState *ts = data; | |
127 | ||
128 | succ = tpm_util_swtpm_start(ts->src_tpm_path, &swtpm_pid, &addr, &error); | |
129 | /* succ may be false if swtpm is not available */ | |
130 | if (!succ) { | |
131 | return; | |
132 | } | |
133 | ||
134 | args = g_strdup_printf( | |
135 | "-chardev socket,id=chr,path=%s " | |
136 | "-tpmdev emulator,id=dev,chardev=chr " | |
137 | "-device tpm-crb,tpmdev=dev", | |
138 | addr->u.q_unix.path); | |
139 | ||
140 | s = qtest_start(args); | |
141 | g_free(args); | |
142 | ||
143 | tpm_util_startup(s, tpm_util_crb_transfer); | |
144 | tpm_util_pcrextend(s, tpm_util_crb_transfer); | |
145 | ||
146 | unsigned char tpm_pcrread_resp[] = | |
147 | "\x80\x01\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00" | |
148 | "\x00\x01\x00\x0b\x03\x00\x04\x00\x00\x00\x00\x01\x00\x20\xf6\x85" | |
149 | "\x98\xe5\x86\x8d\xe6\x8b\x97\x29\x99\x60\xf2\x71\x7d\x17\x67\x89" | |
150 | "\xa4\x2f\x9a\xae\xa8\xc7\xb7\xaa\x79\xa8\x62\x56\xc1\xde"; | |
151 | tpm_util_pcrread(s, tpm_util_crb_transfer, tpm_pcrread_resp, | |
152 | sizeof(tpm_pcrread_resp)); | |
153 | ||
154 | qtest_end(); | |
155 | tpm_util_swtpm_kill(swtpm_pid); | |
156 | ||
157 | if (addr) { | |
158 | g_unlink(addr->u.q_unix.path); | |
159 | qapi_free_SocketAddress(addr); | |
160 | } | |
161 | } | |
162 | ||
163 | static void tpm_crb_swtpm_migration_test(const void *data) | |
164 | { | |
165 | const TestState *ts = data; | |
166 | gboolean succ; | |
167 | GPid src_tpm_pid, dst_tpm_pid; | |
168 | SocketAddress *src_tpm_addr = NULL, *dst_tpm_addr = NULL; | |
169 | GError *error = NULL; | |
170 | QTestState *src_qemu, *dst_qemu; | |
171 | ||
172 | succ = tpm_util_swtpm_start(ts->src_tpm_path, &src_tpm_pid, | |
173 | &src_tpm_addr, &error); | |
174 | /* succ may be false if swtpm is not available */ | |
175 | if (!succ) { | |
176 | return; | |
177 | } | |
178 | ||
179 | succ = tpm_util_swtpm_start(ts->dst_tpm_path, &dst_tpm_pid, | |
180 | &dst_tpm_addr, &error); | |
181 | /* succ may be false if swtpm is not available */ | |
182 | if (!succ) { | |
183 | goto err_src_tpm_kill; | |
184 | } | |
185 | ||
186 | migration_start_qemu(&src_qemu, &dst_qemu, src_tpm_addr, dst_tpm_addr, | |
187 | ts->uri); | |
188 | ||
189 | tpm_util_startup(src_qemu, tpm_util_crb_transfer); | |
190 | tpm_util_pcrextend(src_qemu, tpm_util_crb_transfer); | |
191 | ||
192 | unsigned char tpm_pcrread_resp[] = | |
193 | "\x80\x01\x00\x00\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x16\x00\x00" | |
194 | "\x00\x01\x00\x0b\x03\x00\x04\x00\x00\x00\x00\x01\x00\x20\xf6\x85" | |
195 | "\x98\xe5\x86\x8d\xe6\x8b\x97\x29\x99\x60\xf2\x71\x7d\x17\x67\x89" | |
196 | "\xa4\x2f\x9a\xae\xa8\xc7\xb7\xaa\x79\xa8\x62\x56\xc1\xde"; | |
197 | tpm_util_pcrread(src_qemu, tpm_util_crb_transfer, tpm_pcrread_resp, | |
198 | sizeof(tpm_pcrread_resp)); | |
199 | ||
200 | migrate(src_qemu, ts->uri); | |
201 | wait_for_migration_complete(src_qemu); | |
202 | ||
203 | tpm_util_pcrread(dst_qemu, tpm_util_crb_transfer, tpm_pcrread_resp, | |
204 | sizeof(tpm_pcrread_resp)); | |
205 | ||
206 | qtest_quit(dst_qemu); | |
207 | qtest_quit(src_qemu); | |
208 | ||
209 | tpm_util_swtpm_kill(dst_tpm_pid); | |
210 | if (dst_tpm_addr) { | |
211 | g_unlink(dst_tpm_addr->u.q_unix.path); | |
212 | qapi_free_SocketAddress(dst_tpm_addr); | |
213 | } | |
214 | ||
215 | err_src_tpm_kill: | |
216 | tpm_util_swtpm_kill(src_tpm_pid); | |
217 | if (src_tpm_addr) { | |
218 | g_unlink(src_tpm_addr->u.q_unix.path); | |
219 | qapi_free_SocketAddress(src_tpm_addr); | |
220 | } | |
221 | } | |
222 | ||
223 | int main(int argc, char **argv) | |
224 | { | |
225 | int ret; | |
226 | TestState ts = { 0 }; | |
227 | ||
228 | ts.src_tpm_path = g_dir_make_tmp("qemu-tpm-crb-swtpm-test.XXXXXX", NULL); | |
229 | ts.dst_tpm_path = g_dir_make_tmp("qemu-tpm-crb-swtpm-test.XXXXXX", NULL); | |
230 | ts.uri = g_strdup_printf("unix:%s/migsocket", ts.src_tpm_path); | |
231 | ||
232 | module_call_init(MODULE_INIT_QOM); | |
233 | g_test_init(&argc, &argv, NULL); | |
234 | ||
235 | qtest_add_data_func("/tpm/crb-swtpm/test", &ts, tpm_crb_swtpm_test); | |
236 | qtest_add_data_func("/tpm/crb-swtpm-migration/test", &ts, | |
237 | tpm_crb_swtpm_migration_test); | |
238 | ret = g_test_run(); | |
239 | ||
240 | g_rmdir(ts.dst_tpm_path); | |
241 | g_free(ts.dst_tpm_path); | |
242 | g_rmdir(ts.src_tpm_path); | |
243 | g_free(ts.src_tpm_path); | |
244 | g_free(ts.uri); | |
245 | ||
246 | return ret; | |
247 | } |