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