blob: 46ebda1a35d0ae52b3e7e3ca12c6b38445a4820c [file] [log] [blame]
Anthony Liguoric7f0f3b2012-03-28 15:42:02 +02001/*
2 * Test Server
3 *
4 * Copyright IBM, Corp. 2011
5 *
6 * Authors:
7 * Anthony Liguori <aliguori@us.ibm.com>
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
23const char *qtest_chrdev;
24const char *qtest_log;
25int qtest_allowed = 0;
26
27static FILE *qtest_log_fp;
28static CharDriverState *qtest_chr;
29static GString *inbuf;
30static int irq_levels[MAX_IRQ];
31static struct timeval start_time;
32static 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
83static 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
96static 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
107static 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
120static 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
136static 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
249static 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
271static 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
279static int qtest_can_read(void *opaque)
280{
281 return 1024;
282}
283
284static 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
315static 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
330int 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}