xref: /openbmc/qemu/system/qtest.c (revision e2bc7787c8dcc38f0f788d68764ffb6b756f7ea6)
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é 
hex2nib(char ch)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é 
qtest_send_prefix(CharBackend * chr)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é 
qtest_log_send(const char * fmt,...)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é 
qtest_server_char_be_send(void * opaque,const char * str)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é 
qtest_send(CharBackend * chr,const char * str)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é 
qtest_sendf(CharBackend * chr,const char * fmt,...)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é 
qtest_irq_handler(void * opaque,int n,int level)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é 
qtest_set_command_cb(bool (* pc_cb)(CharBackend * chr,gchar ** words))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é 
qtest_install_gpio_out_intercept(DeviceState * dev,const char * name,int n)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é 
qtest_process_command(CharBackend * chr,gchar ** words)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é 
qtest_process_inbuf(CharBackend * chr,GString * inbuf)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é 
qtest_read(void * opaque,const uint8_t * buf,int size)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é 
qtest_can_read(void * opaque)7918d7f2e76SPhilippe Mathieu-Daudé static int qtest_can_read(void *opaque)
7928d7f2e76SPhilippe Mathieu-Daudé {
7938d7f2e76SPhilippe Mathieu-Daudé     return 1024;
7948d7f2e76SPhilippe Mathieu-Daudé }
7958d7f2e76SPhilippe Mathieu-Daudé 
qtest_event(void * opaque,QEMUChrEvent event)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é 
qtest_server_init(const char * qtest_chrdev,const char * qtest_log,Error ** errp)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é 
qtest_server_start(QTest * q,Error ** errp)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é 
qtest_server_set_send_handler(void (* send)(void *,const char *),void * opaque)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é 
qtest_driver(void)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é 
qtest_server_inproc_recv(void * dummy,const char * buf)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é 
qtest_complete(UserCreatable * uc,Error ** errp)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é 
qtest_unparent(Object * obj)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é 
qtest_set_log(Object * obj,const char * value,Error ** errp)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é 
qtest_get_log(Object * obj,Error ** errp)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é 
qtest_set_chardev(Object * obj,const char * value,Error ** errp)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é 
qtest_get_chardev(Object * obj,Error ** errp)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é 
qtest_class_init(ObjectClass * oc,void * data)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é 
register_types(void)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