]>
Commit | Line | Data |
---|---|---|
b21373d0 SB |
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 <[email protected]> | |
9 | * Marc-André Lureau <[email protected]> | |
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" | |
a2ce7dbd | 18 | #include "libqos/libqtest.h" |
b21373d0 | 19 | #include "tpm-util.h" |
b1e4b7c6 SB |
20 | #include "qapi/qmp/qdict.h" |
21 | ||
b21373d0 SB |
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 | ||
70663851 SB |
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 | ||
b21373d0 SB |
98 | void tpm_util_startup(QTestState *s, tx_func *tx) |
99 | { | |
100 | unsigned char buffer[1024]; | |
ed943cc9 | 101 | static const unsigned char tpm_startup[] = |
b21373d0 | 102 | "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00"; |
ed943cc9 | 103 | static const unsigned char tpm_startup_resp[] = |
b21373d0 SB |
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]; | |
ed943cc9 | 115 | static const unsigned char tpm_pcrextend[] = |
b21373d0 SB |
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 | ||
ed943cc9 | 122 | static const unsigned char tpm_pcrextend_resp[] = |
b21373d0 SB |
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]; | |
ed943cc9 | 136 | static const unsigned char tpm_pcrread[] = |
b21373d0 SB |
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 | ||
df8a7568 SB |
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); | |
b21373d0 SB |
147 | } |
148 | ||
e2f246a5 | 149 | bool tpm_util_swtpm_has_tpm2(void) |
b21373d0 | 150 | { |
c1d99200 MAL |
151 | bool has_tpm2 = false; |
152 | char *out = NULL; | |
153 | static const char *argv[] = { | |
154 | "swtpm", "socket", "--help", NULL | |
b21373d0 SB |
155 | }; |
156 | ||
c1d99200 MAL |
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; | |
b21373d0 SB |
168 | } |
169 | ||
c1d99200 MAL |
170 | if (strstr(out, "--tpm2")) { |
171 | has_tpm2 = true; | |
b21373d0 SB |
172 | } |
173 | ||
c1d99200 MAL |
174 | g_free(out); |
175 | return has_tpm2; | |
b21373d0 SB |
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 | ||
b21373d0 SB |
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 | ||
b21373d0 SB |
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 | } | |
b1e4b7c6 SB |
225 | |
226 | void tpm_util_migrate(QTestState *who, const char *uri) | |
227 | { | |
228 | QDict *rsp; | |
b1e4b7c6 | 229 | |
015715f5 MA |
230 | rsp = qtest_qmp(who, |
231 | "{ 'execute': 'migrate', 'arguments': { 'uri': %s } }", | |
232 | uri); | |
b1e4b7c6 SB |
233 | g_assert(qdict_haskey(rsp, "return")); |
234 | qobject_unref(rsp); | |
235 | } | |
236 | ||
b1e4b7c6 SB |
237 | void tpm_util_wait_for_migration_complete(QTestState *who) |
238 | { | |
239 | while (true) { | |
3cd46d42 | 240 | QDict *rsp_return; |
b1e4b7c6 SB |
241 | bool completed; |
242 | const char *status; | |
243 | ||
3cd46d42 MA |
244 | qtest_qmp_send(who, "{ 'execute': 'query-migrate' }"); |
245 | rsp_return = qtest_qmp_receive_success(who, NULL, NULL); | |
b1e4b7c6 SB |
246 | status = qdict_get_str(rsp_return, "status"); |
247 | completed = strcmp(status, "completed") == 0; | |
248 | g_assert_cmpstr(status, !=, "failed"); | |
3cd46d42 | 249 | qobject_unref(rsp_return); |
b1e4b7c6 SB |
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, | |
ea71a336 | 261 | const char *miguri, |
551cabdf EA |
262 | const char *ifmodel, |
263 | const char *machine_options) | |
b1e4b7c6 SB |
264 | { |
265 | char *src_qemu_args, *dst_qemu_args; | |
266 | ||
267 | src_qemu_args = g_strdup_printf( | |
551cabdf | 268 | "%s " |
b1e4b7c6 SB |
269 | "-chardev socket,id=chr,path=%s " |
270 | "-tpmdev emulator,id=dev,chardev=chr " | |
ea71a336 | 271 | "-device %s,tpmdev=dev ", |
551cabdf | 272 | machine_options ? : "", src_tpm_addr->u.q_unix.path, ifmodel); |
b1e4b7c6 SB |
273 | |
274 | *src_qemu = qtest_init(src_qemu_args); | |
275 | ||
276 | dst_qemu_args = g_strdup_printf( | |
551cabdf | 277 | "%s " |
b1e4b7c6 SB |
278 | "-chardev socket,id=chr,path=%s " |
279 | "-tpmdev emulator,id=dev,chardev=chr " | |
ea71a336 | 280 | "-device %s,tpmdev=dev " |
b1e4b7c6 | 281 | "-incoming %s", |
551cabdf | 282 | machine_options ? : "", |
b1e4b7c6 | 283 | dst_tpm_addr->u.q_unix.path, |
ea71a336 | 284 | ifmodel, miguri); |
b1e4b7c6 SB |
285 | |
286 | *dst_qemu = qtest_init(dst_qemu_args); | |
287 | ||
288 | free(src_qemu_args); | |
289 | free(dst_qemu_args); | |
290 | } |