diff mbox

backends: Introduce chr-testdev

Message ID 1405064666-5359-1-git-send-email-drjones@redhat.com
State New
Headers show

Commit Message

Andrew Jones July 11, 2014, 7:44 a.m. UTC
From: Paolo Bonzini <pbonzini@redhat.com>

From: Paolo Bonzini <pbonzini@redhat.com>

chr-testdev enables a virtio serial channel to be used for guest
initiated qemu exits. hw/misc/debugexit already enables guest
initiated qemu exits, but only for PC targets. chr-testdev supports
any virtio-capable target. kvm-unit-tests/arm is already making use
of this backend.

Currently there is a single command implemented, "q".  It takes a
(prefix) argument for the exit code, thus an exit is implemented by
writing, e.g. "1q", to the virtio-serial port.

It can be used as:
   $QEMU ... \
     -device virtio-serial-device \
     -device virtserialport,chardev=ctd -chardev testdev,id=ctd

or, use:
   $QEMU ... \
     -device virtio-serial-device \
     -device virtconsole,chardev=ctd -chardev testdev,id=ctd

to bind it to virtio-serial port0.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Andrew Jones <drjones@redhat.com>
---
 backends/Makefile.objs |   2 +-
 backends/testdev.c     | 131 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/sysemu/char.h  |   3 ++
 qapi-schema.json       |   1 +
 qemu-char.c            |   4 ++
 5 files changed, 140 insertions(+), 1 deletion(-)
 create mode 100644 backends/testdev.c

Comments

Eric Blake July 11, 2014, 3:30 p.m. UTC | #1
On 07/11/2014 01:44 AM, Andrew Jones wrote:
> From: Paolo Bonzini <pbonzini@redhat.com>
> 
> From: Paolo Bonzini <pbonzini@redhat.com>
> 
> chr-testdev enables a virtio serial channel to be used for guest
> initiated qemu exits. hw/misc/debugexit already enables guest
> initiated qemu exits, but only for PC targets. chr-testdev supports
> any virtio-capable target. kvm-unit-tests/arm is already making use
> of this backend.
> 
> Currently there is a single command implemented, "q".  It takes a
> (prefix) argument for the exit code, thus an exit is implemented by
> writing, e.g. "1q", to the virtio-serial port.
> 
> It can be used as:
>    $QEMU ... \
>      -device virtio-serial-device \
>      -device virtserialport,chardev=ctd -chardev testdev,id=ctd
> 
> or, use:
>    $QEMU ... \
>      -device virtio-serial-device \
>      -device virtconsole,chardev=ctd -chardev testdev,id=ctd
> 
> to bind it to virtio-serial port0.
> 

> +
> +    switch (c) {
> +    case 'q':
> +        exit((arg << 1) | 1);
> +        break;

I'm trying to figure out the motive for only exiting with odd numbers.
That is, 'q' => 1, '1q' => 3, '2q' => 5, '3q' => 7.  It means that at
most, I can do '127q' => 255 before I suffer from exit() limiting things
to 8 bits.  This wasn't explained in the commit message.

> +++ b/qapi-schema.json
> @@ -2764,6 +2764,7 @@
>                                         'mux'    : 'ChardevMux',
>                                         'msmouse': 'ChardevDummy',
>                                         'braille': 'ChardevDummy',
> +                                       'testdev': 'ChardevDummy',
>                                         'stdio'  : 'ChardevStdio',

It would be nice to have some sort of 'testdev since 2.2' documentation.
Paolo Bonzini July 11, 2014, 3:49 p.m. UTC | #2
Il 11/07/2014 17:30, Eric Blake ha scritto:
> On 07/11/2014 01:44 AM, Andrew Jones wrote:
>> From: Paolo Bonzini <pbonzini@redhat.com>
>>
>> From: Paolo Bonzini <pbonzini@redhat.com>
>>
>> chr-testdev enables a virtio serial channel to be used for guest
>> initiated qemu exits. hw/misc/debugexit already enables guest
>> initiated qemu exits, but only for PC targets. chr-testdev supports
>> any virtio-capable target. kvm-unit-tests/arm is already making use
>> of this backend.
>>
>> Currently there is a single command implemented, "q".  It takes a
>> (prefix) argument for the exit code, thus an exit is implemented by
>> writing, e.g. "1q", to the virtio-serial port.
>>
>> It can be used as:
>>    $QEMU ... \
>>      -device virtio-serial-device \
>>      -device virtserialport,chardev=ctd -chardev testdev,id=ctd
>>
>> or, use:
>>    $QEMU ... \
>>      -device virtio-serial-device \
>>      -device virtconsole,chardev=ctd -chardev testdev,id=ctd
>>
>> to bind it to virtio-serial port0.
>>
>
>> +
>> +    switch (c) {
>> +    case 'q':
>> +        exit((arg << 1) | 1);
>> +        break;
>
> I'm trying to figure out the motive for only exiting with odd numbers.
> That is, 'q' => 1, '1q' => 3, '2q' => 5, '3q' => 7.  It means that at
> most, I can do '127q' => 255 before I suffer from exit() limiting things
> to 8 bits.  This wasn't explained in the commit message.

It's just for parity with isa-debugexit.

Paolo
diff mbox

Patch

diff --git a/backends/Makefile.objs b/backends/Makefile.objs
index 506a46c33b057..31a3a894f5ead 100644
--- a/backends/Makefile.objs
+++ b/backends/Makefile.objs
@@ -1,7 +1,7 @@ 
 common-obj-y += rng.o rng-egd.o
 common-obj-$(CONFIG_POSIX) += rng-random.o
 
-common-obj-y += msmouse.o
+common-obj-y += msmouse.o testdev.o
 common-obj-$(CONFIG_BRLAPI) += baum.o
 baum.o-cflags := $(SDL_CFLAGS)
 
diff --git a/backends/testdev.c b/backends/testdev.c
new file mode 100644
index 0000000000000..70d63b3b8d6bc
--- /dev/null
+++ b/backends/testdev.c
@@ -0,0 +1,131 @@ 
+/*
+ * QEMU Char Device for testsuite control
+ *
+ * Copyright (c) 2014 Red Hat, Inc.
+ *
+ * Author: Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "sysemu/char.h"
+
+#define BUF_SIZE 32
+
+typedef struct {
+    CharDriverState *chr;
+    uint8_t in_buf[32];
+    int in_buf_used;
+} TestdevCharState;
+
+/* Try to interpret a whole incoming packet */
+static int testdev_eat_packet(TestdevCharState *testdev)
+{
+    const uint8_t *cur = testdev->in_buf;
+    int len = testdev->in_buf_used;
+    uint8_t c;
+    int arg;
+
+#define EAT(c) do { \
+    if (!len--) {   \
+        return 0;   \
+    }               \
+    c = *cur++;     \
+} while (0)
+
+    EAT(c);
+
+    while (isspace(c)) {
+        EAT(c);
+    }
+
+    arg = 0;
+    while (isdigit(c)) {
+        arg = arg * 10 + c - '0';
+        EAT(c);
+    }
+
+    while (isspace(c)) {
+        EAT(c);
+    }
+
+    switch (c) {
+    case 'q':
+        exit((arg << 1) | 1);
+        break;
+    default:
+        break;
+    }
+    return cur - testdev->in_buf;
+}
+
+/* The other end is writing some data.  Store it and try to interpret */
+static int testdev_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+    TestdevCharState *testdev = chr->opaque;
+    int tocopy, eaten, orig_len = len;
+
+    while (len) {
+        /* Complete our buffer as much as possible */
+        tocopy = MIN(len, BUF_SIZE - testdev->in_buf_used);
+
+        memcpy(testdev->in_buf + testdev->in_buf_used, buf, tocopy);
+        testdev->in_buf_used += tocopy;
+        buf += tocopy;
+        len -= tocopy;
+
+        /* Interpret it as much as possible */
+        while (testdev->in_buf_used > 0 &&
+               (eaten = testdev_eat_packet(testdev)) > 0) {
+            memmove(testdev->in_buf, testdev->in_buf + eaten,
+                    testdev->in_buf_used - eaten);
+            testdev->in_buf_used -= eaten;
+        }
+    }
+    return orig_len;
+}
+
+static void testdev_close(struct CharDriverState *chr)
+{
+    TestdevCharState *testdev = chr->opaque;
+
+    g_free(testdev);
+}
+
+CharDriverState *chr_testdev_init(void)
+{
+    TestdevCharState *testdev;
+    CharDriverState *chr;
+
+    testdev = g_malloc0(sizeof(TestdevCharState));
+    testdev->chr = chr = g_malloc0(sizeof(CharDriverState));
+
+    chr->opaque = testdev;
+    chr->chr_write = testdev_write;
+    chr->chr_close = testdev_close;
+
+    return chr;
+}
+
+static void register_types(void)
+{
+    register_char_driver_qapi("testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL);
+}
+
+type_init(register_types);
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 0bbd631e72a47..98cd4c958e723 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -358,6 +358,9 @@  CharDriverState *qemu_char_get_next_serial(void);
 /* msmouse */
 CharDriverState *qemu_chr_open_msmouse(void);
 
+/* testdev.c */
+CharDriverState *chr_testdev_init(void);
+
 /* baum.c */
 CharDriverState *chr_baum_init(void);
 
diff --git a/qapi-schema.json b/qapi-schema.json
index b11aad2068118..80b8f9cf5d677 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2764,6 +2764,7 @@ 
                                        'mux'    : 'ChardevMux',
                                        'msmouse': 'ChardevDummy',
                                        'braille': 'ChardevDummy',
+                                       'testdev': 'ChardevDummy',
                                        'stdio'  : 'ChardevStdio',
                                        'console': 'ChardevDummy',
                                        'spicevmc' : 'ChardevSpiceChannel',
diff --git a/qemu-char.c b/qemu-char.c
index 55e372cf320d2..544c21a4937ca 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -3234,6 +3234,7 @@  QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
         strcmp(filename, "pty")     == 0 ||
         strcmp(filename, "msmouse") == 0 ||
         strcmp(filename, "braille") == 0 ||
+        strcmp(filename, "testdev") == 0 ||
         strcmp(filename, "stdio")   == 0) {
         qemu_opt_set(opts, "backend", filename);
         return opts;
@@ -4031,6 +4032,9 @@  ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
         chr = chr_baum_init();
         break;
 #endif
+    case CHARDEV_BACKEND_KIND_TESTDEV:
+        chr = chr_testdev_init();
+        break;
     case CHARDEV_BACKEND_KIND_STDIO:
         chr = qemu_chr_open_stdio(backend->stdio);
         break;