Annotation of qemu/qtest.c, revision 1.1

1.1     ! root        1: /*
        !             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 "hw/qdev.h"
        !            16: #include "qemu-char.h"
        !            17: #include "ioport.h"
        !            18: #include "memory.h"
        !            19: #include "hw/irq.h"
        !            20: #include "sysemu.h"
        !            21: #include "cpus.h"
        !            22: 
        !            23: #define MAX_IRQ 256
        !            24: 
        !            25: const char *qtest_chrdev;
        !            26: const char *qtest_log;
        !            27: int qtest_allowed = 0;
        !            28: 
        !            29: static DeviceState *irq_intercept_dev;
        !            30: static FILE *qtest_log_fp;
        !            31: static CharDriverState *qtest_chr;
        !            32: static GString *inbuf;
        !            33: static int irq_levels[MAX_IRQ];
        !            34: static qemu_timeval start_time;
        !            35: static bool qtest_opened;
        !            36: 
        !            37: #define FMT_timeval "%ld.%06ld"
        !            38: 
        !            39: /**
        !            40:  * QTest Protocol
        !            41:  *
        !            42:  * Line based protocol, request/response based.  Server can send async messages
        !            43:  * so clients should always handle many async messages before the response
        !            44:  * comes in.
        !            45:  *
        !            46:  * Valid requests
        !            47:  *
        !            48:  * Clock management:
        !            49:  *
        !            50:  * The qtest client is completely in charge of the vm_clock.  qtest commands
        !            51:  * let you adjust the value of the clock (monotonically).  All the commands
        !            52:  * return the current value of the clock in nanoseconds.
        !            53:  *
        !            54:  *  > clock_step
        !            55:  *  < OK VALUE
        !            56:  *
        !            57:  *     Advance the clock to the next deadline.  Useful when waiting for
        !            58:  *     asynchronous events.
        !            59:  *
        !            60:  *  > clock_step NS
        !            61:  *  < OK VALUE
        !            62:  *
        !            63:  *     Advance the clock by NS nanoseconds.
        !            64:  *
        !            65:  *  > clock_set NS
        !            66:  *  < OK VALUE
        !            67:  *
        !            68:  *     Advance the clock to NS nanoseconds (do nothing if it's already past).
        !            69:  *
        !            70:  * PIO and memory access:
        !            71:  *
        !            72:  *  > outb ADDR VALUE
        !            73:  *  < OK
        !            74:  *
        !            75:  *  > outw ADDR VALUE
        !            76:  *  < OK
        !            77:  *
        !            78:  *  > outl ADDR VALUE
        !            79:  *  < OK
        !            80:  *
        !            81:  *  > inb ADDR
        !            82:  *  < OK VALUE
        !            83:  *
        !            84:  *  > inw ADDR
        !            85:  *  < OK VALUE
        !            86:  *
        !            87:  *  > inl ADDR
        !            88:  *  < OK VALUE
        !            89:  *
        !            90:  *  > read ADDR SIZE
        !            91:  *  < OK DATA
        !            92:  *
        !            93:  *  > write ADDR SIZE DATA
        !            94:  *  < OK
        !            95:  *
        !            96:  * ADDR, SIZE, VALUE are all integers parsed with strtoul() with a base of 0.
        !            97:  *
        !            98:  * DATA is an arbitrarily long hex number prefixed with '0x'.  If it's smaller
        !            99:  * than the expected size, the value will be zero filled at the end of the data
        !           100:  * sequence.
        !           101:  *
        !           102:  * IRQ management:
        !           103:  *
        !           104:  *  > irq_intercept_in QOM-PATH
        !           105:  *  < OK
        !           106:  *
        !           107:  *  > irq_intercept_out QOM-PATH
        !           108:  *  < OK
        !           109:  *
        !           110:  * Attach to the gpio-in (resp. gpio-out) pins exported by the device at
        !           111:  * QOM-PATH.  When the pin is triggered, one of the following async messages
        !           112:  * will be printed to the qtest stream:
        !           113:  *
        !           114:  *  IRQ raise NUM
        !           115:  *  IRQ lower NUM
        !           116:  *
        !           117:  * where NUM is an IRQ number.  For the PC, interrupts can be intercepted
        !           118:  * simply with "irq_intercept_in ioapic" (note that IRQ0 comes out with
        !           119:  * NUM=0 even though it is remapped to GSI 2).
        !           120:  */
        !           121: 
        !           122: static int hex2nib(char ch)
        !           123: {
        !           124:     if (ch >= '0' && ch <= '9') {
        !           125:         return ch - '0';
        !           126:     } else if (ch >= 'a' && ch <= 'f') {
        !           127:         return 10 + (ch - 'a');
        !           128:     } else if (ch >= 'A' && ch <= 'F') {
        !           129:         return 10 + (ch - 'a');
        !           130:     } else {
        !           131:         return -1;
        !           132:     }
        !           133: }
        !           134: 
        !           135: static void qtest_get_time(qemu_timeval *tv)
        !           136: {
        !           137:     qemu_gettimeofday(tv);
        !           138:     tv->tv_sec -= start_time.tv_sec;
        !           139:     tv->tv_usec -= start_time.tv_usec;
        !           140:     if (tv->tv_usec < 0) {
        !           141:         tv->tv_usec += 1000000;
        !           142:         tv->tv_sec -= 1;
        !           143:     }
        !           144: }
        !           145: 
        !           146: static void qtest_send_prefix(CharDriverState *chr)
        !           147: {
        !           148:     qemu_timeval tv;
        !           149: 
        !           150:     if (!qtest_log_fp || !qtest_opened) {
        !           151:         return;
        !           152:     }
        !           153: 
        !           154:     qtest_get_time(&tv);
        !           155:     fprintf(qtest_log_fp, "[S +" FMT_timeval "] ",
        !           156:             tv.tv_sec, (long) tv.tv_usec);
        !           157: }
        !           158: 
        !           159: static void GCC_FMT_ATTR(2, 3) qtest_send(CharDriverState *chr,
        !           160:                                           const char *fmt, ...)
        !           161: {
        !           162:     va_list ap;
        !           163:     char buffer[1024];
        !           164:     size_t len;
        !           165: 
        !           166:     va_start(ap, fmt);
        !           167:     len = vsnprintf(buffer, sizeof(buffer), fmt, ap);
        !           168:     va_end(ap);
        !           169: 
        !           170:     qemu_chr_fe_write(chr, (uint8_t *)buffer, len);
        !           171:     if (qtest_log_fp && qtest_opened) {
        !           172:         fprintf(qtest_log_fp, "%s", buffer);
        !           173:     }
        !           174: }
        !           175: 
        !           176: static void qtest_irq_handler(void *opaque, int n, int level)
        !           177: {
        !           178:     qemu_irq *old_irqs = opaque;
        !           179:     qemu_set_irq(old_irqs[n], level);
        !           180: 
        !           181:     if (irq_levels[n] != level) {
        !           182:         CharDriverState *chr = qtest_chr;
        !           183:         irq_levels[n] = level;
        !           184:         qtest_send_prefix(chr);
        !           185:         qtest_send(chr, "IRQ %s %d\n",
        !           186:                    level ? "raise" : "lower", n);
        !           187:     }
        !           188: }
        !           189: 
        !           190: static void qtest_process_command(CharDriverState *chr, gchar **words)
        !           191: {
        !           192:     const gchar *command;
        !           193: 
        !           194:     g_assert(words);
        !           195: 
        !           196:     command = words[0];
        !           197: 
        !           198:     if (qtest_log_fp) {
        !           199:         qemu_timeval tv;
        !           200:         int i;
        !           201: 
        !           202:         qtest_get_time(&tv);
        !           203:         fprintf(qtest_log_fp, "[R +" FMT_timeval "]",
        !           204:                 tv.tv_sec, (long) tv.tv_usec);
        !           205:         for (i = 0; words[i]; i++) {
        !           206:             fprintf(qtest_log_fp, " %s", words[i]);
        !           207:         }
        !           208:         fprintf(qtest_log_fp, "\n");
        !           209:     }
        !           210: 
        !           211:     g_assert(command);
        !           212:     if (strcmp(words[0], "irq_intercept_out") == 0
        !           213:         || strcmp(words[0], "irq_intercept_in") == 0) {
        !           214:        DeviceState *dev;
        !           215: 
        !           216:         g_assert(words[1]);
        !           217:         dev = DEVICE(object_resolve_path(words[1], NULL));
        !           218:         if (!dev) {
        !           219:             qtest_send_prefix(chr);
        !           220:             qtest_send(chr, "FAIL Unknown device\n");
        !           221:            return;
        !           222:         }
        !           223: 
        !           224:         if (irq_intercept_dev) {
        !           225:             qtest_send_prefix(chr);
        !           226:             if (irq_intercept_dev != dev) {
        !           227:                 qtest_send(chr, "FAIL IRQ intercept already enabled\n");
        !           228:             } else {
        !           229:                 qtest_send(chr, "OK\n");
        !           230:             }
        !           231:            return;
        !           232:         }
        !           233: 
        !           234:         if (words[0][14] == 'o') {
        !           235:             qemu_irq_intercept_out(&dev->gpio_out, qtest_irq_handler, dev->num_gpio_out);
        !           236:         } else {
        !           237:             qemu_irq_intercept_in(dev->gpio_in, qtest_irq_handler, dev->num_gpio_in);
        !           238:         }
        !           239:         irq_intercept_dev = dev;
        !           240:         qtest_send_prefix(chr);
        !           241:         qtest_send(chr, "OK\n");
        !           242: 
        !           243:     } else if (strcmp(words[0], "outb") == 0 ||
        !           244:                strcmp(words[0], "outw") == 0 ||
        !           245:                strcmp(words[0], "outl") == 0) {
        !           246:         uint16_t addr;
        !           247:         uint32_t value;
        !           248: 
        !           249:         g_assert(words[1] && words[2]);
        !           250:         addr = strtol(words[1], NULL, 0);
        !           251:         value = strtol(words[2], NULL, 0);
        !           252: 
        !           253:         if (words[0][3] == 'b') {
        !           254:             cpu_outb(addr, value);
        !           255:         } else if (words[0][3] == 'w') {
        !           256:             cpu_outw(addr, value);
        !           257:         } else if (words[0][3] == 'l') {
        !           258:             cpu_outl(addr, value);
        !           259:         }
        !           260:         qtest_send_prefix(chr);
        !           261:         qtest_send(chr, "OK\n");
        !           262:     } else if (strcmp(words[0], "inb") == 0 ||
        !           263:         strcmp(words[0], "inw") == 0 ||
        !           264:         strcmp(words[0], "inl") == 0) {
        !           265:         uint16_t addr;
        !           266:         uint32_t value = -1U;
        !           267: 
        !           268:         g_assert(words[1]);
        !           269:         addr = strtol(words[1], NULL, 0);
        !           270: 
        !           271:         if (words[0][2] == 'b') {
        !           272:             value = cpu_inb(addr);
        !           273:         } else if (words[0][2] == 'w') {
        !           274:             value = cpu_inw(addr);
        !           275:         } else if (words[0][2] == 'l') {
        !           276:             value = cpu_inl(addr);
        !           277:         }
        !           278:         qtest_send_prefix(chr);
        !           279:         qtest_send(chr, "OK 0x%04x\n", value);
        !           280:     } else if (strcmp(words[0], "read") == 0) {
        !           281:         uint64_t addr, len, i;
        !           282:         uint8_t *data;
        !           283: 
        !           284:         g_assert(words[1] && words[2]);
        !           285:         addr = strtoul(words[1], NULL, 0);
        !           286:         len = strtoul(words[2], NULL, 0);
        !           287: 
        !           288:         data = g_malloc(len);
        !           289:         cpu_physical_memory_read(addr, data, len);
        !           290: 
        !           291:         qtest_send_prefix(chr);
        !           292:         qtest_send(chr, "OK 0x");
        !           293:         for (i = 0; i < len; i++) {
        !           294:             qtest_send(chr, "%02x", data[i]);
        !           295:         }
        !           296:         qtest_send(chr, "\n");
        !           297: 
        !           298:         g_free(data);
        !           299:     } else if (strcmp(words[0], "write") == 0) {
        !           300:         uint64_t addr, len, i;
        !           301:         uint8_t *data;
        !           302:         size_t data_len;
        !           303: 
        !           304:         g_assert(words[1] && words[2] && words[3]);
        !           305:         addr = strtoul(words[1], NULL, 0);
        !           306:         len = strtoul(words[2], NULL, 0);
        !           307: 
        !           308:         data_len = strlen(words[3]);
        !           309:         if (data_len < 3) {
        !           310:             qtest_send(chr, "ERR invalid argument size\n");
        !           311:             return;
        !           312:         }
        !           313: 
        !           314:         data = g_malloc(len);
        !           315:         for (i = 0; i < len; i++) {
        !           316:             if ((i * 2 + 4) <= data_len) {
        !           317:                 data[i] = hex2nib(words[3][i * 2 + 2]) << 4;
        !           318:                 data[i] |= hex2nib(words[3][i * 2 + 3]);
        !           319:             } else {
        !           320:                 data[i] = 0;
        !           321:             }
        !           322:         }
        !           323:         cpu_physical_memory_write(addr, data, len);
        !           324:         g_free(data);
        !           325: 
        !           326:         qtest_send_prefix(chr);
        !           327:         qtest_send(chr, "OK\n");
        !           328:     } else if (strcmp(words[0], "clock_step") == 0) {
        !           329:         int64_t ns;
        !           330: 
        !           331:         if (words[1]) {
        !           332:             ns = strtoll(words[1], NULL, 0);
        !           333:         } else {
        !           334:             ns = qemu_clock_deadline(vm_clock);
        !           335:         }
        !           336:         qtest_clock_warp(qemu_get_clock_ns(vm_clock) + ns);
        !           337:         qtest_send_prefix(chr);
        !           338:         qtest_send(chr, "OK %"PRIi64"\n", (int64_t)qemu_get_clock_ns(vm_clock));
        !           339:     } else if (strcmp(words[0], "clock_set") == 0) {
        !           340:         int64_t ns;
        !           341: 
        !           342:         g_assert(words[1]);
        !           343:         ns = strtoll(words[1], NULL, 0);
        !           344:         qtest_clock_warp(ns);
        !           345:         qtest_send_prefix(chr);
        !           346:         qtest_send(chr, "OK %"PRIi64"\n", (int64_t)qemu_get_clock_ns(vm_clock));
        !           347:     } else {
        !           348:         qtest_send_prefix(chr);
        !           349:         qtest_send(chr, "FAIL Unknown command `%s'\n", words[0]);
        !           350:     }
        !           351: }
        !           352: 
        !           353: static void qtest_process_inbuf(CharDriverState *chr, GString *inbuf)
        !           354: {
        !           355:     char *end;
        !           356: 
        !           357:     while ((end = strchr(inbuf->str, '\n')) != NULL) {
        !           358:         size_t offset;
        !           359:         GString *cmd;
        !           360:         gchar **words;
        !           361: 
        !           362:         offset = end - inbuf->str;
        !           363: 
        !           364:         cmd = g_string_new_len(inbuf->str, offset);
        !           365:         g_string_erase(inbuf, 0, offset + 1);
        !           366: 
        !           367:         words = g_strsplit(cmd->str, " ", 0);
        !           368:         qtest_process_command(chr, words);
        !           369:         g_strfreev(words);
        !           370: 
        !           371:         g_string_free(cmd, TRUE);
        !           372:     }
        !           373: }
        !           374: 
        !           375: static void qtest_read(void *opaque, const uint8_t *buf, int size)
        !           376: {
        !           377:     CharDriverState *chr = opaque;
        !           378: 
        !           379:     g_string_append_len(inbuf, (const gchar *)buf, size);
        !           380:     qtest_process_inbuf(chr, inbuf);
        !           381: }
        !           382: 
        !           383: static int qtest_can_read(void *opaque)
        !           384: {
        !           385:     return 1024;
        !           386: }
        !           387: 
        !           388: static void qtest_event(void *opaque, int event)
        !           389: {
        !           390:     int i;
        !           391: 
        !           392:     switch (event) {
        !           393:     case CHR_EVENT_OPENED:
        !           394:         qemu_system_reset(false);
        !           395:         for (i = 0; i < ARRAY_SIZE(irq_levels); i++) {
        !           396:             irq_levels[i] = 0;
        !           397:         }
        !           398:         qemu_gettimeofday(&start_time);
        !           399:         qtest_opened = true;
        !           400:         if (qtest_log_fp) {
        !           401:             fprintf(qtest_log_fp, "[I " FMT_timeval "] OPENED\n",
        !           402:                     start_time.tv_sec, (long) start_time.tv_usec);
        !           403:         }
        !           404:         break;
        !           405:     case CHR_EVENT_CLOSED:
        !           406:         qtest_opened = false;
        !           407:         if (qtest_log_fp) {
        !           408:             qemu_timeval tv;
        !           409:             qtest_get_time(&tv);
        !           410:             fprintf(qtest_log_fp, "[I +" FMT_timeval "] CLOSED\n",
        !           411:                     tv.tv_sec, (long) tv.tv_usec);
        !           412:         }
        !           413:         break;
        !           414:     default:
        !           415:         break;
        !           416:     }
        !           417: }
        !           418: 
        !           419: int qtest_init(void)
        !           420: {
        !           421:     CharDriverState *chr;
        !           422: 
        !           423:     g_assert(qtest_chrdev != NULL);
        !           424: 
        !           425:     configure_icount("0");
        !           426:     chr = qemu_chr_new("qtest", qtest_chrdev, NULL);
        !           427: 
        !           428:     qemu_chr_add_handlers(chr, qtest_can_read, qtest_read, qtest_event, chr);
        !           429:     qemu_chr_fe_set_echo(chr, true);
        !           430: 
        !           431:     inbuf = g_string_new("");
        !           432: 
        !           433:     if (qtest_log) {
        !           434:         if (strcmp(qtest_log, "none") != 0) {
        !           435:             qtest_log_fp = fopen(qtest_log, "w+");
        !           436:         }
        !           437:     } else {
        !           438:         qtest_log_fp = stderr;
        !           439:     }
        !           440: 
        !           441:     qtest_chr = chr;
        !           442: 
        !           443:     return 0;
        !           444: }

unix.superglobalmegacorp.com