|
|
1.1 ! root 1: /* ! 2: * QTest ! 3: * ! 4: * Copyright IBM, Corp. 2012 ! 5: * Copyright Red Hat, Inc. 2012 ! 6: * ! 7: * Authors: ! 8: * Anthony Liguori <[email protected]> ! 9: * Paolo Bonzini <[email protected]> ! 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 "libqtest.h" ! 16: ! 17: #include <glib.h> ! 18: #include <sys/types.h> ! 19: #include <sys/socket.h> ! 20: #include <sys/wait.h> ! 21: #include <sys/un.h> ! 22: #include <inttypes.h> ! 23: #include <errno.h> ! 24: #include <stdio.h> ! 25: #include <stdlib.h> ! 26: #include <unistd.h> ! 27: #include <string.h> ! 28: ! 29: #include "compiler.h" ! 30: #include "osdep.h" ! 31: ! 32: #define MAX_IRQ 256 ! 33: ! 34: QTestState *global_qtest; ! 35: ! 36: struct QTestState ! 37: { ! 38: int fd; ! 39: int qmp_fd; ! 40: bool irq_level[MAX_IRQ]; ! 41: GString *rx; ! 42: gchar *pid_file; ! 43: }; ! 44: ! 45: #define g_assert_no_errno(ret) do { \ ! 46: g_assert_cmpint(ret, !=, -1); \ ! 47: } while (0) ! 48: ! 49: static int init_socket(const char *socket_path) ! 50: { ! 51: struct sockaddr_un addr; ! 52: int sock; ! 53: int ret; ! 54: ! 55: sock = socket(PF_UNIX, SOCK_STREAM, 0); ! 56: g_assert_no_errno(sock); ! 57: ! 58: addr.sun_family = AF_UNIX; ! 59: snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path); ! 60: qemu_set_cloexec(sock); ! 61: ! 62: do { ! 63: ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr)); ! 64: } while (ret == -1 && errno == EINTR); ! 65: g_assert_no_errno(ret); ! 66: listen(sock, 1); ! 67: ! 68: return sock; ! 69: } ! 70: ! 71: static int socket_accept(int sock) ! 72: { ! 73: struct sockaddr_un addr; ! 74: socklen_t addrlen; ! 75: int ret; ! 76: ! 77: do { ! 78: ret = accept(sock, (struct sockaddr *)&addr, &addrlen); ! 79: } while (ret == -1 && errno == EINTR); ! 80: g_assert_no_errno(ret); ! 81: close(sock); ! 82: ! 83: return ret; ! 84: } ! 85: ! 86: QTestState *qtest_init(const char *extra_args) ! 87: { ! 88: QTestState *s; ! 89: int sock, qmpsock, ret, i; ! 90: gchar *socket_path; ! 91: gchar *qmp_socket_path; ! 92: gchar *pid_file; ! 93: gchar *command; ! 94: const char *qemu_binary; ! 95: pid_t pid; ! 96: ! 97: qemu_binary = getenv("QTEST_QEMU_BINARY"); ! 98: g_assert(qemu_binary != NULL); ! 99: ! 100: socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid()); ! 101: qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid()); ! 102: pid_file = g_strdup_printf("/tmp/qtest-%d.pid", getpid()); ! 103: ! 104: s = g_malloc(sizeof(*s)); ! 105: ! 106: sock = init_socket(socket_path); ! 107: qmpsock = init_socket(qmp_socket_path); ! 108: ! 109: pid = fork(); ! 110: if (pid == 0) { ! 111: command = g_strdup_printf("%s " ! 112: "-qtest unix:%s,nowait " ! 113: "-qtest-log /dev/null " ! 114: "-qmp unix:%s,nowait " ! 115: "-pidfile %s " ! 116: "-machine accel=qtest " ! 117: "%s", qemu_binary, socket_path, ! 118: qmp_socket_path, pid_file, ! 119: extra_args ?: ""); ! 120: ! 121: ret = system(command); ! 122: exit(ret); ! 123: g_free(command); ! 124: } ! 125: ! 126: s->fd = socket_accept(sock); ! 127: s->qmp_fd = socket_accept(qmpsock); ! 128: ! 129: s->rx = g_string_new(""); ! 130: s->pid_file = pid_file; ! 131: for (i = 0; i < MAX_IRQ; i++) { ! 132: s->irq_level[i] = false; ! 133: } ! 134: ! 135: g_free(socket_path); ! 136: g_free(qmp_socket_path); ! 137: ! 138: /* Read the QMP greeting and then do the handshake */ ! 139: qtest_qmp(s, ""); ! 140: qtest_qmp(s, "{ 'execute': 'qmp_capabilities' }"); ! 141: ! 142: return s; ! 143: } ! 144: ! 145: void qtest_quit(QTestState *s) ! 146: { ! 147: FILE *f; ! 148: char buffer[1024]; ! 149: ! 150: f = fopen(s->pid_file, "r"); ! 151: if (f) { ! 152: if (fgets(buffer, sizeof(buffer), f)) { ! 153: pid_t pid = atoi(buffer); ! 154: int status = 0; ! 155: ! 156: kill(pid, SIGTERM); ! 157: waitpid(pid, &status, 0); ! 158: } ! 159: ! 160: fclose(f); ! 161: } ! 162: } ! 163: ! 164: static void socket_sendf(int fd, const char *fmt, va_list ap) ! 165: { ! 166: gchar *str; ! 167: size_t size, offset; ! 168: ! 169: str = g_strdup_vprintf(fmt, ap); ! 170: size = strlen(str); ! 171: ! 172: offset = 0; ! 173: while (offset < size) { ! 174: ssize_t len; ! 175: ! 176: len = write(fd, str + offset, size - offset); ! 177: if (len == -1 && errno == EINTR) { ! 178: continue; ! 179: } ! 180: ! 181: g_assert_no_errno(len); ! 182: g_assert_cmpint(len, >, 0); ! 183: ! 184: offset += len; ! 185: } ! 186: } ! 187: ! 188: static void GCC_FMT_ATTR(2, 3) qtest_sendf(QTestState *s, const char *fmt, ...) ! 189: { ! 190: va_list ap; ! 191: ! 192: va_start(ap, fmt); ! 193: socket_sendf(s->fd, fmt, ap); ! 194: va_end(ap); ! 195: } ! 196: ! 197: static GString *qtest_recv_line(QTestState *s) ! 198: { ! 199: GString *line; ! 200: size_t offset; ! 201: char *eol; ! 202: ! 203: while ((eol = strchr(s->rx->str, '\n')) == NULL) { ! 204: ssize_t len; ! 205: char buffer[1024]; ! 206: ! 207: len = read(s->fd, buffer, sizeof(buffer)); ! 208: if (len == -1 && errno == EINTR) { ! 209: continue; ! 210: } ! 211: ! 212: if (len == -1 || len == 0) { ! 213: fprintf(stderr, "Broken pipe\n"); ! 214: exit(1); ! 215: } ! 216: ! 217: g_string_append_len(s->rx, buffer, len); ! 218: } ! 219: ! 220: offset = eol - s->rx->str; ! 221: line = g_string_new_len(s->rx->str, offset); ! 222: g_string_erase(s->rx, 0, offset + 1); ! 223: ! 224: return line; ! 225: } ! 226: ! 227: static gchar **qtest_rsp(QTestState *s, int expected_args) ! 228: { ! 229: GString *line; ! 230: gchar **words; ! 231: int i; ! 232: ! 233: redo: ! 234: line = qtest_recv_line(s); ! 235: words = g_strsplit(line->str, " ", 0); ! 236: g_string_free(line, TRUE); ! 237: ! 238: if (strcmp(words[0], "IRQ") == 0) { ! 239: int irq; ! 240: ! 241: g_assert(words[1] != NULL); ! 242: g_assert(words[2] != NULL); ! 243: ! 244: irq = strtoul(words[2], NULL, 0); ! 245: g_assert_cmpint(irq, >=, 0); ! 246: g_assert_cmpint(irq, <, MAX_IRQ); ! 247: ! 248: if (strcmp(words[1], "raise") == 0) { ! 249: s->irq_level[irq] = true; ! 250: } else { ! 251: s->irq_level[irq] = false; ! 252: } ! 253: ! 254: g_strfreev(words); ! 255: goto redo; ! 256: } ! 257: ! 258: g_assert(words[0] != NULL); ! 259: g_assert_cmpstr(words[0], ==, "OK"); ! 260: ! 261: if (expected_args) { ! 262: for (i = 0; i < expected_args; i++) { ! 263: g_assert(words[i] != NULL); ! 264: } ! 265: } else { ! 266: g_strfreev(words); ! 267: } ! 268: ! 269: return words; ! 270: } ! 271: ! 272: void qtest_qmp(QTestState *s, const char *fmt, ...) ! 273: { ! 274: va_list ap; ! 275: bool has_reply = false; ! 276: int nesting = 0; ! 277: ! 278: /* Send QMP request */ ! 279: va_start(ap, fmt); ! 280: socket_sendf(s->qmp_fd, fmt, ap); ! 281: va_end(ap); ! 282: ! 283: /* Receive reply */ ! 284: while (!has_reply || nesting > 0) { ! 285: ssize_t len; ! 286: char c; ! 287: ! 288: len = read(s->qmp_fd, &c, 1); ! 289: if (len == -1 && errno == EINTR) { ! 290: continue; ! 291: } ! 292: ! 293: switch (c) { ! 294: case '{': ! 295: nesting++; ! 296: has_reply = true; ! 297: break; ! 298: case '}': ! 299: nesting--; ! 300: break; ! 301: } ! 302: } ! 303: } ! 304: ! 305: const char *qtest_get_arch(void) ! 306: { ! 307: const char *qemu = getenv("QTEST_QEMU_BINARY"); ! 308: const char *end = strrchr(qemu, '/'); ! 309: ! 310: return end + strlen("/qemu-system-"); ! 311: } ! 312: ! 313: bool qtest_get_irq(QTestState *s, int num) ! 314: { ! 315: /* dummy operation in order to make sure irq is up to date */ ! 316: qtest_inb(s, 0); ! 317: ! 318: return s->irq_level[num]; ! 319: } ! 320: ! 321: static int64_t qtest_clock_rsp(QTestState *s) ! 322: { ! 323: gchar **words; ! 324: int64_t clock; ! 325: words = qtest_rsp(s, 2); ! 326: clock = g_ascii_strtoll(words[1], NULL, 0); ! 327: g_strfreev(words); ! 328: return clock; ! 329: } ! 330: ! 331: int64_t qtest_clock_step_next(QTestState *s) ! 332: { ! 333: qtest_sendf(s, "clock_step\n"); ! 334: return qtest_clock_rsp(s); ! 335: } ! 336: ! 337: int64_t qtest_clock_step(QTestState *s, int64_t step) ! 338: { ! 339: qtest_sendf(s, "clock_step %"PRIi64"\n", step); ! 340: return qtest_clock_rsp(s); ! 341: } ! 342: ! 343: int64_t qtest_clock_set(QTestState *s, int64_t val) ! 344: { ! 345: qtest_sendf(s, "clock_set %"PRIi64"\n", val); ! 346: return qtest_clock_rsp(s); ! 347: } ! 348: ! 349: void qtest_irq_intercept_out(QTestState *s, const char *qom_path) ! 350: { ! 351: qtest_sendf(s, "irq_intercept_out %s\n", qom_path); ! 352: qtest_rsp(s, 0); ! 353: } ! 354: ! 355: void qtest_irq_intercept_in(QTestState *s, const char *qom_path) ! 356: { ! 357: qtest_sendf(s, "irq_intercept_in %s\n", qom_path); ! 358: qtest_rsp(s, 0); ! 359: } ! 360: ! 361: static void qtest_out(QTestState *s, const char *cmd, uint16_t addr, uint32_t value) ! 362: { ! 363: qtest_sendf(s, "%s 0x%x 0x%x\n", cmd, addr, value); ! 364: qtest_rsp(s, 0); ! 365: } ! 366: ! 367: void qtest_outb(QTestState *s, uint16_t addr, uint8_t value) ! 368: { ! 369: qtest_out(s, "outb", addr, value); ! 370: } ! 371: ! 372: void qtest_outw(QTestState *s, uint16_t addr, uint16_t value) ! 373: { ! 374: qtest_out(s, "outw", addr, value); ! 375: } ! 376: ! 377: void qtest_outl(QTestState *s, uint16_t addr, uint32_t value) ! 378: { ! 379: qtest_out(s, "outl", addr, value); ! 380: } ! 381: ! 382: static uint32_t qtest_in(QTestState *s, const char *cmd, uint16_t addr) ! 383: { ! 384: gchar **args; ! 385: uint32_t value; ! 386: ! 387: qtest_sendf(s, "%s 0x%x\n", cmd, addr); ! 388: args = qtest_rsp(s, 2); ! 389: value = strtoul(args[1], NULL, 0); ! 390: g_strfreev(args); ! 391: ! 392: return value; ! 393: } ! 394: ! 395: uint8_t qtest_inb(QTestState *s, uint16_t addr) ! 396: { ! 397: return qtest_in(s, "inb", addr); ! 398: } ! 399: ! 400: uint16_t qtest_inw(QTestState *s, uint16_t addr) ! 401: { ! 402: return qtest_in(s, "inw", addr); ! 403: } ! 404: ! 405: uint32_t qtest_inl(QTestState *s, uint16_t addr) ! 406: { ! 407: return qtest_in(s, "inl", addr); ! 408: } ! 409: ! 410: static int hex2nib(char ch) ! 411: { ! 412: if (ch >= '0' && ch <= '9') { ! 413: return ch - '0'; ! 414: } else if (ch >= 'a' && ch <= 'f') { ! 415: return 10 + (ch - 'a'); ! 416: } else if (ch >= 'A' && ch <= 'F') { ! 417: return 10 + (ch - 'a'); ! 418: } else { ! 419: return -1; ! 420: } ! 421: } ! 422: ! 423: void qtest_memread(QTestState *s, uint64_t addr, void *data, size_t size) ! 424: { ! 425: uint8_t *ptr = data; ! 426: gchar **args; ! 427: size_t i; ! 428: ! 429: qtest_sendf(s, "read 0x%" PRIx64 " 0x%zx\n", addr, size); ! 430: args = qtest_rsp(s, 2); ! 431: ! 432: for (i = 0; i < size; i++) { ! 433: ptr[i] = hex2nib(args[1][2 + (i * 2)]) << 4; ! 434: ptr[i] |= hex2nib(args[1][2 + (i * 2) + 1]); ! 435: } ! 436: ! 437: g_strfreev(args); ! 438: } ! 439: ! 440: void qtest_add_func(const char *str, void (*fn)) ! 441: { ! 442: gchar *path = g_strdup_printf("/%s/%s", qtest_get_arch(), str); ! 443: g_test_add_func(path, fn); ! 444: } ! 445: ! 446: void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size) ! 447: { ! 448: const uint8_t *ptr = data; ! 449: size_t i; ! 450: ! 451: qtest_sendf(s, "write 0x%" PRIx64 " 0x%zx 0x", addr, size); ! 452: for (i = 0; i < size; i++) { ! 453: qtest_sendf(s, "%02x", ptr[i]); ! 454: } ! 455: qtest_sendf(s, "\n"); ! 456: qtest_rsp(s, 0); ! 457: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.