]> Git Repo - qemu.git/blob - qtest.c
qtest: add test framework
[qemu.git] / qtest.c
1 /*
2  * Test Server
3  *
4  * Copyright IBM, Corp. 2011
5  *
6  * Authors:
7  *  Anthony Liguori   <[email protected]>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10  * See the COPYING file in the top-level directory.
11  *
12  */
13
14 #include "qtest.h"
15 #include "qemu-char.h"
16 #include "ioport.h"
17 #include "memory.h"
18 #include "hw/irq.h"
19 #include "sysemu.h"
20
21 #define MAX_IRQ 256
22
23 const char *qtest_chrdev;
24 const char *qtest_log;
25 int qtest_allowed = 0;
26
27 static FILE *qtest_log_fp;
28 static CharDriverState *qtest_chr;
29 static GString *inbuf;
30 static int irq_levels[MAX_IRQ];
31 static struct timeval start_time;
32 static bool qtest_opened;
33
34 #define FMT_timeval "%" PRId64 ".%06" PRId64
35
36 /**
37  * QTest Protocol
38  *
39  * Line based protocol, request/response based.  Server can send async messages
40  * so clients should always handle many async messages before the response
41  * comes in.
42  *
43  * Valid requests
44  *
45  *  > outb ADDR VALUE
46  *  < OK
47  *
48  *  > outw ADDR VALUE
49  *  < OK
50  *
51  *  > outl ADDR VALUE
52  *  < OK
53  *
54  *  > inb ADDR
55  *  < OK VALUE
56  *
57  *  > inw ADDR
58  *  < OK VALUE
59  *
60  *  > inl ADDR
61  *  < OK VALUE
62  *
63  *  > read ADDR SIZE
64  *  < OK DATA
65  *
66  *  > write ADDR SIZE DATA
67  *  < OK
68  *
69  * Valid async messages:
70  *
71  *  IRQ raise NUM
72  *  IRQ lower NUM
73  *
74  * ADDR, SIZE, VALUE are all integers parsed with strtoul() with a base of 0.
75  *
76  * DATA is an arbitrarily long hex number prefixed with '0x'.  If it's smaller
77  * than the expected size, the value will be zero filled at the end of the data
78  * sequence.
79  *
80  * NUM is an IRQ number.
81  */
82
83 static int hex2nib(char ch)
84 {
85     if (ch >= '0' && ch <= '9') {
86         return ch - '0';
87     } else if (ch >= 'a' && ch <= 'f') {
88         return 10 + (ch - 'a');
89     } else if (ch >= 'A' && ch <= 'F') {
90         return 10 + (ch - 'a');
91     } else {
92         return -1;
93     }
94 }
95
96 static void qtest_get_time(struct timeval *tv)
97 {
98     gettimeofday(tv, NULL);
99     tv->tv_sec -= start_time.tv_sec;
100     tv->tv_usec -= start_time.tv_usec;
101     if (tv->tv_usec < 0) {
102         tv->tv_usec += 1000000;
103         tv->tv_sec -= 1;
104     }
105 }
106
107 static void qtest_send_prefix(CharDriverState *chr)
108 {
109     struct timeval tv;
110
111     if (!qtest_log_fp || !qtest_opened) {
112         return;
113     }
114
115     qtest_get_time(&tv);
116     fprintf(qtest_log_fp, "[S +" FMT_timeval "] ",
117             tv.tv_sec, tv.tv_usec);
118 }
119
120 static void qtest_send(CharDriverState *chr, const char *fmt, ...)
121 {
122     va_list ap;
123     char buffer[1024];
124     size_t len;
125
126     va_start(ap, fmt);
127     len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
128     va_end(ap);
129
130     qemu_chr_fe_write(chr, (uint8_t *)buffer, len);
131     if (qtest_log_fp && qtest_opened) {
132         fprintf(qtest_log_fp, "%s", buffer);
133     }
134 }
135
136 static void qtest_process_command(CharDriverState *chr, gchar **words)
137 {
138     const gchar *command;
139
140     g_assert(words);
141
142     command = words[0];
143
144     if (qtest_log_fp) {
145         struct timeval tv;
146         int i;
147
148         qtest_get_time(&tv);
149         fprintf(qtest_log_fp, "[R +" FMT_timeval "]",
150                 tv.tv_sec, tv.tv_usec);
151         for (i = 0; words[i]; i++) {
152             fprintf(qtest_log_fp, " %s", words[i]);
153         }
154         fprintf(qtest_log_fp, "\n");
155     }
156
157     g_assert(command);
158     if (strcmp(words[0], "outb") == 0 ||
159         strcmp(words[0], "outw") == 0 ||
160         strcmp(words[0], "outl") == 0) {
161         uint16_t addr;
162         uint32_t value;
163
164         g_assert(words[1] && words[2]);
165         addr = strtol(words[1], NULL, 0);
166         value = strtol(words[2], NULL, 0);
167
168         if (words[0][3] == 'b') {
169             cpu_outb(addr, value);
170         } else if (words[0][3] == 'w') {
171             cpu_outw(addr, value);
172         } else if (words[0][3] == 'l') {
173             cpu_outl(addr, value);
174         }
175         qtest_send_prefix(chr);
176         qtest_send(chr, "OK\n");
177     } else if (strcmp(words[0], "inb") == 0 ||
178         strcmp(words[0], "inw") == 0 ||
179         strcmp(words[0], "inl") == 0) {
180         uint16_t addr;
181         uint32_t value = -1U;
182
183         g_assert(words[1]);
184         addr = strtol(words[1], NULL, 0);
185
186         if (words[0][2] == 'b') {
187             value = cpu_inb(addr);
188         } else if (words[0][2] == 'w') {
189             value = cpu_inw(addr);
190         } else if (words[0][2] == 'l') {
191             value = cpu_inl(addr);
192         }
193         qtest_send_prefix(chr);
194         qtest_send(chr, "OK 0x%04x\n", value);
195     } else if (strcmp(words[0], "read") == 0) {
196         uint64_t addr, len, i;
197         uint8_t *data;
198
199         g_assert(words[1] && words[2]);
200         addr = strtoul(words[1], NULL, 0);
201         len = strtoul(words[2], NULL, 0);
202
203         data = g_malloc(len);
204         cpu_physical_memory_read(addr, data, len);
205
206         qtest_send_prefix(chr);
207         qtest_send(chr, "OK 0x");
208         for (i = 0; i < len; i++) {
209             qtest_send(chr, "%02x", data[i]);
210         }
211         qtest_send(chr, "\n");
212
213         g_free(data);
214     } else if (strcmp(words[0], "write") == 0) {
215         uint64_t addr, len, i;
216         uint8_t *data;
217         size_t data_len;
218
219         g_assert(words[1] && words[2] && words[3]);
220         addr = strtoul(words[1], NULL, 0);
221         len = strtoul(words[2], NULL, 0);
222
223         data_len = strlen(words[3]);
224         if (data_len < 3) {
225             qtest_send(chr, "ERR invalid argument size\n");
226             return;
227         }
228
229         data = g_malloc(len);
230         for (i = 0; i < len; i++) {
231             if ((i * 2 + 4) <= data_len) {
232                 data[i] = hex2nib(words[3][i * 2 + 2]) << 4;
233                 data[i] |= hex2nib(words[3][i * 2 + 3]);
234             } else {
235                 data[i] = 0;
236             }
237         }
238         cpu_physical_memory_write(addr, data, len);
239         g_free(data);
240
241         qtest_send_prefix(chr);
242         qtest_send(chr, "OK\n");
243     } else {
244         qtest_send_prefix(chr);
245         qtest_send(chr, "FAIL Unknown command `%s'\n", words[0]);
246     }
247 }
248
249 static void qtest_process_inbuf(CharDriverState *chr, GString *inbuf)
250 {
251     char *end;
252
253     while ((end = strchr(inbuf->str, '\n')) != NULL) {
254         size_t offset;
255         GString *cmd;
256         gchar **words;
257
258         offset = end - inbuf->str;
259
260         cmd = g_string_new_len(inbuf->str, offset);
261         g_string_erase(inbuf, 0, offset + 1);
262
263         words = g_strsplit(cmd->str, " ", 0);
264         qtest_process_command(chr, words);
265         g_strfreev(words);
266
267         g_string_free(cmd, TRUE);
268     }
269 }
270
271 static void qtest_read(void *opaque, const uint8_t *buf, int size)
272 {
273     CharDriverState *chr = opaque;
274
275     g_string_append_len(inbuf, (const gchar *)buf, size);
276     qtest_process_inbuf(chr, inbuf);
277 }
278
279 static int qtest_can_read(void *opaque)
280 {
281     return 1024;
282 }
283
284 static void qtest_event(void *opaque, int event)
285 {
286     int i;
287
288     switch (event) {
289     case CHR_EVENT_OPENED:
290         qemu_system_reset(false);
291         for (i = 0; i < ARRAY_SIZE(irq_levels); i++) {
292             irq_levels[i] = 0;
293         }
294         gettimeofday(&start_time, NULL);
295         qtest_opened = true;
296         if (qtest_log_fp) {
297             fprintf(qtest_log_fp, "[I " FMT_timeval "] OPENED\n",
298                     start_time.tv_sec, start_time.tv_usec);
299         }
300         break;
301     case CHR_EVENT_CLOSED:
302         qtest_opened = false;
303         if (qtest_log_fp) {
304             struct timeval tv;
305             qtest_get_time(&tv);
306             fprintf(qtest_log_fp, "[I +" FMT_timeval "] CLOSED\n",
307                     tv.tv_sec, tv.tv_usec);
308         }
309         break;
310     default:
311         break;
312     }
313 }
314
315 static void qtest_set_irq(void *opaque, int irq, int level)
316 {
317     CharDriverState *chr = qtest_chr;
318     bool changed;
319
320     changed = (irq_levels[irq] != level);
321     irq_levels[irq] = level;
322
323     if (changed) {
324         qtest_send_prefix(chr);
325         qtest_send(chr, "IRQ %s %d\n",
326                    level ? "raise" : "lower", irq);
327     }
328 }
329
330 int qtest_init(void)
331 {
332     CharDriverState *chr;
333
334     g_assert(qtest_chrdev != NULL);
335
336     chr = qemu_chr_new("qtest", qtest_chrdev, NULL);
337
338     qemu_chr_add_handlers(chr, qtest_can_read, qtest_read, qtest_event, chr);
339     qemu_chr_fe_set_echo(chr, true);
340
341     inbuf = g_string_new("");
342
343     if (qtest_log) {
344         if (strcmp(qtest_log, "none") != 0) {
345             qtest_log_fp = fopen(qtest_log, "w+");
346         }
347     } else {
348         qtest_log_fp = stderr;
349     }
350
351     qtest_chr = chr;
352
353     return 0;
354 }
This page took 0.044666 seconds and 4 git commands to generate.