xref: /openbmc/qemu/tests/qtest/libqos/libqos.c (revision 9f6df01d)
1 #include "qemu/osdep.h"
2 #include <sys/wait.h>
3 
4 #include "libqtest.h"
5 #include "libqos.h"
6 #include "pci.h"
7 #include "qapi/qmp/qdict.h"
8 
9 /*** Test Setup & Teardown ***/
10 
11 /**
12  * Launch QEMU with the given command line,
13  * and then set up interrupts and our guest malloc interface.
14  * Never returns NULL:
15  * Terminates the application in case an error is encountered.
16  */
17 QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap)
18 {
19     char *cmdline;
20 
21     QOSState *qs = g_new0(QOSState, 1);
22 
23     cmdline = g_strdup_vprintf(cmdline_fmt, ap);
24     qs->qts = qtest_init(cmdline);
25     qs->ops = ops;
26     if (ops) {
27         ops->alloc_init(&qs->alloc, qs->qts, ALLOC_NO_FLAGS);
28         qs->pcibus = ops->qpci_new(qs->qts, &qs->alloc);
29     }
30 
31     g_free(cmdline);
32     return qs;
33 }
34 
35 /**
36  * Launch QEMU with the given command line,
37  * and then set up interrupts and our guest malloc interface.
38  */
39 QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...)
40 {
41     QOSState *qs;
42     va_list ap;
43 
44     va_start(ap, cmdline_fmt);
45     qs = qtest_vboot(ops, cmdline_fmt, ap);
46     va_end(ap);
47 
48     return qs;
49 }
50 
51 /**
52  * Tear down the QEMU instance.
53  */
54 void qtest_common_shutdown(QOSState *qs)
55 {
56     if (qs->ops) {
57         if (qs->pcibus && qs->ops->qpci_free) {
58             qs->ops->qpci_free(qs->pcibus);
59             qs->pcibus = NULL;
60         }
61     }
62     alloc_destroy(&qs->alloc);
63     qtest_quit(qs->qts);
64     g_free(qs);
65 }
66 
67 void qtest_shutdown(QOSState *qs)
68 {
69     if (qs->ops && qs->ops->shutdown) {
70         qs->ops->shutdown(qs);
71     } else {
72         qtest_common_shutdown(qs);
73     }
74 }
75 
76 static QDict *qmp_execute(QTestState *qts, const char *command)
77 {
78     return qtest_qmp(qts, "{ 'execute': %s }", command);
79 }
80 
81 void migrate(QOSState *from, QOSState *to, const char *uri)
82 {
83     const char *st;
84     QDict *rsp, *sub;
85     bool running;
86 
87     /* Is the machine currently running? */
88     rsp = qmp_execute(from->qts, "query-status");
89     g_assert(qdict_haskey(rsp, "return"));
90     sub = qdict_get_qdict(rsp, "return");
91     g_assert(qdict_haskey(sub, "running"));
92     running = qdict_get_bool(sub, "running");
93     qobject_unref(rsp);
94 
95     /* Issue the migrate command. */
96     rsp = qtest_qmp(from->qts,
97                     "{ 'execute': 'migrate', 'arguments': { 'uri': %s }}",
98                     uri);
99     g_assert(qdict_haskey(rsp, "return"));
100     qobject_unref(rsp);
101 
102     /* Wait for STOP event, but only if we were running: */
103     if (running) {
104         qtest_qmp_eventwait(from->qts, "STOP");
105     }
106 
107     /* If we were running, we can wait for an event. */
108     if (running) {
109         migrate_allocator(&from->alloc, &to->alloc);
110         qtest_qmp_eventwait(to->qts, "RESUME");
111         return;
112     }
113 
114     /* Otherwise, we need to wait: poll until migration is completed. */
115     while (1) {
116         rsp = qmp_execute(from->qts, "query-migrate");
117         g_assert(qdict_haskey(rsp, "return"));
118         sub = qdict_get_qdict(rsp, "return");
119         g_assert(qdict_haskey(sub, "status"));
120         st = qdict_get_str(sub, "status");
121 
122         /* "setup", "active", "completed", "failed", "cancelled" */
123         if (strcmp(st, "completed") == 0) {
124             qobject_unref(rsp);
125             break;
126         }
127 
128         if ((strcmp(st, "setup") == 0) || (strcmp(st, "active") == 0)
129             || (strcmp(st, "wait-unplug") == 0)) {
130             qobject_unref(rsp);
131             g_usleep(5000);
132             continue;
133         }
134 
135         fprintf(stderr, "Migration did not complete, status: %s\n", st);
136         g_assert_not_reached();
137     }
138 
139     migrate_allocator(&from->alloc, &to->alloc);
140 }
141 
142 bool have_qemu_img(void)
143 {
144     char *rpath;
145     const char *path = getenv("QTEST_QEMU_IMG");
146     if (!path) {
147         return false;
148     }
149 
150     rpath = realpath(path, NULL);
151     if (!rpath) {
152         return false;
153     } else {
154         free(rpath);
155         return true;
156     }
157 }
158 
159 void mkimg(const char *file, const char *fmt, unsigned size_mb)
160 {
161     gchar *cli;
162     bool ret;
163     int rc;
164     GError *err = NULL;
165     char *qemu_img_path;
166     gchar *out, *out2;
167     char *qemu_img_abs_path;
168 
169     qemu_img_path = getenv("QTEST_QEMU_IMG");
170     g_assert(qemu_img_path);
171     qemu_img_abs_path = realpath(qemu_img_path, NULL);
172     g_assert(qemu_img_abs_path);
173 
174     cli = g_strdup_printf("%s create -f %s %s %uM", qemu_img_abs_path,
175                           fmt, file, size_mb);
176     ret = g_spawn_command_line_sync(cli, &out, &out2, &rc, &err);
177     if (err || !g_spawn_check_exit_status(rc, &err)) {
178         fprintf(stderr, "%s\n", err->message);
179         g_error_free(err);
180     }
181     g_assert(ret && !err);
182 
183     g_free(out);
184     g_free(out2);
185     g_free(cli);
186     free(qemu_img_abs_path);
187 }
188 
189 void mkqcow2(const char *file, unsigned size_mb)
190 {
191     return mkimg(file, "qcow2", size_mb);
192 }
193 
194 void prepare_blkdebug_script(const char *debug_fn, const char *event)
195 {
196     FILE *debug_file = fopen(debug_fn, "w");
197     int ret;
198 
199     fprintf(debug_file, "[inject-error]\n");
200     fprintf(debug_file, "event = \"%s\"\n", event);
201     fprintf(debug_file, "errno = \"5\"\n");
202     fprintf(debug_file, "state = \"1\"\n");
203     fprintf(debug_file, "immediately = \"off\"\n");
204     fprintf(debug_file, "once = \"on\"\n");
205 
206     fprintf(debug_file, "[set-state]\n");
207     fprintf(debug_file, "event = \"%s\"\n", event);
208     fprintf(debug_file, "new_state = \"2\"\n");
209     fflush(debug_file);
210     g_assert(!ferror(debug_file));
211 
212     ret = fclose(debug_file);
213     g_assert(ret == 0);
214 }
215 
216 void generate_pattern(void *buffer, size_t len, size_t cycle_len)
217 {
218     int i, j;
219     unsigned char *tx = (unsigned char *)buffer;
220     unsigned char p;
221     size_t *sx;
222 
223     /* Write an indicative pattern that varies and is unique per-cycle */
224     p = rand() % 256;
225     for (i = 0; i < len; i++) {
226         tx[i] = p++ % 256;
227         if (i % cycle_len == 0) {
228             p = rand() % 256;
229         }
230     }
231 
232     /* force uniqueness by writing an id per-cycle */
233     for (i = 0; i < len / cycle_len; i++) {
234         j = i * cycle_len;
235         if (j + sizeof(*sx) <= len) {
236             sx = (size_t *)&tx[j];
237             *sx = i;
238         }
239     }
240 }
241