18d7f2e76SPhilippe Mathieu-Daudé /* 28d7f2e76SPhilippe Mathieu-Daudé * Test Server 38d7f2e76SPhilippe Mathieu-Daudé * 48d7f2e76SPhilippe Mathieu-Daudé * Copyright IBM, Corp. 2011 58d7f2e76SPhilippe Mathieu-Daudé * 68d7f2e76SPhilippe Mathieu-Daudé * Authors: 78d7f2e76SPhilippe Mathieu-Daudé * Anthony Liguori <aliguori@us.ibm.com> 88d7f2e76SPhilippe Mathieu-Daudé * 98d7f2e76SPhilippe Mathieu-Daudé * This work is licensed under the terms of the GNU GPL, version 2 or later. 108d7f2e76SPhilippe Mathieu-Daudé * See the COPYING file in the top-level directory. 118d7f2e76SPhilippe Mathieu-Daudé * 128d7f2e76SPhilippe Mathieu-Daudé */ 138d7f2e76SPhilippe Mathieu-Daudé 148d7f2e76SPhilippe Mathieu-Daudé #include "qemu/osdep.h" 158d7f2e76SPhilippe Mathieu-Daudé #include "qapi/error.h" 168d7f2e76SPhilippe Mathieu-Daudé #include "sysemu/qtest.h" 178d7f2e76SPhilippe Mathieu-Daudé #include "sysemu/runstate.h" 188d7f2e76SPhilippe Mathieu-Daudé #include "chardev/char-fe.h" 198d7f2e76SPhilippe Mathieu-Daudé #include "exec/ioport.h" 208d7f2e76SPhilippe Mathieu-Daudé #include "exec/memory.h" 218d7f2e76SPhilippe Mathieu-Daudé #include "exec/tswap.h" 228d7f2e76SPhilippe Mathieu-Daudé #include "hw/qdev-core.h" 238d7f2e76SPhilippe Mathieu-Daudé #include "hw/irq.h" 2465eac5bdSPhilippe Mathieu-Daudé #include "hw/core/cpu.h" 258d7f2e76SPhilippe Mathieu-Daudé #include "qemu/accel.h" 268d7f2e76SPhilippe Mathieu-Daudé #include "sysemu/cpu-timers.h" 278d7f2e76SPhilippe Mathieu-Daudé #include "qemu/config-file.h" 288d7f2e76SPhilippe Mathieu-Daudé #include "qemu/option.h" 298d7f2e76SPhilippe Mathieu-Daudé #include "qemu/error-report.h" 308d7f2e76SPhilippe Mathieu-Daudé #include "qemu/module.h" 318d7f2e76SPhilippe Mathieu-Daudé #include "qemu/cutils.h" 328d7f2e76SPhilippe Mathieu-Daudé #include "qom/object_interfaces.h" 338d7f2e76SPhilippe Mathieu-Daudé 348d7f2e76SPhilippe Mathieu-Daudé #define MAX_IRQ 256 358d7f2e76SPhilippe Mathieu-Daudé 368d7f2e76SPhilippe Mathieu-Daudé #define TYPE_QTEST "qtest" 378d7f2e76SPhilippe Mathieu-Daudé 388d7f2e76SPhilippe Mathieu-Daudé OBJECT_DECLARE_SIMPLE_TYPE(QTest, QTEST) 398d7f2e76SPhilippe Mathieu-Daudé 408d7f2e76SPhilippe Mathieu-Daudé struct QTest { 418d7f2e76SPhilippe Mathieu-Daudé Object parent; 428d7f2e76SPhilippe Mathieu-Daudé 438d7f2e76SPhilippe Mathieu-Daudé bool has_machine_link; 448d7f2e76SPhilippe Mathieu-Daudé char *chr_name; 458d7f2e76SPhilippe Mathieu-Daudé Chardev *chr; 468d7f2e76SPhilippe Mathieu-Daudé CharBackend qtest_chr; 478d7f2e76SPhilippe Mathieu-Daudé char *log; 488d7f2e76SPhilippe Mathieu-Daudé }; 498d7f2e76SPhilippe Mathieu-Daudé 508d7f2e76SPhilippe Mathieu-Daudé bool qtest_allowed; 518d7f2e76SPhilippe Mathieu-Daudé 528d7f2e76SPhilippe Mathieu-Daudé static DeviceState *irq_intercept_dev; 538d7f2e76SPhilippe Mathieu-Daudé static FILE *qtest_log_fp; 548d7f2e76SPhilippe Mathieu-Daudé static QTest *qtest; 558d7f2e76SPhilippe Mathieu-Daudé static GString *inbuf; 568d7f2e76SPhilippe Mathieu-Daudé static int irq_levels[MAX_IRQ]; 578d7f2e76SPhilippe Mathieu-Daudé static GTimer *timer; 588d7f2e76SPhilippe Mathieu-Daudé static bool qtest_opened; 598d7f2e76SPhilippe Mathieu-Daudé static void (*qtest_server_send)(void*, const char*); 608d7f2e76SPhilippe Mathieu-Daudé static void *qtest_server_send_opaque; 618d7f2e76SPhilippe Mathieu-Daudé 628d7f2e76SPhilippe Mathieu-Daudé #define FMT_timeval "%.06f" 638d7f2e76SPhilippe Mathieu-Daudé 648d7f2e76SPhilippe Mathieu-Daudé /** 658d7f2e76SPhilippe Mathieu-Daudé * DOC: QTest Protocol 668d7f2e76SPhilippe Mathieu-Daudé * 678d7f2e76SPhilippe Mathieu-Daudé * Line based protocol, request/response based. Server can send async messages 688d7f2e76SPhilippe Mathieu-Daudé * so clients should always handle many async messages before the response 698d7f2e76SPhilippe Mathieu-Daudé * comes in. 708d7f2e76SPhilippe Mathieu-Daudé * 718d7f2e76SPhilippe Mathieu-Daudé * Valid requests 728d7f2e76SPhilippe Mathieu-Daudé * ^^^^^^^^^^^^^^ 738d7f2e76SPhilippe Mathieu-Daudé * 748d7f2e76SPhilippe Mathieu-Daudé * Clock management: 758d7f2e76SPhilippe Mathieu-Daudé * """"""""""""""""" 768d7f2e76SPhilippe Mathieu-Daudé * 778d7f2e76SPhilippe Mathieu-Daudé * The qtest client is completely in charge of the QEMU_CLOCK_VIRTUAL. qtest commands 788d7f2e76SPhilippe Mathieu-Daudé * let you adjust the value of the clock (monotonically). All the commands 798d7f2e76SPhilippe Mathieu-Daudé * return the current value of the clock in nanoseconds. 808d7f2e76SPhilippe Mathieu-Daudé * 818d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 828d7f2e76SPhilippe Mathieu-Daudé * 838d7f2e76SPhilippe Mathieu-Daudé * > clock_step 848d7f2e76SPhilippe Mathieu-Daudé * < OK VALUE 858d7f2e76SPhilippe Mathieu-Daudé * 868d7f2e76SPhilippe Mathieu-Daudé * Advance the clock to the next deadline. Useful when waiting for 878d7f2e76SPhilippe Mathieu-Daudé * asynchronous events. 888d7f2e76SPhilippe Mathieu-Daudé * 898d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 908d7f2e76SPhilippe Mathieu-Daudé * 918d7f2e76SPhilippe Mathieu-Daudé * > clock_step NS 928d7f2e76SPhilippe Mathieu-Daudé * < OK VALUE 938d7f2e76SPhilippe Mathieu-Daudé * 948d7f2e76SPhilippe Mathieu-Daudé * Advance the clock by NS nanoseconds. 958d7f2e76SPhilippe Mathieu-Daudé * 968d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 978d7f2e76SPhilippe Mathieu-Daudé * 988d7f2e76SPhilippe Mathieu-Daudé * > clock_set NS 998d7f2e76SPhilippe Mathieu-Daudé * < OK VALUE 1008d7f2e76SPhilippe Mathieu-Daudé * 1018d7f2e76SPhilippe Mathieu-Daudé * Advance the clock to NS nanoseconds (do nothing if it's already past). 1028d7f2e76SPhilippe Mathieu-Daudé * 1038d7f2e76SPhilippe Mathieu-Daudé * PIO and memory access: 1048d7f2e76SPhilippe Mathieu-Daudé * """""""""""""""""""""" 1058d7f2e76SPhilippe Mathieu-Daudé * 1068d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 1078d7f2e76SPhilippe Mathieu-Daudé * 1088d7f2e76SPhilippe Mathieu-Daudé * > outb ADDR VALUE 1098d7f2e76SPhilippe Mathieu-Daudé * < OK 1108d7f2e76SPhilippe Mathieu-Daudé * 1118d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 1128d7f2e76SPhilippe Mathieu-Daudé * 1138d7f2e76SPhilippe Mathieu-Daudé * > outw ADDR VALUE 1148d7f2e76SPhilippe Mathieu-Daudé * < OK 1158d7f2e76SPhilippe Mathieu-Daudé * 1168d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 1178d7f2e76SPhilippe Mathieu-Daudé * 1188d7f2e76SPhilippe Mathieu-Daudé * > outl ADDR VALUE 1198d7f2e76SPhilippe Mathieu-Daudé * < OK 1208d7f2e76SPhilippe Mathieu-Daudé * 1218d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 1228d7f2e76SPhilippe Mathieu-Daudé * 1238d7f2e76SPhilippe Mathieu-Daudé * > inb ADDR 1248d7f2e76SPhilippe Mathieu-Daudé * < OK VALUE 1258d7f2e76SPhilippe Mathieu-Daudé * 1268d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 1278d7f2e76SPhilippe Mathieu-Daudé * 1288d7f2e76SPhilippe Mathieu-Daudé * > inw ADDR 1298d7f2e76SPhilippe Mathieu-Daudé * < OK VALUE 1308d7f2e76SPhilippe Mathieu-Daudé * 1318d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 1328d7f2e76SPhilippe Mathieu-Daudé * 1338d7f2e76SPhilippe Mathieu-Daudé * > inl ADDR 1348d7f2e76SPhilippe Mathieu-Daudé * < OK VALUE 1358d7f2e76SPhilippe Mathieu-Daudé * 1368d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 1378d7f2e76SPhilippe Mathieu-Daudé * 1388d7f2e76SPhilippe Mathieu-Daudé * > writeb ADDR VALUE 1398d7f2e76SPhilippe Mathieu-Daudé * < OK 1408d7f2e76SPhilippe Mathieu-Daudé * 1418d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 1428d7f2e76SPhilippe Mathieu-Daudé * 1438d7f2e76SPhilippe Mathieu-Daudé * > writew ADDR VALUE 1448d7f2e76SPhilippe Mathieu-Daudé * < OK 1458d7f2e76SPhilippe Mathieu-Daudé * 1468d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 1478d7f2e76SPhilippe Mathieu-Daudé * 1488d7f2e76SPhilippe Mathieu-Daudé * > writel ADDR VALUE 1498d7f2e76SPhilippe Mathieu-Daudé * < OK 1508d7f2e76SPhilippe Mathieu-Daudé * 1518d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 1528d7f2e76SPhilippe Mathieu-Daudé * 1538d7f2e76SPhilippe Mathieu-Daudé * > writeq ADDR VALUE 1548d7f2e76SPhilippe Mathieu-Daudé * < OK 1558d7f2e76SPhilippe Mathieu-Daudé * 1568d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 1578d7f2e76SPhilippe Mathieu-Daudé * 1588d7f2e76SPhilippe Mathieu-Daudé * > readb ADDR 1598d7f2e76SPhilippe Mathieu-Daudé * < OK VALUE 1608d7f2e76SPhilippe Mathieu-Daudé * 1618d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 1628d7f2e76SPhilippe Mathieu-Daudé * 1638d7f2e76SPhilippe Mathieu-Daudé * > readw ADDR 1648d7f2e76SPhilippe Mathieu-Daudé * < OK VALUE 1658d7f2e76SPhilippe Mathieu-Daudé * 1668d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 1678d7f2e76SPhilippe Mathieu-Daudé * 1688d7f2e76SPhilippe Mathieu-Daudé * > readl ADDR 1698d7f2e76SPhilippe Mathieu-Daudé * < OK VALUE 1708d7f2e76SPhilippe Mathieu-Daudé * 1718d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 1728d7f2e76SPhilippe Mathieu-Daudé * 1738d7f2e76SPhilippe Mathieu-Daudé * > readq ADDR 1748d7f2e76SPhilippe Mathieu-Daudé * < OK VALUE 1758d7f2e76SPhilippe Mathieu-Daudé * 1768d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 1778d7f2e76SPhilippe Mathieu-Daudé * 1788d7f2e76SPhilippe Mathieu-Daudé * > read ADDR SIZE 1798d7f2e76SPhilippe Mathieu-Daudé * < OK DATA 1808d7f2e76SPhilippe Mathieu-Daudé * 1818d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 1828d7f2e76SPhilippe Mathieu-Daudé * 1838d7f2e76SPhilippe Mathieu-Daudé * > write ADDR SIZE DATA 1848d7f2e76SPhilippe Mathieu-Daudé * < OK 1858d7f2e76SPhilippe Mathieu-Daudé * 1868d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 1878d7f2e76SPhilippe Mathieu-Daudé * 1888d7f2e76SPhilippe Mathieu-Daudé * > b64read ADDR SIZE 1898d7f2e76SPhilippe Mathieu-Daudé * < OK B64_DATA 1908d7f2e76SPhilippe Mathieu-Daudé * 1918d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 1928d7f2e76SPhilippe Mathieu-Daudé * 1938d7f2e76SPhilippe Mathieu-Daudé * > b64write ADDR SIZE B64_DATA 1948d7f2e76SPhilippe Mathieu-Daudé * < OK 1958d7f2e76SPhilippe Mathieu-Daudé * 1968d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 1978d7f2e76SPhilippe Mathieu-Daudé * 1988d7f2e76SPhilippe Mathieu-Daudé * > memset ADDR SIZE VALUE 1998d7f2e76SPhilippe Mathieu-Daudé * < OK 2008d7f2e76SPhilippe Mathieu-Daudé * 2018d7f2e76SPhilippe Mathieu-Daudé * ADDR, SIZE, VALUE are all integers parsed with strtoul() with a base of 0. 2028d7f2e76SPhilippe Mathieu-Daudé * For 'memset' a zero size is permitted and does nothing. 2038d7f2e76SPhilippe Mathieu-Daudé * 2048d7f2e76SPhilippe Mathieu-Daudé * DATA is an arbitrarily long hex number prefixed with '0x'. If it's smaller 2058d7f2e76SPhilippe Mathieu-Daudé * than the expected size, the value will be zero filled at the end of the data 2068d7f2e76SPhilippe Mathieu-Daudé * sequence. 2078d7f2e76SPhilippe Mathieu-Daudé * 2088d7f2e76SPhilippe Mathieu-Daudé * B64_DATA is an arbitrarily long base64 encoded string. 2098d7f2e76SPhilippe Mathieu-Daudé * If the sizes do not match, the data will be truncated. 2108d7f2e76SPhilippe Mathieu-Daudé * 2118d7f2e76SPhilippe Mathieu-Daudé * IRQ management: 2128d7f2e76SPhilippe Mathieu-Daudé * """"""""""""""" 2138d7f2e76SPhilippe Mathieu-Daudé * 2148d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 2158d7f2e76SPhilippe Mathieu-Daudé * 2168d7f2e76SPhilippe Mathieu-Daudé * > irq_intercept_in QOM-PATH 2178d7f2e76SPhilippe Mathieu-Daudé * < OK 2188d7f2e76SPhilippe Mathieu-Daudé * 2198d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 2208d7f2e76SPhilippe Mathieu-Daudé * 2218d7f2e76SPhilippe Mathieu-Daudé * > irq_intercept_out QOM-PATH 2228d7f2e76SPhilippe Mathieu-Daudé * < OK 2238d7f2e76SPhilippe Mathieu-Daudé * 2248d7f2e76SPhilippe Mathieu-Daudé * Attach to the gpio-in (resp. gpio-out) pins exported by the device at 2258d7f2e76SPhilippe Mathieu-Daudé * QOM-PATH. When the pin is triggered, one of the following async messages 2268d7f2e76SPhilippe Mathieu-Daudé * will be printed to the qtest stream:: 2278d7f2e76SPhilippe Mathieu-Daudé * 2288d7f2e76SPhilippe Mathieu-Daudé * IRQ raise NUM 2298d7f2e76SPhilippe Mathieu-Daudé * IRQ lower NUM 2308d7f2e76SPhilippe Mathieu-Daudé * 2318d7f2e76SPhilippe Mathieu-Daudé * where NUM is an IRQ number. For the PC, interrupts can be intercepted 2328d7f2e76SPhilippe Mathieu-Daudé * simply with "irq_intercept_in ioapic" (note that IRQ0 comes out with 2338d7f2e76SPhilippe Mathieu-Daudé * NUM=0 even though it is remapped to GSI 2). 2348d7f2e76SPhilippe Mathieu-Daudé * 2358d7f2e76SPhilippe Mathieu-Daudé * Setting interrupt level: 2368d7f2e76SPhilippe Mathieu-Daudé * """""""""""""""""""""""" 2378d7f2e76SPhilippe Mathieu-Daudé * 2388d7f2e76SPhilippe Mathieu-Daudé * .. code-block:: none 2398d7f2e76SPhilippe Mathieu-Daudé * 2408d7f2e76SPhilippe Mathieu-Daudé * > set_irq_in QOM-PATH NAME NUM LEVEL 2418d7f2e76SPhilippe Mathieu-Daudé * < OK 2428d7f2e76SPhilippe Mathieu-Daudé * 2438d7f2e76SPhilippe Mathieu-Daudé * where NAME is the name of the irq/gpio list, NUM is an IRQ number and 2448d7f2e76SPhilippe Mathieu-Daudé * LEVEL is an signed integer IRQ level. 2458d7f2e76SPhilippe Mathieu-Daudé * 2468d7f2e76SPhilippe Mathieu-Daudé * Forcibly set the given interrupt pin to the given level. 2478d7f2e76SPhilippe Mathieu-Daudé * 2488d7f2e76SPhilippe Mathieu-Daudé */ 2498d7f2e76SPhilippe Mathieu-Daudé 2508d7f2e76SPhilippe Mathieu-Daudé static int hex2nib(char ch) 2518d7f2e76SPhilippe Mathieu-Daudé { 2528d7f2e76SPhilippe Mathieu-Daudé if (ch >= '0' && ch <= '9') { 2538d7f2e76SPhilippe Mathieu-Daudé return ch - '0'; 2548d7f2e76SPhilippe Mathieu-Daudé } else if (ch >= 'a' && ch <= 'f') { 2558d7f2e76SPhilippe Mathieu-Daudé return 10 + (ch - 'a'); 2568d7f2e76SPhilippe Mathieu-Daudé } else if (ch >= 'A' && ch <= 'F') { 2578d7f2e76SPhilippe Mathieu-Daudé return 10 + (ch - 'A'); 2588d7f2e76SPhilippe Mathieu-Daudé } else { 2598d7f2e76SPhilippe Mathieu-Daudé return -1; 2608d7f2e76SPhilippe Mathieu-Daudé } 2618d7f2e76SPhilippe Mathieu-Daudé } 2628d7f2e76SPhilippe Mathieu-Daudé 2638d7f2e76SPhilippe Mathieu-Daudé void qtest_send_prefix(CharBackend *chr) 2648d7f2e76SPhilippe Mathieu-Daudé { 2658d7f2e76SPhilippe Mathieu-Daudé if (!qtest_log_fp || !qtest_opened) { 2668d7f2e76SPhilippe Mathieu-Daudé return; 2678d7f2e76SPhilippe Mathieu-Daudé } 2688d7f2e76SPhilippe Mathieu-Daudé 2698d7f2e76SPhilippe Mathieu-Daudé fprintf(qtest_log_fp, "[S +" FMT_timeval "] ", g_timer_elapsed(timer, NULL)); 2708d7f2e76SPhilippe Mathieu-Daudé } 2718d7f2e76SPhilippe Mathieu-Daudé 2728d7f2e76SPhilippe Mathieu-Daudé static void G_GNUC_PRINTF(1, 2) qtest_log_send(const char *fmt, ...) 2738d7f2e76SPhilippe Mathieu-Daudé { 2748d7f2e76SPhilippe Mathieu-Daudé va_list ap; 2758d7f2e76SPhilippe Mathieu-Daudé 2768d7f2e76SPhilippe Mathieu-Daudé if (!qtest_log_fp || !qtest_opened) { 2778d7f2e76SPhilippe Mathieu-Daudé return; 2788d7f2e76SPhilippe Mathieu-Daudé } 2798d7f2e76SPhilippe Mathieu-Daudé 2808d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(NULL); 2818d7f2e76SPhilippe Mathieu-Daudé 2828d7f2e76SPhilippe Mathieu-Daudé va_start(ap, fmt); 2838d7f2e76SPhilippe Mathieu-Daudé vfprintf(qtest_log_fp, fmt, ap); 2848d7f2e76SPhilippe Mathieu-Daudé va_end(ap); 2858d7f2e76SPhilippe Mathieu-Daudé } 2868d7f2e76SPhilippe Mathieu-Daudé 2878d7f2e76SPhilippe Mathieu-Daudé static void qtest_server_char_be_send(void *opaque, const char *str) 2888d7f2e76SPhilippe Mathieu-Daudé { 2898d7f2e76SPhilippe Mathieu-Daudé size_t len = strlen(str); 2908d7f2e76SPhilippe Mathieu-Daudé CharBackend* chr = (CharBackend *)opaque; 2918d7f2e76SPhilippe Mathieu-Daudé qemu_chr_fe_write_all(chr, (uint8_t *)str, len); 2928d7f2e76SPhilippe Mathieu-Daudé if (qtest_log_fp && qtest_opened) { 2938d7f2e76SPhilippe Mathieu-Daudé fprintf(qtest_log_fp, "%s", str); 2948d7f2e76SPhilippe Mathieu-Daudé } 2958d7f2e76SPhilippe Mathieu-Daudé } 2968d7f2e76SPhilippe Mathieu-Daudé 2978d7f2e76SPhilippe Mathieu-Daudé static void qtest_send(CharBackend *chr, const char *str) 2988d7f2e76SPhilippe Mathieu-Daudé { 2998d7f2e76SPhilippe Mathieu-Daudé qtest_server_send(qtest_server_send_opaque, str); 3008d7f2e76SPhilippe Mathieu-Daudé } 3018d7f2e76SPhilippe Mathieu-Daudé 3028d7f2e76SPhilippe Mathieu-Daudé void qtest_sendf(CharBackend *chr, const char *fmt, ...) 3038d7f2e76SPhilippe Mathieu-Daudé { 3048d7f2e76SPhilippe Mathieu-Daudé va_list ap; 3058d7f2e76SPhilippe Mathieu-Daudé gchar *buffer; 3068d7f2e76SPhilippe Mathieu-Daudé 3078d7f2e76SPhilippe Mathieu-Daudé va_start(ap, fmt); 3088d7f2e76SPhilippe Mathieu-Daudé buffer = g_strdup_vprintf(fmt, ap); 3098d7f2e76SPhilippe Mathieu-Daudé qtest_send(chr, buffer); 3108d7f2e76SPhilippe Mathieu-Daudé g_free(buffer); 3118d7f2e76SPhilippe Mathieu-Daudé va_end(ap); 3128d7f2e76SPhilippe Mathieu-Daudé } 3138d7f2e76SPhilippe Mathieu-Daudé 3148d7f2e76SPhilippe Mathieu-Daudé static void qtest_irq_handler(void *opaque, int n, int level) 3158d7f2e76SPhilippe Mathieu-Daudé { 3168d7f2e76SPhilippe Mathieu-Daudé qemu_irq old_irq = *(qemu_irq *)opaque; 3178d7f2e76SPhilippe Mathieu-Daudé qemu_set_irq(old_irq, level); 3188d7f2e76SPhilippe Mathieu-Daudé 3198d7f2e76SPhilippe Mathieu-Daudé if (irq_levels[n] != level) { 3208d7f2e76SPhilippe Mathieu-Daudé CharBackend *chr = &qtest->qtest_chr; 3218d7f2e76SPhilippe Mathieu-Daudé irq_levels[n] = level; 3228d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(chr); 3238d7f2e76SPhilippe Mathieu-Daudé qtest_sendf(chr, "IRQ %s %d\n", 3248d7f2e76SPhilippe Mathieu-Daudé level ? "raise" : "lower", n); 3258d7f2e76SPhilippe Mathieu-Daudé } 3268d7f2e76SPhilippe Mathieu-Daudé } 3278d7f2e76SPhilippe Mathieu-Daudé 3288d7f2e76SPhilippe Mathieu-Daudé static bool (*process_command_cb)(CharBackend *chr, gchar **words); 3298d7f2e76SPhilippe Mathieu-Daudé 3308d7f2e76SPhilippe Mathieu-Daudé void qtest_set_command_cb(bool (*pc_cb)(CharBackend *chr, gchar **words)) 3318d7f2e76SPhilippe Mathieu-Daudé { 3328d7f2e76SPhilippe Mathieu-Daudé assert(!process_command_cb); /* Switch to a list if we need more than one */ 3338d7f2e76SPhilippe Mathieu-Daudé 3348d7f2e76SPhilippe Mathieu-Daudé process_command_cb = pc_cb; 3358d7f2e76SPhilippe Mathieu-Daudé } 3368d7f2e76SPhilippe Mathieu-Daudé 3378d7f2e76SPhilippe Mathieu-Daudé static void qtest_install_gpio_out_intercept(DeviceState *dev, const char *name, int n) 3388d7f2e76SPhilippe Mathieu-Daudé { 3398d7f2e76SPhilippe Mathieu-Daudé qemu_irq *disconnected = g_new0(qemu_irq, 1); 3408d7f2e76SPhilippe Mathieu-Daudé qemu_irq icpt = qemu_allocate_irq(qtest_irq_handler, 3418d7f2e76SPhilippe Mathieu-Daudé disconnected, n); 3428d7f2e76SPhilippe Mathieu-Daudé 3438d7f2e76SPhilippe Mathieu-Daudé *disconnected = qdev_intercept_gpio_out(dev, icpt, name, n); 3448d7f2e76SPhilippe Mathieu-Daudé } 3458d7f2e76SPhilippe Mathieu-Daudé 3468d7f2e76SPhilippe Mathieu-Daudé static void qtest_process_command(CharBackend *chr, gchar **words) 3478d7f2e76SPhilippe Mathieu-Daudé { 3488d7f2e76SPhilippe Mathieu-Daudé const gchar *command; 3498d7f2e76SPhilippe Mathieu-Daudé 3508d7f2e76SPhilippe Mathieu-Daudé g_assert(words); 3518d7f2e76SPhilippe Mathieu-Daudé 3528d7f2e76SPhilippe Mathieu-Daudé command = words[0]; 3538d7f2e76SPhilippe Mathieu-Daudé 3548d7f2e76SPhilippe Mathieu-Daudé if (qtest_log_fp) { 3558d7f2e76SPhilippe Mathieu-Daudé int i; 3568d7f2e76SPhilippe Mathieu-Daudé 3578d7f2e76SPhilippe Mathieu-Daudé fprintf(qtest_log_fp, "[R +" FMT_timeval "]", g_timer_elapsed(timer, NULL)); 3588d7f2e76SPhilippe Mathieu-Daudé for (i = 0; words[i]; i++) { 3598d7f2e76SPhilippe Mathieu-Daudé fprintf(qtest_log_fp, " %s", words[i]); 3608d7f2e76SPhilippe Mathieu-Daudé } 3618d7f2e76SPhilippe Mathieu-Daudé fprintf(qtest_log_fp, "\n"); 3628d7f2e76SPhilippe Mathieu-Daudé } 3638d7f2e76SPhilippe Mathieu-Daudé 3648d7f2e76SPhilippe Mathieu-Daudé g_assert(command); 3658d7f2e76SPhilippe Mathieu-Daudé if (strcmp(words[0], "irq_intercept_out") == 0 3668d7f2e76SPhilippe Mathieu-Daudé || strcmp(words[0], "irq_intercept_in") == 0) { 3678d7f2e76SPhilippe Mathieu-Daudé DeviceState *dev; 3688d7f2e76SPhilippe Mathieu-Daudé NamedGPIOList *ngl; 3698d7f2e76SPhilippe Mathieu-Daudé bool is_named; 3708d7f2e76SPhilippe Mathieu-Daudé bool is_outbound; 3718d7f2e76SPhilippe Mathieu-Daudé bool interception_succeeded = false; 3728d7f2e76SPhilippe Mathieu-Daudé 3738d7f2e76SPhilippe Mathieu-Daudé g_assert(words[1]); 3748d7f2e76SPhilippe Mathieu-Daudé is_named = words[2] != NULL; 3758d7f2e76SPhilippe Mathieu-Daudé is_outbound = words[0][14] == 'o'; 3768d7f2e76SPhilippe Mathieu-Daudé dev = DEVICE(object_resolve_path(words[1], NULL)); 3778d7f2e76SPhilippe Mathieu-Daudé if (!dev) { 3788d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(chr); 3798d7f2e76SPhilippe Mathieu-Daudé qtest_send(chr, "FAIL Unknown device\n"); 3808d7f2e76SPhilippe Mathieu-Daudé return; 3818d7f2e76SPhilippe Mathieu-Daudé } 3828d7f2e76SPhilippe Mathieu-Daudé 3838d7f2e76SPhilippe Mathieu-Daudé if (is_named && !is_outbound) { 3848d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(chr); 3858d7f2e76SPhilippe Mathieu-Daudé qtest_send(chr, "FAIL Interception of named in-GPIOs not yet supported\n"); 3868d7f2e76SPhilippe Mathieu-Daudé return; 3878d7f2e76SPhilippe Mathieu-Daudé } 3888d7f2e76SPhilippe Mathieu-Daudé 3898d7f2e76SPhilippe Mathieu-Daudé if (irq_intercept_dev) { 3908d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(chr); 3918d7f2e76SPhilippe Mathieu-Daudé if (irq_intercept_dev != dev) { 3928d7f2e76SPhilippe Mathieu-Daudé qtest_send(chr, "FAIL IRQ intercept already enabled\n"); 3938d7f2e76SPhilippe Mathieu-Daudé } else { 3948d7f2e76SPhilippe Mathieu-Daudé qtest_send(chr, "OK\n"); 3958d7f2e76SPhilippe Mathieu-Daudé } 3968d7f2e76SPhilippe Mathieu-Daudé return; 3978d7f2e76SPhilippe Mathieu-Daudé } 3988d7f2e76SPhilippe Mathieu-Daudé 3998d7f2e76SPhilippe Mathieu-Daudé QLIST_FOREACH(ngl, &dev->gpios, node) { 4008d7f2e76SPhilippe Mathieu-Daudé /* We don't support inbound interception of named GPIOs yet */ 4018d7f2e76SPhilippe Mathieu-Daudé if (is_outbound) { 4028d7f2e76SPhilippe Mathieu-Daudé /* NULL is valid and matchable, for "unnamed GPIO" */ 4038d7f2e76SPhilippe Mathieu-Daudé if (g_strcmp0(ngl->name, words[2]) == 0) { 4048d7f2e76SPhilippe Mathieu-Daudé int i; 4058d7f2e76SPhilippe Mathieu-Daudé for (i = 0; i < ngl->num_out; ++i) { 4068d7f2e76SPhilippe Mathieu-Daudé qtest_install_gpio_out_intercept(dev, ngl->name, i); 4078d7f2e76SPhilippe Mathieu-Daudé } 4088d7f2e76SPhilippe Mathieu-Daudé interception_succeeded = true; 4098d7f2e76SPhilippe Mathieu-Daudé } 4108d7f2e76SPhilippe Mathieu-Daudé } else { 4118d7f2e76SPhilippe Mathieu-Daudé qemu_irq_intercept_in(ngl->in, qtest_irq_handler, 4128d7f2e76SPhilippe Mathieu-Daudé ngl->num_in); 4138d7f2e76SPhilippe Mathieu-Daudé interception_succeeded = true; 4148d7f2e76SPhilippe Mathieu-Daudé } 4158d7f2e76SPhilippe Mathieu-Daudé } 4168d7f2e76SPhilippe Mathieu-Daudé 4178d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(chr); 4188d7f2e76SPhilippe Mathieu-Daudé if (interception_succeeded) { 4198d7f2e76SPhilippe Mathieu-Daudé irq_intercept_dev = dev; 4208d7f2e76SPhilippe Mathieu-Daudé qtest_send(chr, "OK\n"); 4218d7f2e76SPhilippe Mathieu-Daudé } else { 4228d7f2e76SPhilippe Mathieu-Daudé qtest_send(chr, "FAIL No intercepts installed\n"); 4238d7f2e76SPhilippe Mathieu-Daudé } 4248d7f2e76SPhilippe Mathieu-Daudé } else if (strcmp(words[0], "set_irq_in") == 0) { 4258d7f2e76SPhilippe Mathieu-Daudé DeviceState *dev; 4268d7f2e76SPhilippe Mathieu-Daudé qemu_irq irq; 4278d7f2e76SPhilippe Mathieu-Daudé char *name; 4288d7f2e76SPhilippe Mathieu-Daudé int ret; 4298d7f2e76SPhilippe Mathieu-Daudé int num; 4308d7f2e76SPhilippe Mathieu-Daudé int level; 4318d7f2e76SPhilippe Mathieu-Daudé 4328d7f2e76SPhilippe Mathieu-Daudé g_assert(words[1] && words[2] && words[3] && words[4]); 4338d7f2e76SPhilippe Mathieu-Daudé 4348d7f2e76SPhilippe Mathieu-Daudé dev = DEVICE(object_resolve_path(words[1], NULL)); 4358d7f2e76SPhilippe Mathieu-Daudé if (!dev) { 4368d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(chr); 4378d7f2e76SPhilippe Mathieu-Daudé qtest_send(chr, "FAIL Unknown device\n"); 4388d7f2e76SPhilippe Mathieu-Daudé return; 4398d7f2e76SPhilippe Mathieu-Daudé } 4408d7f2e76SPhilippe Mathieu-Daudé 4418d7f2e76SPhilippe Mathieu-Daudé if (strcmp(words[2], "unnamed-gpio-in") == 0) { 4428d7f2e76SPhilippe Mathieu-Daudé name = NULL; 4438d7f2e76SPhilippe Mathieu-Daudé } else { 4448d7f2e76SPhilippe Mathieu-Daudé name = words[2]; 4458d7f2e76SPhilippe Mathieu-Daudé } 4468d7f2e76SPhilippe Mathieu-Daudé 4478d7f2e76SPhilippe Mathieu-Daudé ret = qemu_strtoi(words[3], NULL, 0, &num); 4488d7f2e76SPhilippe Mathieu-Daudé g_assert(!ret); 4498d7f2e76SPhilippe Mathieu-Daudé ret = qemu_strtoi(words[4], NULL, 0, &level); 4508d7f2e76SPhilippe Mathieu-Daudé g_assert(!ret); 4518d7f2e76SPhilippe Mathieu-Daudé 4528d7f2e76SPhilippe Mathieu-Daudé irq = qdev_get_gpio_in_named(dev, name, num); 4538d7f2e76SPhilippe Mathieu-Daudé 4548d7f2e76SPhilippe Mathieu-Daudé qemu_set_irq(irq, level); 4558d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(chr); 4568d7f2e76SPhilippe Mathieu-Daudé qtest_send(chr, "OK\n"); 4578d7f2e76SPhilippe Mathieu-Daudé } else if (strcmp(words[0], "outb") == 0 || 4588d7f2e76SPhilippe Mathieu-Daudé strcmp(words[0], "outw") == 0 || 4598d7f2e76SPhilippe Mathieu-Daudé strcmp(words[0], "outl") == 0) { 4608d7f2e76SPhilippe Mathieu-Daudé unsigned long addr; 4618d7f2e76SPhilippe Mathieu-Daudé unsigned long value; 4628d7f2e76SPhilippe Mathieu-Daudé int ret; 4638d7f2e76SPhilippe Mathieu-Daudé 4648d7f2e76SPhilippe Mathieu-Daudé g_assert(words[1] && words[2]); 4658d7f2e76SPhilippe Mathieu-Daudé ret = qemu_strtoul(words[1], NULL, 0, &addr); 4668d7f2e76SPhilippe Mathieu-Daudé g_assert(ret == 0); 4678d7f2e76SPhilippe Mathieu-Daudé ret = qemu_strtoul(words[2], NULL, 0, &value); 4688d7f2e76SPhilippe Mathieu-Daudé g_assert(ret == 0); 4698d7f2e76SPhilippe Mathieu-Daudé g_assert(addr <= 0xffff); 4708d7f2e76SPhilippe Mathieu-Daudé 4718d7f2e76SPhilippe Mathieu-Daudé if (words[0][3] == 'b') { 4728d7f2e76SPhilippe Mathieu-Daudé cpu_outb(addr, value); 4738d7f2e76SPhilippe Mathieu-Daudé } else if (words[0][3] == 'w') { 4748d7f2e76SPhilippe Mathieu-Daudé cpu_outw(addr, value); 4758d7f2e76SPhilippe Mathieu-Daudé } else if (words[0][3] == 'l') { 4768d7f2e76SPhilippe Mathieu-Daudé cpu_outl(addr, value); 4778d7f2e76SPhilippe Mathieu-Daudé } 4788d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(chr); 4798d7f2e76SPhilippe Mathieu-Daudé qtest_send(chr, "OK\n"); 4808d7f2e76SPhilippe Mathieu-Daudé } else if (strcmp(words[0], "inb") == 0 || 4818d7f2e76SPhilippe Mathieu-Daudé strcmp(words[0], "inw") == 0 || 4828d7f2e76SPhilippe Mathieu-Daudé strcmp(words[0], "inl") == 0) { 4838d7f2e76SPhilippe Mathieu-Daudé unsigned long addr; 4848d7f2e76SPhilippe Mathieu-Daudé uint32_t value = -1U; 4858d7f2e76SPhilippe Mathieu-Daudé int ret; 4868d7f2e76SPhilippe Mathieu-Daudé 4878d7f2e76SPhilippe Mathieu-Daudé g_assert(words[1]); 4888d7f2e76SPhilippe Mathieu-Daudé ret = qemu_strtoul(words[1], NULL, 0, &addr); 4898d7f2e76SPhilippe Mathieu-Daudé g_assert(ret == 0); 4908d7f2e76SPhilippe Mathieu-Daudé g_assert(addr <= 0xffff); 4918d7f2e76SPhilippe Mathieu-Daudé 4928d7f2e76SPhilippe Mathieu-Daudé if (words[0][2] == 'b') { 4938d7f2e76SPhilippe Mathieu-Daudé value = cpu_inb(addr); 4948d7f2e76SPhilippe Mathieu-Daudé } else if (words[0][2] == 'w') { 4958d7f2e76SPhilippe Mathieu-Daudé value = cpu_inw(addr); 4968d7f2e76SPhilippe Mathieu-Daudé } else if (words[0][2] == 'l') { 4978d7f2e76SPhilippe Mathieu-Daudé value = cpu_inl(addr); 4988d7f2e76SPhilippe Mathieu-Daudé } 4998d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(chr); 5008d7f2e76SPhilippe Mathieu-Daudé qtest_sendf(chr, "OK 0x%04x\n", value); 5018d7f2e76SPhilippe Mathieu-Daudé } else if (strcmp(words[0], "writeb") == 0 || 5028d7f2e76SPhilippe Mathieu-Daudé strcmp(words[0], "writew") == 0 || 5038d7f2e76SPhilippe Mathieu-Daudé strcmp(words[0], "writel") == 0 || 5048d7f2e76SPhilippe Mathieu-Daudé strcmp(words[0], "writeq") == 0) { 5058d7f2e76SPhilippe Mathieu-Daudé uint64_t addr; 5068d7f2e76SPhilippe Mathieu-Daudé uint64_t value; 5078d7f2e76SPhilippe Mathieu-Daudé int ret; 5088d7f2e76SPhilippe Mathieu-Daudé 5098d7f2e76SPhilippe Mathieu-Daudé g_assert(words[1] && words[2]); 5108d7f2e76SPhilippe Mathieu-Daudé ret = qemu_strtou64(words[1], NULL, 0, &addr); 5118d7f2e76SPhilippe Mathieu-Daudé g_assert(ret == 0); 5128d7f2e76SPhilippe Mathieu-Daudé ret = qemu_strtou64(words[2], NULL, 0, &value); 5138d7f2e76SPhilippe Mathieu-Daudé g_assert(ret == 0); 5148d7f2e76SPhilippe Mathieu-Daudé 5158d7f2e76SPhilippe Mathieu-Daudé if (words[0][5] == 'b') { 5168d7f2e76SPhilippe Mathieu-Daudé uint8_t data = value; 5178d7f2e76SPhilippe Mathieu-Daudé address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, 5188d7f2e76SPhilippe Mathieu-Daudé &data, 1); 5198d7f2e76SPhilippe Mathieu-Daudé } else if (words[0][5] == 'w') { 5208d7f2e76SPhilippe Mathieu-Daudé uint16_t data = value; 5218d7f2e76SPhilippe Mathieu-Daudé tswap16s(&data); 5228d7f2e76SPhilippe Mathieu-Daudé address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, 5238d7f2e76SPhilippe Mathieu-Daudé &data, 2); 5248d7f2e76SPhilippe Mathieu-Daudé } else if (words[0][5] == 'l') { 5258d7f2e76SPhilippe Mathieu-Daudé uint32_t data = value; 5268d7f2e76SPhilippe Mathieu-Daudé tswap32s(&data); 5278d7f2e76SPhilippe Mathieu-Daudé address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, 5288d7f2e76SPhilippe Mathieu-Daudé &data, 4); 5298d7f2e76SPhilippe Mathieu-Daudé } else if (words[0][5] == 'q') { 5308d7f2e76SPhilippe Mathieu-Daudé uint64_t data = value; 5318d7f2e76SPhilippe Mathieu-Daudé tswap64s(&data); 5328d7f2e76SPhilippe Mathieu-Daudé address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, 5338d7f2e76SPhilippe Mathieu-Daudé &data, 8); 5348d7f2e76SPhilippe Mathieu-Daudé } 5358d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(chr); 5368d7f2e76SPhilippe Mathieu-Daudé qtest_send(chr, "OK\n"); 5378d7f2e76SPhilippe Mathieu-Daudé } else if (strcmp(words[0], "readb") == 0 || 5388d7f2e76SPhilippe Mathieu-Daudé strcmp(words[0], "readw") == 0 || 5398d7f2e76SPhilippe Mathieu-Daudé strcmp(words[0], "readl") == 0 || 5408d7f2e76SPhilippe Mathieu-Daudé strcmp(words[0], "readq") == 0) { 5418d7f2e76SPhilippe Mathieu-Daudé uint64_t addr; 5428d7f2e76SPhilippe Mathieu-Daudé uint64_t value = UINT64_C(-1); 5438d7f2e76SPhilippe Mathieu-Daudé int ret; 5448d7f2e76SPhilippe Mathieu-Daudé 5458d7f2e76SPhilippe Mathieu-Daudé g_assert(words[1]); 5468d7f2e76SPhilippe Mathieu-Daudé ret = qemu_strtou64(words[1], NULL, 0, &addr); 5478d7f2e76SPhilippe Mathieu-Daudé g_assert(ret == 0); 5488d7f2e76SPhilippe Mathieu-Daudé 5498d7f2e76SPhilippe Mathieu-Daudé if (words[0][4] == 'b') { 5508d7f2e76SPhilippe Mathieu-Daudé uint8_t data; 5518d7f2e76SPhilippe Mathieu-Daudé address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, 5528d7f2e76SPhilippe Mathieu-Daudé &data, 1); 5538d7f2e76SPhilippe Mathieu-Daudé value = data; 5548d7f2e76SPhilippe Mathieu-Daudé } else if (words[0][4] == 'w') { 5558d7f2e76SPhilippe Mathieu-Daudé uint16_t data; 5568d7f2e76SPhilippe Mathieu-Daudé address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, 5578d7f2e76SPhilippe Mathieu-Daudé &data, 2); 5588d7f2e76SPhilippe Mathieu-Daudé value = tswap16(data); 5598d7f2e76SPhilippe Mathieu-Daudé } else if (words[0][4] == 'l') { 5608d7f2e76SPhilippe Mathieu-Daudé uint32_t data; 5618d7f2e76SPhilippe Mathieu-Daudé address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, 5628d7f2e76SPhilippe Mathieu-Daudé &data, 4); 5638d7f2e76SPhilippe Mathieu-Daudé value = tswap32(data); 5648d7f2e76SPhilippe Mathieu-Daudé } else if (words[0][4] == 'q') { 5658d7f2e76SPhilippe Mathieu-Daudé address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, 5668d7f2e76SPhilippe Mathieu-Daudé &value, 8); 5678d7f2e76SPhilippe Mathieu-Daudé tswap64s(&value); 5688d7f2e76SPhilippe Mathieu-Daudé } 5698d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(chr); 5708d7f2e76SPhilippe Mathieu-Daudé qtest_sendf(chr, "OK 0x%016" PRIx64 "\n", value); 5718d7f2e76SPhilippe Mathieu-Daudé } else if (strcmp(words[0], "read") == 0) { 5724b692109SPhilippe Mathieu-Daudé g_autoptr(GString) enc = NULL; 5734b692109SPhilippe Mathieu-Daudé uint64_t addr, len; 5748d7f2e76SPhilippe Mathieu-Daudé uint8_t *data; 5758d7f2e76SPhilippe Mathieu-Daudé int ret; 5768d7f2e76SPhilippe Mathieu-Daudé 5778d7f2e76SPhilippe Mathieu-Daudé g_assert(words[1] && words[2]); 5788d7f2e76SPhilippe Mathieu-Daudé ret = qemu_strtou64(words[1], NULL, 0, &addr); 5798d7f2e76SPhilippe Mathieu-Daudé g_assert(ret == 0); 5808d7f2e76SPhilippe Mathieu-Daudé ret = qemu_strtou64(words[2], NULL, 0, &len); 5818d7f2e76SPhilippe Mathieu-Daudé g_assert(ret == 0); 5828d7f2e76SPhilippe Mathieu-Daudé /* We'd send garbage to libqtest if len is 0 */ 5838d7f2e76SPhilippe Mathieu-Daudé g_assert(len); 5848d7f2e76SPhilippe Mathieu-Daudé 5858d7f2e76SPhilippe Mathieu-Daudé data = g_malloc(len); 5868d7f2e76SPhilippe Mathieu-Daudé address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data, 5878d7f2e76SPhilippe Mathieu-Daudé len); 5888d7f2e76SPhilippe Mathieu-Daudé 5894b692109SPhilippe Mathieu-Daudé enc = qemu_hexdump_line(NULL, data, len, 0, 0); 5908d7f2e76SPhilippe Mathieu-Daudé 5918d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(chr); 5924b692109SPhilippe Mathieu-Daudé qtest_sendf(chr, "OK 0x%s\n", enc->str); 5938d7f2e76SPhilippe Mathieu-Daudé 5948d7f2e76SPhilippe Mathieu-Daudé g_free(data); 5958d7f2e76SPhilippe Mathieu-Daudé } else if (strcmp(words[0], "b64read") == 0) { 5968d7f2e76SPhilippe Mathieu-Daudé uint64_t addr, len; 5978d7f2e76SPhilippe Mathieu-Daudé uint8_t *data; 5988d7f2e76SPhilippe Mathieu-Daudé gchar *b64_data; 5998d7f2e76SPhilippe Mathieu-Daudé int ret; 6008d7f2e76SPhilippe Mathieu-Daudé 6018d7f2e76SPhilippe Mathieu-Daudé g_assert(words[1] && words[2]); 6028d7f2e76SPhilippe Mathieu-Daudé ret = qemu_strtou64(words[1], NULL, 0, &addr); 6038d7f2e76SPhilippe Mathieu-Daudé g_assert(ret == 0); 6048d7f2e76SPhilippe Mathieu-Daudé ret = qemu_strtou64(words[2], NULL, 0, &len); 6058d7f2e76SPhilippe Mathieu-Daudé g_assert(ret == 0); 6068d7f2e76SPhilippe Mathieu-Daudé 6078d7f2e76SPhilippe Mathieu-Daudé data = g_malloc(len); 6088d7f2e76SPhilippe Mathieu-Daudé address_space_read(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data, 6098d7f2e76SPhilippe Mathieu-Daudé len); 6108d7f2e76SPhilippe Mathieu-Daudé b64_data = g_base64_encode(data, len); 6118d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(chr); 6128d7f2e76SPhilippe Mathieu-Daudé qtest_sendf(chr, "OK %s\n", b64_data); 6138d7f2e76SPhilippe Mathieu-Daudé 6148d7f2e76SPhilippe Mathieu-Daudé g_free(data); 6158d7f2e76SPhilippe Mathieu-Daudé g_free(b64_data); 6168d7f2e76SPhilippe Mathieu-Daudé } else if (strcmp(words[0], "write") == 0) { 6178d7f2e76SPhilippe Mathieu-Daudé uint64_t addr, len, i; 6188d7f2e76SPhilippe Mathieu-Daudé uint8_t *data; 6198d7f2e76SPhilippe Mathieu-Daudé size_t data_len; 6208d7f2e76SPhilippe Mathieu-Daudé int ret; 6218d7f2e76SPhilippe Mathieu-Daudé 6228d7f2e76SPhilippe Mathieu-Daudé g_assert(words[1] && words[2] && words[3]); 6238d7f2e76SPhilippe Mathieu-Daudé ret = qemu_strtou64(words[1], NULL, 0, &addr); 6248d7f2e76SPhilippe Mathieu-Daudé g_assert(ret == 0); 6258d7f2e76SPhilippe Mathieu-Daudé ret = qemu_strtou64(words[2], NULL, 0, &len); 6268d7f2e76SPhilippe Mathieu-Daudé g_assert(ret == 0); 6278d7f2e76SPhilippe Mathieu-Daudé 6288d7f2e76SPhilippe Mathieu-Daudé data_len = strlen(words[3]); 6298d7f2e76SPhilippe Mathieu-Daudé if (data_len < 3) { 6308d7f2e76SPhilippe Mathieu-Daudé qtest_send(chr, "ERR invalid argument size\n"); 6318d7f2e76SPhilippe Mathieu-Daudé return; 6328d7f2e76SPhilippe Mathieu-Daudé } 6338d7f2e76SPhilippe Mathieu-Daudé 6348d7f2e76SPhilippe Mathieu-Daudé data = g_malloc(len); 6358d7f2e76SPhilippe Mathieu-Daudé for (i = 0; i < len; i++) { 6368d7f2e76SPhilippe Mathieu-Daudé if ((i * 2 + 4) <= data_len) { 6378d7f2e76SPhilippe Mathieu-Daudé data[i] = hex2nib(words[3][i * 2 + 2]) << 4; 6388d7f2e76SPhilippe Mathieu-Daudé data[i] |= hex2nib(words[3][i * 2 + 3]); 6398d7f2e76SPhilippe Mathieu-Daudé } else { 6408d7f2e76SPhilippe Mathieu-Daudé data[i] = 0; 6418d7f2e76SPhilippe Mathieu-Daudé } 6428d7f2e76SPhilippe Mathieu-Daudé } 6438d7f2e76SPhilippe Mathieu-Daudé address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data, 6448d7f2e76SPhilippe Mathieu-Daudé len); 6458d7f2e76SPhilippe Mathieu-Daudé g_free(data); 6468d7f2e76SPhilippe Mathieu-Daudé 6478d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(chr); 6488d7f2e76SPhilippe Mathieu-Daudé qtest_send(chr, "OK\n"); 6498d7f2e76SPhilippe Mathieu-Daudé } else if (strcmp(words[0], "memset") == 0) { 6508d7f2e76SPhilippe Mathieu-Daudé uint64_t addr, len; 6518d7f2e76SPhilippe Mathieu-Daudé uint8_t *data; 6528d7f2e76SPhilippe Mathieu-Daudé unsigned long pattern; 6538d7f2e76SPhilippe Mathieu-Daudé int ret; 6548d7f2e76SPhilippe Mathieu-Daudé 6558d7f2e76SPhilippe Mathieu-Daudé g_assert(words[1] && words[2] && words[3]); 6568d7f2e76SPhilippe Mathieu-Daudé ret = qemu_strtou64(words[1], NULL, 0, &addr); 6578d7f2e76SPhilippe Mathieu-Daudé g_assert(ret == 0); 6588d7f2e76SPhilippe Mathieu-Daudé ret = qemu_strtou64(words[2], NULL, 0, &len); 6598d7f2e76SPhilippe Mathieu-Daudé g_assert(ret == 0); 6608d7f2e76SPhilippe Mathieu-Daudé ret = qemu_strtoul(words[3], NULL, 0, &pattern); 6618d7f2e76SPhilippe Mathieu-Daudé g_assert(ret == 0); 6628d7f2e76SPhilippe Mathieu-Daudé 6638d7f2e76SPhilippe Mathieu-Daudé if (len) { 6648d7f2e76SPhilippe Mathieu-Daudé data = g_malloc(len); 6658d7f2e76SPhilippe Mathieu-Daudé memset(data, pattern, len); 6668d7f2e76SPhilippe Mathieu-Daudé address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, 6678d7f2e76SPhilippe Mathieu-Daudé data, len); 6688d7f2e76SPhilippe Mathieu-Daudé g_free(data); 6698d7f2e76SPhilippe Mathieu-Daudé } 6708d7f2e76SPhilippe Mathieu-Daudé 6718d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(chr); 6728d7f2e76SPhilippe Mathieu-Daudé qtest_send(chr, "OK\n"); 6738d7f2e76SPhilippe Mathieu-Daudé } else if (strcmp(words[0], "b64write") == 0) { 6748d7f2e76SPhilippe Mathieu-Daudé uint64_t addr, len; 6758d7f2e76SPhilippe Mathieu-Daudé uint8_t *data; 6768d7f2e76SPhilippe Mathieu-Daudé size_t data_len; 6778d7f2e76SPhilippe Mathieu-Daudé gsize out_len; 6788d7f2e76SPhilippe Mathieu-Daudé int ret; 6798d7f2e76SPhilippe Mathieu-Daudé 6808d7f2e76SPhilippe Mathieu-Daudé g_assert(words[1] && words[2] && words[3]); 6818d7f2e76SPhilippe Mathieu-Daudé ret = qemu_strtou64(words[1], NULL, 0, &addr); 6828d7f2e76SPhilippe Mathieu-Daudé g_assert(ret == 0); 6838d7f2e76SPhilippe Mathieu-Daudé ret = qemu_strtou64(words[2], NULL, 0, &len); 6848d7f2e76SPhilippe Mathieu-Daudé g_assert(ret == 0); 6858d7f2e76SPhilippe Mathieu-Daudé 6868d7f2e76SPhilippe Mathieu-Daudé data_len = strlen(words[3]); 6878d7f2e76SPhilippe Mathieu-Daudé if (data_len < 3) { 6888d7f2e76SPhilippe Mathieu-Daudé qtest_send(chr, "ERR invalid argument size\n"); 6898d7f2e76SPhilippe Mathieu-Daudé return; 6908d7f2e76SPhilippe Mathieu-Daudé } 6918d7f2e76SPhilippe Mathieu-Daudé 6928d7f2e76SPhilippe Mathieu-Daudé data = g_base64_decode_inplace(words[3], &out_len); 6938d7f2e76SPhilippe Mathieu-Daudé if (out_len != len) { 6948d7f2e76SPhilippe Mathieu-Daudé qtest_log_send("b64write: data length mismatch (told %"PRIu64", " 6958d7f2e76SPhilippe Mathieu-Daudé "found %zu)\n", 6968d7f2e76SPhilippe Mathieu-Daudé len, out_len); 6978d7f2e76SPhilippe Mathieu-Daudé out_len = MIN(out_len, len); 6988d7f2e76SPhilippe Mathieu-Daudé } 6998d7f2e76SPhilippe Mathieu-Daudé 7008d7f2e76SPhilippe Mathieu-Daudé address_space_write(first_cpu->as, addr, MEMTXATTRS_UNSPECIFIED, data, 7018d7f2e76SPhilippe Mathieu-Daudé len); 7028d7f2e76SPhilippe Mathieu-Daudé 7038d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(chr); 7048d7f2e76SPhilippe Mathieu-Daudé qtest_send(chr, "OK\n"); 7058d7f2e76SPhilippe Mathieu-Daudé } else if (strcmp(words[0], "endianness") == 0) { 7068d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(chr); 7078d7f2e76SPhilippe Mathieu-Daudé if (target_words_bigendian()) { 7088d7f2e76SPhilippe Mathieu-Daudé qtest_sendf(chr, "OK big\n"); 7098d7f2e76SPhilippe Mathieu-Daudé } else { 7108d7f2e76SPhilippe Mathieu-Daudé qtest_sendf(chr, "OK little\n"); 7118d7f2e76SPhilippe Mathieu-Daudé } 7128d7f2e76SPhilippe Mathieu-Daudé } else if (qtest_enabled() && strcmp(words[0], "clock_step") == 0) { 7138d7f2e76SPhilippe Mathieu-Daudé int64_t ns; 7148d7f2e76SPhilippe Mathieu-Daudé 7158d7f2e76SPhilippe Mathieu-Daudé if (words[1]) { 7168d7f2e76SPhilippe Mathieu-Daudé int ret = qemu_strtoi64(words[1], NULL, 0, &ns); 7178d7f2e76SPhilippe Mathieu-Daudé g_assert(ret == 0); 7188d7f2e76SPhilippe Mathieu-Daudé } else { 7198d7f2e76SPhilippe Mathieu-Daudé ns = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL, 7208d7f2e76SPhilippe Mathieu-Daudé QEMU_TIMER_ATTR_ALL); 7218d7f2e76SPhilippe Mathieu-Daudé } 722*ffbc3949SAlex Bennée qemu_clock_advance_virtual_time( 723*ffbc3949SAlex Bennée qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ns); 7248d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(chr); 7258d7f2e76SPhilippe Mathieu-Daudé qtest_sendf(chr, "OK %"PRIi64"\n", 7268d7f2e76SPhilippe Mathieu-Daudé (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); 7278d7f2e76SPhilippe Mathieu-Daudé } else if (strcmp(words[0], "module_load") == 0) { 7288d7f2e76SPhilippe Mathieu-Daudé Error *local_err = NULL; 7298d7f2e76SPhilippe Mathieu-Daudé int rv; 7308d7f2e76SPhilippe Mathieu-Daudé g_assert(words[1] && words[2]); 7318d7f2e76SPhilippe Mathieu-Daudé 7328d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(chr); 7338d7f2e76SPhilippe Mathieu-Daudé rv = module_load(words[1], words[2], &local_err); 7348d7f2e76SPhilippe Mathieu-Daudé if (rv > 0) { 7358d7f2e76SPhilippe Mathieu-Daudé qtest_sendf(chr, "OK\n"); 7368d7f2e76SPhilippe Mathieu-Daudé } else { 7378d7f2e76SPhilippe Mathieu-Daudé if (rv < 0) { 7388d7f2e76SPhilippe Mathieu-Daudé error_report_err(local_err); 7398d7f2e76SPhilippe Mathieu-Daudé } 7408d7f2e76SPhilippe Mathieu-Daudé qtest_sendf(chr, "FAIL\n"); 7418d7f2e76SPhilippe Mathieu-Daudé } 7428d7f2e76SPhilippe Mathieu-Daudé } else if (qtest_enabled() && strcmp(words[0], "clock_set") == 0) { 7438d7f2e76SPhilippe Mathieu-Daudé int64_t ns; 7448d7f2e76SPhilippe Mathieu-Daudé int ret; 7458d7f2e76SPhilippe Mathieu-Daudé 7468d7f2e76SPhilippe Mathieu-Daudé g_assert(words[1]); 7478d7f2e76SPhilippe Mathieu-Daudé ret = qemu_strtoi64(words[1], NULL, 0, &ns); 7488d7f2e76SPhilippe Mathieu-Daudé g_assert(ret == 0); 749*ffbc3949SAlex Bennée qemu_clock_advance_virtual_time(ns); 7508d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(chr); 7518d7f2e76SPhilippe Mathieu-Daudé qtest_sendf(chr, "OK %"PRIi64"\n", 7528d7f2e76SPhilippe Mathieu-Daudé (int64_t)qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); 7538d7f2e76SPhilippe Mathieu-Daudé } else if (process_command_cb && process_command_cb(chr, words)) { 7548d7f2e76SPhilippe Mathieu-Daudé /* Command got consumed by the callback handler */ 7558d7f2e76SPhilippe Mathieu-Daudé } else { 7568d7f2e76SPhilippe Mathieu-Daudé qtest_send_prefix(chr); 7578d7f2e76SPhilippe Mathieu-Daudé qtest_sendf(chr, "FAIL Unknown command '%s'\n", words[0]); 7588d7f2e76SPhilippe Mathieu-Daudé } 7598d7f2e76SPhilippe Mathieu-Daudé } 7608d7f2e76SPhilippe Mathieu-Daudé 7618d7f2e76SPhilippe Mathieu-Daudé static void qtest_process_inbuf(CharBackend *chr, GString *inbuf) 7628d7f2e76SPhilippe Mathieu-Daudé { 7638d7f2e76SPhilippe Mathieu-Daudé char *end; 7648d7f2e76SPhilippe Mathieu-Daudé 7658d7f2e76SPhilippe Mathieu-Daudé while ((end = strchr(inbuf->str, '\n')) != NULL) { 7668d7f2e76SPhilippe Mathieu-Daudé size_t offset; 7678d7f2e76SPhilippe Mathieu-Daudé GString *cmd; 7688d7f2e76SPhilippe Mathieu-Daudé gchar **words; 7698d7f2e76SPhilippe Mathieu-Daudé 7708d7f2e76SPhilippe Mathieu-Daudé offset = end - inbuf->str; 7718d7f2e76SPhilippe Mathieu-Daudé 7728d7f2e76SPhilippe Mathieu-Daudé cmd = g_string_new_len(inbuf->str, offset); 7738d7f2e76SPhilippe Mathieu-Daudé g_string_erase(inbuf, 0, offset + 1); 7748d7f2e76SPhilippe Mathieu-Daudé 7758d7f2e76SPhilippe Mathieu-Daudé words = g_strsplit(cmd->str, " ", 0); 7768d7f2e76SPhilippe Mathieu-Daudé qtest_process_command(chr, words); 7778d7f2e76SPhilippe Mathieu-Daudé g_strfreev(words); 7788d7f2e76SPhilippe Mathieu-Daudé 7798d7f2e76SPhilippe Mathieu-Daudé g_string_free(cmd, TRUE); 7808d7f2e76SPhilippe Mathieu-Daudé } 7818d7f2e76SPhilippe Mathieu-Daudé } 7828d7f2e76SPhilippe Mathieu-Daudé 7838d7f2e76SPhilippe Mathieu-Daudé static void qtest_read(void *opaque, const uint8_t *buf, int size) 7848d7f2e76SPhilippe Mathieu-Daudé { 7858d7f2e76SPhilippe Mathieu-Daudé CharBackend *chr = opaque; 7868d7f2e76SPhilippe Mathieu-Daudé 7878d7f2e76SPhilippe Mathieu-Daudé g_string_append_len(inbuf, (const gchar *)buf, size); 7888d7f2e76SPhilippe Mathieu-Daudé qtest_process_inbuf(chr, inbuf); 7898d7f2e76SPhilippe Mathieu-Daudé } 7908d7f2e76SPhilippe Mathieu-Daudé 7918d7f2e76SPhilippe Mathieu-Daudé static int qtest_can_read(void *opaque) 7928d7f2e76SPhilippe Mathieu-Daudé { 7938d7f2e76SPhilippe Mathieu-Daudé return 1024; 7948d7f2e76SPhilippe Mathieu-Daudé } 7958d7f2e76SPhilippe Mathieu-Daudé 7968d7f2e76SPhilippe Mathieu-Daudé static void qtest_event(void *opaque, QEMUChrEvent event) 7978d7f2e76SPhilippe Mathieu-Daudé { 7988d7f2e76SPhilippe Mathieu-Daudé int i; 7998d7f2e76SPhilippe Mathieu-Daudé 8008d7f2e76SPhilippe Mathieu-Daudé switch (event) { 8018d7f2e76SPhilippe Mathieu-Daudé case CHR_EVENT_OPENED: 8028d7f2e76SPhilippe Mathieu-Daudé /* 8038d7f2e76SPhilippe Mathieu-Daudé * We used to call qemu_system_reset() here, hoping we could 8048d7f2e76SPhilippe Mathieu-Daudé * use the same process for multiple tests that way. Never 8058d7f2e76SPhilippe Mathieu-Daudé * used. Injects an extra reset even when it's not used, and 8068d7f2e76SPhilippe Mathieu-Daudé * that can mess up tests, e.g. -boot once. 8078d7f2e76SPhilippe Mathieu-Daudé */ 8088d7f2e76SPhilippe Mathieu-Daudé for (i = 0; i < ARRAY_SIZE(irq_levels); i++) { 8098d7f2e76SPhilippe Mathieu-Daudé irq_levels[i] = 0; 8108d7f2e76SPhilippe Mathieu-Daudé } 8118d7f2e76SPhilippe Mathieu-Daudé 8128d7f2e76SPhilippe Mathieu-Daudé g_clear_pointer(&timer, g_timer_destroy); 8138d7f2e76SPhilippe Mathieu-Daudé timer = g_timer_new(); 8148d7f2e76SPhilippe Mathieu-Daudé qtest_opened = true; 8158d7f2e76SPhilippe Mathieu-Daudé if (qtest_log_fp) { 8168d7f2e76SPhilippe Mathieu-Daudé fprintf(qtest_log_fp, "[I " FMT_timeval "] OPENED\n", g_timer_elapsed(timer, NULL)); 8178d7f2e76SPhilippe Mathieu-Daudé } 8188d7f2e76SPhilippe Mathieu-Daudé break; 8198d7f2e76SPhilippe Mathieu-Daudé case CHR_EVENT_CLOSED: 8208d7f2e76SPhilippe Mathieu-Daudé qtest_opened = false; 8218d7f2e76SPhilippe Mathieu-Daudé if (qtest_log_fp) { 8228d7f2e76SPhilippe Mathieu-Daudé fprintf(qtest_log_fp, "[I +" FMT_timeval "] CLOSED\n", g_timer_elapsed(timer, NULL)); 8238d7f2e76SPhilippe Mathieu-Daudé } 8248d7f2e76SPhilippe Mathieu-Daudé g_clear_pointer(&timer, g_timer_destroy); 8258d7f2e76SPhilippe Mathieu-Daudé break; 8268d7f2e76SPhilippe Mathieu-Daudé default: 8278d7f2e76SPhilippe Mathieu-Daudé break; 8288d7f2e76SPhilippe Mathieu-Daudé } 8298d7f2e76SPhilippe Mathieu-Daudé } 8308d7f2e76SPhilippe Mathieu-Daudé 8318d7f2e76SPhilippe Mathieu-Daudé void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error **errp) 8328d7f2e76SPhilippe Mathieu-Daudé { 8338d7f2e76SPhilippe Mathieu-Daudé ERRP_GUARD(); 8348d7f2e76SPhilippe Mathieu-Daudé Chardev *chr; 83563ba5e13SPhilippe Mathieu-Daudé Object *qobj; 8368d7f2e76SPhilippe Mathieu-Daudé 8378d7f2e76SPhilippe Mathieu-Daudé chr = qemu_chr_new("qtest", qtest_chrdev, NULL); 8388d7f2e76SPhilippe Mathieu-Daudé if (chr == NULL) { 8398d7f2e76SPhilippe Mathieu-Daudé error_setg(errp, "Failed to initialize device for qtest: \"%s\"", 8408d7f2e76SPhilippe Mathieu-Daudé qtest_chrdev); 8418d7f2e76SPhilippe Mathieu-Daudé return; 8428d7f2e76SPhilippe Mathieu-Daudé } 8438d7f2e76SPhilippe Mathieu-Daudé 84463ba5e13SPhilippe Mathieu-Daudé qobj = object_new(TYPE_QTEST); 84563ba5e13SPhilippe Mathieu-Daudé object_property_set_str(qobj, "chardev", chr->label, &error_abort); 8468d7f2e76SPhilippe Mathieu-Daudé if (qtest_log) { 84763ba5e13SPhilippe Mathieu-Daudé object_property_set_str(qobj, "log", qtest_log, &error_abort); 8488d7f2e76SPhilippe Mathieu-Daudé } 84963ba5e13SPhilippe Mathieu-Daudé object_property_add_child(qdev_get_machine(), "qtest", qobj); 85063ba5e13SPhilippe Mathieu-Daudé user_creatable_complete(USER_CREATABLE(qobj), errp); 8518d7f2e76SPhilippe Mathieu-Daudé if (*errp) { 85263ba5e13SPhilippe Mathieu-Daudé object_unparent(qobj); 8538d7f2e76SPhilippe Mathieu-Daudé } 8548d7f2e76SPhilippe Mathieu-Daudé object_unref(OBJECT(chr)); 85563ba5e13SPhilippe Mathieu-Daudé object_unref(qobj); 8568d7f2e76SPhilippe Mathieu-Daudé } 8578d7f2e76SPhilippe Mathieu-Daudé 8588d7f2e76SPhilippe Mathieu-Daudé static bool qtest_server_start(QTest *q, Error **errp) 8598d7f2e76SPhilippe Mathieu-Daudé { 8608d7f2e76SPhilippe Mathieu-Daudé Chardev *chr = q->chr; 8618d7f2e76SPhilippe Mathieu-Daudé const char *qtest_log = q->log; 8628d7f2e76SPhilippe Mathieu-Daudé 8638d7f2e76SPhilippe Mathieu-Daudé if (qtest_log) { 8648d7f2e76SPhilippe Mathieu-Daudé if (strcmp(qtest_log, "none") != 0) { 8658d7f2e76SPhilippe Mathieu-Daudé qtest_log_fp = fopen(qtest_log, "w+"); 8668d7f2e76SPhilippe Mathieu-Daudé } 8678d7f2e76SPhilippe Mathieu-Daudé } else { 8688d7f2e76SPhilippe Mathieu-Daudé qtest_log_fp = stderr; 8698d7f2e76SPhilippe Mathieu-Daudé } 8708d7f2e76SPhilippe Mathieu-Daudé 8718d7f2e76SPhilippe Mathieu-Daudé if (!qemu_chr_fe_init(&q->qtest_chr, chr, errp)) { 8728d7f2e76SPhilippe Mathieu-Daudé return false; 8738d7f2e76SPhilippe Mathieu-Daudé } 8748d7f2e76SPhilippe Mathieu-Daudé qemu_chr_fe_set_handlers(&q->qtest_chr, qtest_can_read, qtest_read, 8758d7f2e76SPhilippe Mathieu-Daudé qtest_event, NULL, &q->qtest_chr, NULL, true); 8768d7f2e76SPhilippe Mathieu-Daudé qemu_chr_fe_set_echo(&q->qtest_chr, true); 8778d7f2e76SPhilippe Mathieu-Daudé 8788d7f2e76SPhilippe Mathieu-Daudé inbuf = g_string_new(""); 8798d7f2e76SPhilippe Mathieu-Daudé 8808d7f2e76SPhilippe Mathieu-Daudé if (!qtest_server_send) { 8818d7f2e76SPhilippe Mathieu-Daudé qtest_server_set_send_handler(qtest_server_char_be_send, &q->qtest_chr); 8828d7f2e76SPhilippe Mathieu-Daudé } 8838d7f2e76SPhilippe Mathieu-Daudé qtest = q; 8848d7f2e76SPhilippe Mathieu-Daudé return true; 8858d7f2e76SPhilippe Mathieu-Daudé } 8868d7f2e76SPhilippe Mathieu-Daudé 8878d7f2e76SPhilippe Mathieu-Daudé void qtest_server_set_send_handler(void (*send)(void*, const char*), 8888d7f2e76SPhilippe Mathieu-Daudé void *opaque) 8898d7f2e76SPhilippe Mathieu-Daudé { 8908d7f2e76SPhilippe Mathieu-Daudé qtest_server_send = send; 8918d7f2e76SPhilippe Mathieu-Daudé qtest_server_send_opaque = opaque; 8928d7f2e76SPhilippe Mathieu-Daudé } 8938d7f2e76SPhilippe Mathieu-Daudé 8948d7f2e76SPhilippe Mathieu-Daudé bool qtest_driver(void) 8958d7f2e76SPhilippe Mathieu-Daudé { 8968d7f2e76SPhilippe Mathieu-Daudé return qtest && qtest->qtest_chr.chr != NULL; 8978d7f2e76SPhilippe Mathieu-Daudé } 8988d7f2e76SPhilippe Mathieu-Daudé 8998d7f2e76SPhilippe Mathieu-Daudé void qtest_server_inproc_recv(void *dummy, const char *buf) 9008d7f2e76SPhilippe Mathieu-Daudé { 9018d7f2e76SPhilippe Mathieu-Daudé static GString *gstr; 9028d7f2e76SPhilippe Mathieu-Daudé if (!gstr) { 9038d7f2e76SPhilippe Mathieu-Daudé gstr = g_string_new(NULL); 9048d7f2e76SPhilippe Mathieu-Daudé } 9058d7f2e76SPhilippe Mathieu-Daudé g_string_append(gstr, buf); 9068d7f2e76SPhilippe Mathieu-Daudé if (gstr->str[gstr->len - 1] == '\n') { 9078d7f2e76SPhilippe Mathieu-Daudé qtest_process_inbuf(NULL, gstr); 9088d7f2e76SPhilippe Mathieu-Daudé g_string_truncate(gstr, 0); 9098d7f2e76SPhilippe Mathieu-Daudé } 9108d7f2e76SPhilippe Mathieu-Daudé } 9118d7f2e76SPhilippe Mathieu-Daudé 9128d7f2e76SPhilippe Mathieu-Daudé static void qtest_complete(UserCreatable *uc, Error **errp) 9138d7f2e76SPhilippe Mathieu-Daudé { 9148d7f2e76SPhilippe Mathieu-Daudé QTest *q = QTEST(uc); 9158d7f2e76SPhilippe Mathieu-Daudé if (qtest) { 9168d7f2e76SPhilippe Mathieu-Daudé error_setg(errp, "Only one instance of qtest can be created"); 9178d7f2e76SPhilippe Mathieu-Daudé return; 9188d7f2e76SPhilippe Mathieu-Daudé } 9198d7f2e76SPhilippe Mathieu-Daudé if (!q->chr_name) { 9208d7f2e76SPhilippe Mathieu-Daudé error_setg(errp, "No backend specified"); 9218d7f2e76SPhilippe Mathieu-Daudé return; 9228d7f2e76SPhilippe Mathieu-Daudé } 9238d7f2e76SPhilippe Mathieu-Daudé 9248d7f2e76SPhilippe Mathieu-Daudé if (OBJECT(uc)->parent != qdev_get_machine()) { 9258d7f2e76SPhilippe Mathieu-Daudé q->has_machine_link = true; 9268d7f2e76SPhilippe Mathieu-Daudé object_property_add_const_link(qdev_get_machine(), "qtest", OBJECT(uc)); 9278d7f2e76SPhilippe Mathieu-Daudé } else { 9288d7f2e76SPhilippe Mathieu-Daudé /* -qtest was used. */ 9298d7f2e76SPhilippe Mathieu-Daudé } 9308d7f2e76SPhilippe Mathieu-Daudé 9318d7f2e76SPhilippe Mathieu-Daudé qtest_server_start(q, errp); 9328d7f2e76SPhilippe Mathieu-Daudé } 9338d7f2e76SPhilippe Mathieu-Daudé 9348d7f2e76SPhilippe Mathieu-Daudé static void qtest_unparent(Object *obj) 9358d7f2e76SPhilippe Mathieu-Daudé { 9368d7f2e76SPhilippe Mathieu-Daudé QTest *q = QTEST(obj); 9378d7f2e76SPhilippe Mathieu-Daudé 9388d7f2e76SPhilippe Mathieu-Daudé if (qtest == q) { 9398d7f2e76SPhilippe Mathieu-Daudé qemu_chr_fe_disconnect(&q->qtest_chr); 9408d7f2e76SPhilippe Mathieu-Daudé assert(!qtest_opened); 9418d7f2e76SPhilippe Mathieu-Daudé qemu_chr_fe_deinit(&q->qtest_chr, false); 9428d7f2e76SPhilippe Mathieu-Daudé if (qtest_log_fp) { 9438d7f2e76SPhilippe Mathieu-Daudé fclose(qtest_log_fp); 9448d7f2e76SPhilippe Mathieu-Daudé qtest_log_fp = NULL; 9458d7f2e76SPhilippe Mathieu-Daudé } 9468d7f2e76SPhilippe Mathieu-Daudé qtest = NULL; 9478d7f2e76SPhilippe Mathieu-Daudé } 9488d7f2e76SPhilippe Mathieu-Daudé 9498d7f2e76SPhilippe Mathieu-Daudé if (q->has_machine_link) { 9508d7f2e76SPhilippe Mathieu-Daudé object_property_del(qdev_get_machine(), "qtest"); 9518d7f2e76SPhilippe Mathieu-Daudé q->has_machine_link = false; 9528d7f2e76SPhilippe Mathieu-Daudé } 9538d7f2e76SPhilippe Mathieu-Daudé } 9548d7f2e76SPhilippe Mathieu-Daudé 9558d7f2e76SPhilippe Mathieu-Daudé static void qtest_set_log(Object *obj, const char *value, Error **errp) 9568d7f2e76SPhilippe Mathieu-Daudé { 9578d7f2e76SPhilippe Mathieu-Daudé QTest *q = QTEST(obj); 9588d7f2e76SPhilippe Mathieu-Daudé 9598d7f2e76SPhilippe Mathieu-Daudé if (qtest == q) { 9608d7f2e76SPhilippe Mathieu-Daudé error_setg(errp, "Property 'log' can not be set now"); 9618d7f2e76SPhilippe Mathieu-Daudé } else { 9628d7f2e76SPhilippe Mathieu-Daudé g_free(q->log); 9638d7f2e76SPhilippe Mathieu-Daudé q->log = g_strdup(value); 9648d7f2e76SPhilippe Mathieu-Daudé } 9658d7f2e76SPhilippe Mathieu-Daudé } 9668d7f2e76SPhilippe Mathieu-Daudé 9678d7f2e76SPhilippe Mathieu-Daudé static char *qtest_get_log(Object *obj, Error **errp) 9688d7f2e76SPhilippe Mathieu-Daudé { 9698d7f2e76SPhilippe Mathieu-Daudé QTest *q = QTEST(obj); 9708d7f2e76SPhilippe Mathieu-Daudé 9718d7f2e76SPhilippe Mathieu-Daudé return g_strdup(q->log); 9728d7f2e76SPhilippe Mathieu-Daudé } 9738d7f2e76SPhilippe Mathieu-Daudé 9748d7f2e76SPhilippe Mathieu-Daudé static void qtest_set_chardev(Object *obj, const char *value, Error **errp) 9758d7f2e76SPhilippe Mathieu-Daudé { 9768d7f2e76SPhilippe Mathieu-Daudé QTest *q = QTEST(obj); 9778d7f2e76SPhilippe Mathieu-Daudé Chardev *chr; 9788d7f2e76SPhilippe Mathieu-Daudé 9798d7f2e76SPhilippe Mathieu-Daudé if (qtest == q) { 9808d7f2e76SPhilippe Mathieu-Daudé error_setg(errp, "Property 'chardev' can not be set now"); 9818d7f2e76SPhilippe Mathieu-Daudé return; 9828d7f2e76SPhilippe Mathieu-Daudé } 9838d7f2e76SPhilippe Mathieu-Daudé 9848d7f2e76SPhilippe Mathieu-Daudé chr = qemu_chr_find(value); 9858d7f2e76SPhilippe Mathieu-Daudé if (!chr) { 9868d7f2e76SPhilippe Mathieu-Daudé error_setg(errp, "Cannot find character device '%s'", value); 9878d7f2e76SPhilippe Mathieu-Daudé return; 9888d7f2e76SPhilippe Mathieu-Daudé } 9898d7f2e76SPhilippe Mathieu-Daudé 9908d7f2e76SPhilippe Mathieu-Daudé g_free(q->chr_name); 9918d7f2e76SPhilippe Mathieu-Daudé q->chr_name = g_strdup(value); 9928d7f2e76SPhilippe Mathieu-Daudé 9938d7f2e76SPhilippe Mathieu-Daudé if (q->chr) { 9948d7f2e76SPhilippe Mathieu-Daudé object_unref(q->chr); 9958d7f2e76SPhilippe Mathieu-Daudé } 9968d7f2e76SPhilippe Mathieu-Daudé q->chr = chr; 9978d7f2e76SPhilippe Mathieu-Daudé object_ref(chr); 9988d7f2e76SPhilippe Mathieu-Daudé } 9998d7f2e76SPhilippe Mathieu-Daudé 10008d7f2e76SPhilippe Mathieu-Daudé static char *qtest_get_chardev(Object *obj, Error **errp) 10018d7f2e76SPhilippe Mathieu-Daudé { 10028d7f2e76SPhilippe Mathieu-Daudé QTest *q = QTEST(obj); 10038d7f2e76SPhilippe Mathieu-Daudé 10048d7f2e76SPhilippe Mathieu-Daudé return g_strdup(q->chr_name); 10058d7f2e76SPhilippe Mathieu-Daudé } 10068d7f2e76SPhilippe Mathieu-Daudé 10078d7f2e76SPhilippe Mathieu-Daudé static void qtest_class_init(ObjectClass *oc, void *data) 10088d7f2e76SPhilippe Mathieu-Daudé { 10098d7f2e76SPhilippe Mathieu-Daudé UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); 10108d7f2e76SPhilippe Mathieu-Daudé 10118d7f2e76SPhilippe Mathieu-Daudé oc->unparent = qtest_unparent; 10128d7f2e76SPhilippe Mathieu-Daudé ucc->complete = qtest_complete; 10138d7f2e76SPhilippe Mathieu-Daudé 10148d7f2e76SPhilippe Mathieu-Daudé object_class_property_add_str(oc, "chardev", 10158d7f2e76SPhilippe Mathieu-Daudé qtest_get_chardev, qtest_set_chardev); 10168d7f2e76SPhilippe Mathieu-Daudé object_class_property_add_str(oc, "log", 10178d7f2e76SPhilippe Mathieu-Daudé qtest_get_log, qtest_set_log); 10188d7f2e76SPhilippe Mathieu-Daudé } 10198d7f2e76SPhilippe Mathieu-Daudé 10208d7f2e76SPhilippe Mathieu-Daudé static const TypeInfo qtest_info = { 10218d7f2e76SPhilippe Mathieu-Daudé .name = TYPE_QTEST, 10228d7f2e76SPhilippe Mathieu-Daudé .parent = TYPE_OBJECT, 10238d7f2e76SPhilippe Mathieu-Daudé .class_init = qtest_class_init, 10248d7f2e76SPhilippe Mathieu-Daudé .instance_size = sizeof(QTest), 10258d7f2e76SPhilippe Mathieu-Daudé .interfaces = (InterfaceInfo[]) { 10268d7f2e76SPhilippe Mathieu-Daudé { TYPE_USER_CREATABLE }, 10278d7f2e76SPhilippe Mathieu-Daudé { } 10288d7f2e76SPhilippe Mathieu-Daudé } 10298d7f2e76SPhilippe Mathieu-Daudé }; 10308d7f2e76SPhilippe Mathieu-Daudé 10318d7f2e76SPhilippe Mathieu-Daudé static void register_types(void) 10328d7f2e76SPhilippe Mathieu-Daudé { 10338d7f2e76SPhilippe Mathieu-Daudé type_register_static(&qtest_info); 10348d7f2e76SPhilippe Mathieu-Daudé } 10358d7f2e76SPhilippe Mathieu-Daudé 10368d7f2e76SPhilippe Mathieu-Daudé type_init(register_types); 1037