xref: /openbmc/qemu/tests/qtest/tpm-util.c (revision 2f95279a)
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