Merge remote-tracking branch 'remotes/kraxel/tags/vga-20180618-pull-request' into...
[qemu.git] / tests / tpm-util.c
blob672cedf90512dabd66a2715c81ff7668e1ade8b3
1 /*
2 * QTest TPM utilities
4 * Copyright (c) 2018 IBM Corporation
5 * Copyright (c) 2018 Red Hat, Inc.
7 * Authors:
8 * Stefan Berger <stefanb@linux.vnet.ibm.com>
9 * Marc-André Lureau <marcandre.lureau@redhat.com>
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.
15 #include "qemu/osdep.h"
17 #include "hw/acpi/tpm.h"
18 #include "libqtest.h"
19 #include "tpm-util.h"
20 #include "qapi/qmp/qdict.h"
22 #define TIS_REG(LOCTY, REG) \
23 (TPM_TIS_ADDR_BASE + ((LOCTY) << 12) + REG)
25 static bool got_stop;
27 void tpm_util_crb_transfer(QTestState *s,
28 const unsigned char *req, size_t req_size,
29 unsigned char *rsp, size_t rsp_size)
31 uint64_t caddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_CMD_LADDR);
32 uint64_t raddr = qtest_readq(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_RSP_ADDR);
34 qtest_writeb(s, TPM_CRB_ADDR_BASE + A_CRB_LOC_CTRL, 1);
36 qtest_memwrite(s, caddr, req, req_size);
38 uint32_t sts, start = 1;
39 uint64_t end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
40 qtest_writel(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START, start);
41 while (true) {
42 start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START);
43 if ((start & 1) == 0) {
44 break;
46 if (g_get_monotonic_time() >= end_time) {
47 break;
50 start = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_START);
51 g_assert_cmpint(start & 1, ==, 0);
52 sts = qtest_readl(s, TPM_CRB_ADDR_BASE + A_CRB_CTRL_STS);
53 g_assert_cmpint(sts & 1, ==, 0);
55 qtest_memread(s, raddr, rsp, rsp_size);
58 void tpm_util_tis_transfer(QTestState *s,
59 const unsigned char *req, size_t req_size,
60 unsigned char *rsp, size_t rsp_size)
62 uint32_t sts;
63 uint16_t bcount;
64 size_t i;
66 /* request use of locality 0 */
67 qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_ACCESS), TPM_TIS_ACCESS_REQUEST_USE);
68 qtest_writel(s, TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_COMMAND_READY);
70 sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS));
71 bcount = (sts >> 8) & 0xffff;
72 g_assert_cmpint(bcount, >=, req_size);
74 /* transmit command */
75 for (i = 0; i < req_size; i++) {
76 qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_DATA_FIFO), req[i]);
79 /* start processing */
80 qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_STS), TPM_TIS_STS_TPM_GO);
82 uint64_t end_time = g_get_monotonic_time() + 50 * G_TIME_SPAN_SECOND;
83 do {
84 sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS));
85 if ((sts & TPM_TIS_STS_DATA_AVAILABLE) != 0) {
86 break;
88 } while (g_get_monotonic_time() < end_time);
90 sts = qtest_readl(s, TIS_REG(0, TPM_TIS_REG_STS));
91 bcount = (sts >> 8) & 0xffff;
93 memset(rsp, 0, rsp_size);
94 for (i = 0; i < bcount; i++) {
95 rsp[i] = qtest_readb(s, TIS_REG(0, TPM_TIS_REG_DATA_FIFO));
98 /* relinquish use of locality 0 */
99 qtest_writeb(s, TIS_REG(0, TPM_TIS_REG_ACCESS),
100 TPM_TIS_ACCESS_ACTIVE_LOCALITY);
103 void tpm_util_startup(QTestState *s, tx_func *tx)
105 unsigned char buffer[1024];
106 unsigned char tpm_startup[] =
107 "\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00";
108 unsigned char tpm_startup_resp[] =
109 "\x80\x01\x00\x00\x00\x0a\x00\x00\x00\x00";
111 tx(s, tpm_startup, sizeof(tpm_startup), buffer, sizeof(buffer));
113 g_assert_cmpmem(buffer, sizeof(tpm_startup_resp),
114 tpm_startup_resp, sizeof(tpm_startup_resp));
117 void tpm_util_pcrextend(QTestState *s, tx_func *tx)
119 unsigned char buffer[1024];
120 unsigned char tpm_pcrextend[] =
121 "\x80\x02\x00\x00\x00\x41\x00\x00\x01\x82\x00\x00\x00\x0a\x00\x00"
122 "\x00\x09\x40\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00"
123 "\x0b\x74\x65\x73\x74\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
124 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
125 "\x00";
127 unsigned char tpm_pcrextend_resp[] =
128 "\x80\x02\x00\x00\x00\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
129 "\x01\x00\x00";
131 tx(s, tpm_pcrextend, sizeof(tpm_pcrextend), buffer, sizeof(buffer));
133 g_assert_cmpmem(buffer, sizeof(tpm_pcrextend_resp),
134 tpm_pcrextend_resp, sizeof(tpm_pcrextend_resp));
137 void tpm_util_pcrread(QTestState *s, tx_func *tx,
138 const unsigned char *exp_resp, size_t exp_resp_size)
140 unsigned char buffer[1024];
141 unsigned char tpm_pcrread[] =
142 "\x80\x01\x00\x00\x00\x14\x00\x00\x01\x7e\x00\x00\x00\x01\x00\x0b"
143 "\x03\x00\x04\x00";
145 tx(s, tpm_pcrread, sizeof(tpm_pcrread), buffer, sizeof(buffer));
147 g_assert_cmpmem(buffer, exp_resp_size, exp_resp, exp_resp_size);
150 static gboolean tpm_util_swtpm_has_tpm2(void)
152 gint mystdout;
153 gboolean succ;
154 unsigned i;
155 char buffer[10240];
156 ssize_t n;
157 gchar *swtpm_argv[] = {
158 g_strdup("swtpm"), g_strdup("socket"), g_strdup("--help"), NULL
161 succ = g_spawn_async_with_pipes(NULL, swtpm_argv, NULL,
162 G_SPAWN_SEARCH_PATH, NULL, NULL, NULL,
163 NULL, &mystdout, NULL, NULL);
164 if (!succ) {
165 goto cleanup;
168 n = read(mystdout, buffer, sizeof(buffer) - 1);
169 if (n < 0) {
170 goto cleanup;
172 buffer[n] = 0;
173 if (!strstr(buffer, "--tpm2")) {
174 succ = false;
177 cleanup:
178 for (i = 0; swtpm_argv[i]; i++) {
179 g_free(swtpm_argv[i]);
182 return succ;
185 gboolean tpm_util_swtpm_start(const char *path, GPid *pid,
186 SocketAddress **addr, GError **error)
188 char *swtpm_argv_tpmstate = g_strdup_printf("dir=%s", path);
189 char *swtpm_argv_ctrl = g_strdup_printf("type=unixio,path=%s/sock",
190 path);
191 gchar *swtpm_argv[] = {
192 g_strdup("swtpm"), g_strdup("socket"),
193 g_strdup("--tpmstate"), swtpm_argv_tpmstate,
194 g_strdup("--ctrl"), swtpm_argv_ctrl,
195 g_strdup("--tpm2"),
196 NULL
198 gboolean succ;
199 unsigned i;
201 succ = tpm_util_swtpm_has_tpm2();
202 if (!succ) {
203 goto cleanup;
206 *addr = g_new0(SocketAddress, 1);
207 (*addr)->type = SOCKET_ADDRESS_TYPE_UNIX;
208 (*addr)->u.q_unix.path = g_build_filename(path, "sock", NULL);
210 succ = g_spawn_async(NULL, swtpm_argv, NULL, G_SPAWN_SEARCH_PATH,
211 NULL, NULL, pid, error);
213 cleanup:
214 for (i = 0; swtpm_argv[i]; i++) {
215 g_free(swtpm_argv[i]);
218 return succ;
221 void tpm_util_swtpm_kill(GPid pid)
223 int n;
225 if (!pid) {
226 return;
229 g_spawn_close_pid(pid);
231 n = kill(pid, 0);
232 if (n < 0) {
233 return;
236 kill(pid, SIGKILL);
239 void tpm_util_migrate(QTestState *who, const char *uri)
241 QDict *rsp;
242 gchar *cmd;
244 cmd = g_strdup_printf("{ 'execute': 'migrate',"
245 "'arguments': { 'uri': '%s' } }",
246 uri);
247 rsp = qtest_qmp(who, cmd);
248 g_free(cmd);
249 g_assert(qdict_haskey(rsp, "return"));
250 qobject_unref(rsp);
254 * Events can get in the way of responses we are actually waiting for.
256 static QDict *tpm_util_wait_command(QTestState *who, const char *command)
258 const char *event_string;
259 QDict *response;
261 response = qtest_qmp(who, command);
263 while (qdict_haskey(response, "event")) {
264 /* OK, it was an event */
265 event_string = qdict_get_str(response, "event");
266 if (!strcmp(event_string, "STOP")) {
267 got_stop = true;
269 qobject_unref(response);
270 response = qtest_qmp_receive(who);
272 return response;
275 void tpm_util_wait_for_migration_complete(QTestState *who)
277 while (true) {
278 QDict *rsp, *rsp_return;
279 bool completed;
280 const char *status;
282 rsp = tpm_util_wait_command(who, "{ 'execute': 'query-migrate' }");
283 rsp_return = qdict_get_qdict(rsp, "return");
284 status = qdict_get_str(rsp_return, "status");
285 completed = strcmp(status, "completed") == 0;
286 g_assert_cmpstr(status, !=, "failed");
287 qobject_unref(rsp);
288 if (completed) {
289 return;
291 usleep(1000);
295 void tpm_util_migration_start_qemu(QTestState **src_qemu,
296 QTestState **dst_qemu,
297 SocketAddress *src_tpm_addr,
298 SocketAddress *dst_tpm_addr,
299 const char *miguri,
300 const char *ifmodel)
302 char *src_qemu_args, *dst_qemu_args;
304 src_qemu_args = g_strdup_printf(
305 "-chardev socket,id=chr,path=%s "
306 "-tpmdev emulator,id=dev,chardev=chr "
307 "-device %s,tpmdev=dev ",
308 src_tpm_addr->u.q_unix.path, ifmodel);
310 *src_qemu = qtest_init(src_qemu_args);
312 dst_qemu_args = g_strdup_printf(
313 "-chardev socket,id=chr,path=%s "
314 "-tpmdev emulator,id=dev,chardev=chr "
315 "-device %s,tpmdev=dev "
316 "-incoming %s",
317 dst_tpm_addr->u.q_unix.path,
318 ifmodel, miguri);
320 *dst_qemu = qtest_init(dst_qemu_args);
322 free(src_qemu_args);
323 free(dst_qemu_args);