diff options
author | Peter Maydell | 2017-01-30 11:23:20 +0100 |
---|---|---|
committer | Peter Maydell | 2017-01-30 11:23:20 +0100 |
commit | a0def594286d9110a6035e02eef558cf3cf5d847 (patch) | |
tree | 32275a1bfdd6c17682788f2b2e018a15c3b30214 | |
parent | Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20170127'... (diff) | |
parent | memory: don't sign-extend 32-bit writes (diff) | |
download | qemu-a0def594286d9110a6035e02eef558cf3cf5d847.tar.gz qemu-a0def594286d9110a6035e02eef558cf3cf5d847.tar.xz qemu-a0def594286d9110a6035e02eef558cf3cf5d847.zip |
Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging
* SCSI max_transfer support for scsi-generic (Eric)
* x86 SMI broadcast (Laszlo)
* Character device QOMification (Marc-André)
* Record/replay improvements (Pavel)
* iscsi fixes (Peter L.)
* "info mtree -f" command (Peter Xu)
* TSC clock rate reporting (Phil)
* DEVICE_CATEGORY_CPU (Thomas)
* Memory sign-extension fix (Ladi)
# gpg: Signature made Fri 27 Jan 2017 17:08:51 GMT
# gpg: using RSA key 0xBFFBD25F78C7AE83
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>"
# gpg: aka "Paolo Bonzini <pbonzini@redhat.com>"
# Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1
# Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83
* remotes/bonzini/tags/for-upstream: (41 commits)
memory: don't sign-extend 32-bit writes
chardev: qom-ify
vc: use a common prefix for chr callbacks
baum: use a common prefix for chr callbacks
gtk: overwrite the console.c char driver
char: use error_report()
spice-char: improve error reporting
char: rename TCPChardev and NetChardev
char: rename CharDriverState Chardev
bt: use qemu_chr_alloc()
char: allocate CharDriverState as a single object
char: use a feature bit for replay
char: introduce generic qemu_chr_get_kind()
char: fold single-user functions in caller
char: move callbacks in CharDriver
char: use a static array for backends
char: use a const CharDriver
doc: fix spelling
char: add qemu_chr_fe_add_watch() Returns description
qemu-options: stdio is available on win32
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
114 files changed, 2192 insertions, 1448 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index a428cb2e23..e0be7bc0d4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1642,6 +1642,7 @@ M: Peter Lieven <pl@kamp.de> L: qemu-block@nongnu.org S: Supported F: block/iscsi.c +F: block/iscsi-opts.c NFS M: Jeff Cody <jcody@redhat.com> diff --git a/backends/baum.c b/backends/baum.c index b045ef49c5..0f418ed358 100644 --- a/backends/baum.c +++ b/backends/baum.c @@ -85,7 +85,7 @@ #define BUF_SIZE 256 typedef struct { - CharDriverState *chr; + Chardev parent; brlapi_handle_t *brlapi; int brlapi_fd; @@ -98,7 +98,10 @@ typedef struct { uint8_t out_buf_used, out_buf_ptr; QEMUTimer *cellCount_timer; -} BaumDriverState; +} BaumChardev; + +#define TYPE_CHARDEV_BRAILLE "chardev-braille" +#define BAUM_CHARDEV(obj) OBJECT_CHECK(BaumChardev, (obj), TYPE_CHARDEV_BRAILLE) /* Let's assume NABCC by default */ enum way { @@ -223,7 +226,7 @@ static const uint8_t nabcc_translation[2][256] = { }; /* The guest OS has started discussing with us, finish initializing BrlAPI */ -static int baum_deferred_init(BaumDriverState *baum) +static int baum_deferred_init(BaumChardev *baum) { int tty = BRLAPI_TTY_DEFAULT; QemuConsole *con; @@ -253,9 +256,9 @@ static int baum_deferred_init(BaumDriverState *baum) } /* The serial port can receive more of our data */ -static void baum_accept_input(struct CharDriverState *chr) +static void baum_chr_accept_input(struct Chardev *chr) { - BaumDriverState *baum = chr->opaque; + BaumChardev *baum = BAUM_CHARDEV(chr); int room, first; if (!baum->out_buf_used) @@ -279,24 +282,25 @@ static void baum_accept_input(struct CharDriverState *chr) } /* We want to send a packet */ -static void baum_write_packet(BaumDriverState *baum, const uint8_t *buf, int len) +static void baum_write_packet(BaumChardev *baum, const uint8_t *buf, int len) { + Chardev *chr = CHARDEV(baum); uint8_t io_buf[1 + 2 * len], *cur = io_buf; int room; *cur++ = ESC; while (len--) if ((*cur++ = *buf++) == ESC) *cur++ = ESC; - room = qemu_chr_be_can_write(baum->chr); + room = qemu_chr_be_can_write(chr); len = cur - io_buf; if (len <= room) { /* Fits */ - qemu_chr_be_write(baum->chr, io_buf, len); + qemu_chr_be_write(chr, io_buf, len); } else { int first; uint8_t out; /* Can't fit all, send what can be, and store the rest. */ - qemu_chr_be_write(baum->chr, io_buf, room); + qemu_chr_be_write(chr, io_buf, room); len -= room; cur = io_buf + room; if (len > BUF_SIZE - baum->out_buf_used) { @@ -321,14 +325,14 @@ static void baum_write_packet(BaumDriverState *baum, const uint8_t *buf, int len /* Called when the other end seems to have a wrong idea of our display size */ static void baum_cellCount_timer_cb(void *opaque) { - BaumDriverState *baum = opaque; + BaumChardev *baum = BAUM_CHARDEV(opaque); uint8_t cell_count[] = { BAUM_RSP_CellCount, baum->x * baum->y }; DPRINTF("Timeout waiting for DisplayData, sending cell count\n"); baum_write_packet(baum, cell_count, sizeof(cell_count)); } /* Try to interpret a whole incoming packet */ -static int baum_eat_packet(BaumDriverState *baum, const uint8_t *buf, int len) +static int baum_eat_packet(BaumChardev *baum, const uint8_t *buf, int len) { const uint8_t *cur = buf; uint8_t req = 0; @@ -469,9 +473,9 @@ static int baum_eat_packet(BaumDriverState *baum, const uint8_t *buf, int len) } /* The other end is writing some data. Store it and try to interpret */ -static int baum_write(CharDriverState *chr, const uint8_t *buf, int len) +static int baum_chr_write(Chardev *chr, const uint8_t *buf, int len) { - BaumDriverState *baum = chr->opaque; + BaumChardev *baum = BAUM_CHARDEV(chr); int tocopy, cur, eaten, orig_len = len; if (!len) @@ -510,14 +514,16 @@ static int baum_write(CharDriverState *chr, const uint8_t *buf, int len) } /* Send the key code to the other end */ -static void baum_send_key(BaumDriverState *baum, uint8_t type, uint8_t value) { +static void baum_send_key(BaumChardev *baum, uint8_t type, uint8_t value) +{ uint8_t packet[] = { type, value }; DPRINTF("writing key %x %x\n", type, value); baum_write_packet(baum, packet, sizeof(packet)); } -static void baum_send_key2(BaumDriverState *baum, uint8_t type, uint8_t value, - uint8_t value2) { +static void baum_send_key2(BaumChardev *baum, uint8_t type, uint8_t value, + uint8_t value2) +{ uint8_t packet[] = { type, value, value2 }; DPRINTF("writing key %x %x\n", type, value); baum_write_packet(baum, packet, sizeof(packet)); @@ -526,7 +532,7 @@ static void baum_send_key2(BaumDriverState *baum, uint8_t type, uint8_t value, /* We got some data on the BrlAPI socket */ static void baum_chr_read(void *opaque) { - BaumDriverState *baum = opaque; + BaumChardev *baum = BAUM_CHARDEV(opaque); brlapi_keyCode_t code; int ret; if (!baum->brlapi) @@ -610,41 +616,25 @@ static void baum_chr_read(void *opaque) } } -static void baum_free(struct CharDriverState *chr) +static void baum_chr_free(Chardev *chr) { - BaumDriverState *baum = chr->opaque; + BaumChardev *baum = BAUM_CHARDEV(chr); timer_free(baum->cellCount_timer); if (baum->brlapi) { brlapi__closeConnection(baum->brlapi); g_free(baum->brlapi); } - g_free(baum); } -static CharDriverState *chr_baum_init(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, - bool *be_opened, - Error **errp) +static void baum_chr_open(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { - ChardevCommon *common = backend->u.braille.data; - BaumDriverState *baum; - CharDriverState *chr; + BaumChardev *baum = BAUM_CHARDEV(chr); brlapi_handle_t *handle; - chr = qemu_chr_alloc(common, errp); - if (!chr) { - return NULL; - } - baum = g_malloc0(sizeof(BaumDriverState)); - baum->chr = chr; - - chr->opaque = baum; - chr->chr_write = baum_write; - chr->chr_accept_input = baum_accept_input; - chr->chr_free = baum_free; - handle = g_malloc0(brlapi_getHandleSize()); baum->brlapi = handle; @@ -652,27 +642,41 @@ static CharDriverState *chr_baum_init(const char *id, if (baum->brlapi_fd == -1) { error_setg(errp, "brlapi__openConnection: %s", brlapi_strerror(brlapi_error_location())); - goto fail_handle; + g_free(handle); + return; } baum->deferred_init = 0; baum->cellCount_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, baum_cellCount_timer_cb, baum); qemu_set_fd_handler(baum->brlapi_fd, baum_chr_read, NULL, baum); +} - return chr; +static void char_braille_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); -fail_handle: - g_free(handle); - g_free(chr); - g_free(baum); - return NULL; + cc->open = baum_chr_open; + cc->chr_write = baum_chr_write; + cc->chr_accept_input = baum_chr_accept_input; + cc->chr_free = baum_chr_free; } +static const TypeInfo char_braille_type_info = { + .name = TYPE_CHARDEV_BRAILLE, + .parent = TYPE_CHARDEV, + .instance_size = sizeof(BaumChardev), + .class_init = char_braille_class_init, +}; + static void register_types(void) { - register_char_driver("braille", CHARDEV_BACKEND_KIND_BRAILLE, NULL, - chr_baum_init); + static const CharDriver driver = { + .kind = CHARDEV_BACKEND_KIND_BRAILLE, + }; + + register_char_driver(&driver); + type_register_static(&char_braille_type_info); } type_init(register_types); diff --git a/backends/msmouse.c b/backends/msmouse.c index 733ca80f48..936a5476d5 100644 --- a/backends/msmouse.c +++ b/backends/msmouse.c @@ -31,18 +31,23 @@ #define MSMOUSE_HI2(n) (((n) & 0xc0) >> 6) typedef struct { - CharDriverState *chr; + Chardev parent; + QemuInputHandlerState *hs; int axis[INPUT_AXIS__MAX]; bool btns[INPUT_BUTTON__MAX]; bool btnc[INPUT_BUTTON__MAX]; uint8_t outbuf[32]; int outlen; -} MouseState; +} MouseChardev; + +#define TYPE_CHARDEV_MSMOUSE "chardev-msmouse" +#define MOUSE_CHARDEV(obj) \ + OBJECT_CHECK(MouseChardev, (obj), TYPE_CHARDEV_MSMOUSE) -static void msmouse_chr_accept_input(CharDriverState *chr) +static void msmouse_chr_accept_input(Chardev *chr) { - MouseState *mouse = chr->opaque; + MouseChardev *mouse = MOUSE_CHARDEV(chr); int len; len = qemu_chr_be_can_write(chr); @@ -60,7 +65,7 @@ static void msmouse_chr_accept_input(CharDriverState *chr) } } -static void msmouse_queue_event(MouseState *mouse) +static void msmouse_queue_event(MouseChardev *mouse) { unsigned char bytes[4] = { 0x40, 0x00, 0x00, 0x00 }; int dx, dy, count = 3; @@ -97,7 +102,7 @@ static void msmouse_queue_event(MouseState *mouse) static void msmouse_input_event(DeviceState *dev, QemuConsole *src, InputEvent *evt) { - MouseState *mouse = (MouseState *)dev; + MouseChardev *mouse = MOUSE_CHARDEV(dev); InputMoveEvent *move; InputBtnEvent *btn; @@ -121,24 +126,24 @@ static void msmouse_input_event(DeviceState *dev, QemuConsole *src, static void msmouse_input_sync(DeviceState *dev) { - MouseState *mouse = (MouseState *)dev; + MouseChardev *mouse = MOUSE_CHARDEV(dev); + Chardev *chr = CHARDEV(dev); msmouse_queue_event(mouse); - msmouse_chr_accept_input(mouse->chr); + msmouse_chr_accept_input(chr); } -static int msmouse_chr_write (struct CharDriverState *s, const uint8_t *buf, int len) +static int msmouse_chr_write(struct Chardev *s, const uint8_t *buf, int len) { /* Ignore writes to mouse port */ return len; } -static void msmouse_chr_free(struct CharDriverState *chr) +static void msmouse_chr_free(struct Chardev *chr) { - MouseState *mouse = chr->opaque; + MouseChardev *mouse = MOUSE_CHARDEV(chr); qemu_input_handler_unregister(mouse->hs); - g_free(mouse); } static QemuInputHandler msmouse_handler = { @@ -148,39 +153,43 @@ static QemuInputHandler msmouse_handler = { .sync = msmouse_input_sync, }; -static CharDriverState *qemu_chr_open_msmouse(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, - bool *be_opened, - Error **errp) +static void msmouse_chr_open(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { - ChardevCommon *common = backend->u.msmouse.data; - MouseState *mouse; - CharDriverState *chr; + MouseChardev *mouse = MOUSE_CHARDEV(chr); - chr = qemu_chr_alloc(common, errp); - if (!chr) { - return NULL; - } - chr->chr_write = msmouse_chr_write; - chr->chr_free = msmouse_chr_free; - chr->chr_accept_input = msmouse_chr_accept_input; *be_opened = false; - - mouse = g_new0(MouseState, 1); mouse->hs = qemu_input_handler_register((DeviceState *)mouse, &msmouse_handler); +} - mouse->chr = chr; - chr->opaque = mouse; +static void char_msmouse_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); - return chr; + cc->open = msmouse_chr_open; + cc->chr_write = msmouse_chr_write; + cc->chr_accept_input = msmouse_chr_accept_input; + cc->chr_free = msmouse_chr_free; } +static const TypeInfo char_msmouse_type_info = { + .name = TYPE_CHARDEV_MSMOUSE, + .parent = TYPE_CHARDEV, + .instance_size = sizeof(MouseChardev), + .class_init = char_msmouse_class_init, +}; + static void register_types(void) { - register_char_driver("msmouse", CHARDEV_BACKEND_KIND_MSMOUSE, NULL, - qemu_chr_open_msmouse); + static const CharDriver driver = { + .kind = CHARDEV_BACKEND_KIND_MSMOUSE, + }; + + register_char_driver(&driver); + type_register_static(&char_msmouse_type_info); } type_init(register_types); diff --git a/backends/rng-egd.c b/backends/rng-egd.c index 69c04b1b26..380b19a0a1 100644 --- a/backends/rng-egd.c +++ b/backends/rng-egd.c @@ -86,7 +86,7 @@ static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size) static void rng_egd_opened(RngBackend *b, Error **errp) { RngEgd *s = RNG_EGD(b); - CharDriverState *chr; + Chardev *chr; if (s->chr_name == NULL) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, @@ -125,7 +125,7 @@ static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp) static char *rng_egd_get_chardev(Object *obj, Error **errp) { RngEgd *s = RNG_EGD(obj); - CharDriverState *chr = qemu_chr_fe_get_driver(&s->chr); + Chardev *chr = qemu_chr_fe_get_driver(&s->chr); if (chr && chr->label) { return g_strdup(chr->label); diff --git a/backends/testdev.c b/backends/testdev.c index 60156e320e..ea15143713 100644 --- a/backends/testdev.c +++ b/backends/testdev.c @@ -30,13 +30,18 @@ #define BUF_SIZE 32 typedef struct { - CharDriverState *chr; + Chardev parent; + uint8_t in_buf[32]; int in_buf_used; -} TestdevCharState; +} TestdevChardev; + +#define TYPE_CHARDEV_TESTDEV "chardev-testdev" +#define TESTDEV_CHARDEV(obj) \ + OBJECT_CHECK(TestdevChardev, (obj), TYPE_CHARDEV_TESTDEV) /* Try to interpret a whole incoming packet */ -static int testdev_eat_packet(TestdevCharState *testdev) +static int testdev_eat_packet(TestdevChardev *testdev) { const uint8_t *cur = testdev->in_buf; int len = testdev->in_buf_used; @@ -77,9 +82,9 @@ static int testdev_eat_packet(TestdevCharState *testdev) } /* 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) +static int testdev_chr_write(Chardev *chr, const uint8_t *buf, int len) { - TestdevCharState *testdev = chr->opaque; + TestdevChardev *testdev = TESTDEV_CHARDEV(chr); int tocopy, eaten, orig_len = len; while (len) { @@ -102,36 +107,28 @@ static int testdev_write(CharDriverState *chr, const uint8_t *buf, int len) return orig_len; } -static void testdev_free(struct CharDriverState *chr) +static void char_testdev_class_init(ObjectClass *oc, void *data) { - TestdevCharState *testdev = chr->opaque; + ChardevClass *cc = CHARDEV_CLASS(oc); - g_free(testdev); + cc->chr_write = testdev_chr_write; } -static CharDriverState *chr_testdev_init(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, - bool *be_opened, - Error **errp) -{ - TestdevCharState *testdev; - CharDriverState *chr; - - testdev = g_new0(TestdevCharState, 1); - testdev->chr = chr = g_new0(CharDriverState, 1); - - chr->opaque = testdev; - chr->chr_write = testdev_write; - chr->chr_free = testdev_free; - - return chr; -} +static const TypeInfo char_testdev_type_info = { + .name = TYPE_CHARDEV_TESTDEV, + .parent = TYPE_CHARDEV, + .instance_size = sizeof(TestdevChardev), + .class_init = char_testdev_class_init, +}; static void register_types(void) { - register_char_driver("testdev", CHARDEV_BACKEND_KIND_TESTDEV, NULL, - chr_testdev_init); + static const CharDriver driver = { + .kind = CHARDEV_BACKEND_KIND_TESTDEV, + }; + + register_char_driver(&driver); + type_register_static(&char_testdev_type_info); } type_init(register_types); diff --git a/block/Makefile.objs b/block/Makefile.objs index 0b8fd06f27..c6bd14e883 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -14,6 +14,7 @@ block-obj-y += throttle-groups.o block-obj-y += nbd.o nbd-client.o sheepdog.o block-obj-$(CONFIG_LIBISCSI) += iscsi.o +block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o block-obj-$(CONFIG_LIBNFS) += nfs.o block-obj-$(CONFIG_CURL) += curl.o block-obj-$(CONFIG_RBD) += rbd.o diff --git a/block/file-posix.c b/block/file-posix.c index 28b47d977b..2134e0ef96 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -651,12 +651,15 @@ static void raw_reopen_abort(BDRVReopenState *state) state->opaque = NULL; } -static int hdev_get_max_transfer_length(int fd) +static int hdev_get_max_transfer_length(BlockDriverState *bs, int fd) { #ifdef BLKSECTGET - int max_sectors = 0; - if (ioctl(fd, BLKSECTGET, &max_sectors) == 0) { - return max_sectors; + int max_bytes = 0; + short max_sectors = 0; + if (bs->sg && ioctl(fd, BLKSECTGET, &max_bytes) == 0) { + return max_bytes; + } else if (!bs->sg && ioctl(fd, BLKSECTGET, &max_sectors) == 0) { + return max_sectors << BDRV_SECTOR_BITS; } else { return -errno; } @@ -671,10 +674,10 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) struct stat st; if (!fstat(s->fd, &st)) { - if (S_ISBLK(st.st_mode)) { - int ret = hdev_get_max_transfer_length(s->fd); - if (ret > 0 && ret <= BDRV_REQUEST_MAX_SECTORS) { - bs->bl.max_transfer = pow2floor(ret << BDRV_SECTOR_BITS); + if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) { + int ret = hdev_get_max_transfer_length(bs, s->fd); + if (ret > 0 && ret <= BDRV_REQUEST_MAX_BYTES) { + bs->bl.max_transfer = pow2floor(ret); } } } diff --git a/block/iscsi-opts.c b/block/iscsi-opts.c new file mode 100644 index 0000000000..5335539130 --- /dev/null +++ b/block/iscsi-opts.c @@ -0,0 +1,69 @@ +/* + * QEMU Block driver for iSCSI images (static options) + * + * Copyright (c) 2017 Peter Lieven <pl@kamp.de> + * + * 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/osdep.h" +#include "qemu-common.h" +#include "qemu/config-file.h" + +static QemuOptsList qemu_iscsi_opts = { + .name = "iscsi", + .head = QTAILQ_HEAD_INITIALIZER(qemu_iscsi_opts.head), + .desc = { + { + .name = "user", + .type = QEMU_OPT_STRING, + .help = "username for CHAP authentication to target", + },{ + .name = "password", + .type = QEMU_OPT_STRING, + .help = "password for CHAP authentication to target", + },{ + .name = "password-secret", + .type = QEMU_OPT_STRING, + .help = "ID of the secret providing password for CHAP " + "authentication to target", + },{ + .name = "header-digest", + .type = QEMU_OPT_STRING, + .help = "HeaderDigest setting. " + "{CRC32C|CRC32C-NONE|NONE-CRC32C|NONE}", + },{ + .name = "initiator-name", + .type = QEMU_OPT_STRING, + .help = "Initiator iqn name to use when connecting", + },{ + .name = "timeout", + .type = QEMU_OPT_NUMBER, + .help = "Request timeout in seconds (default 0 = no timeout)", + }, + { /* end of list */ } + }, +}; + +static void iscsi_block_opts_init(void) +{ + qemu_add_opts(&qemu_iscsi_opts); +} + +block_init(iscsi_block_opts_init); diff --git a/block/iscsi.c b/block/iscsi.c index 6aeeb9ec4f..1860f1bc91 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -499,14 +499,18 @@ iscsi_allocmap_update(IscsiLun *iscsilun, int64_t sector_num, if (allocated) { bitmap_set(iscsilun->allocmap, cl_num_expanded, nb_cls_expanded); } else { - bitmap_clear(iscsilun->allocmap, cl_num_shrunk, nb_cls_shrunk); + if (nb_cls_shrunk > 0) { + bitmap_clear(iscsilun->allocmap, cl_num_shrunk, nb_cls_shrunk); + } } if (iscsilun->allocmap_valid == NULL) { return; } if (valid) { - bitmap_set(iscsilun->allocmap_valid, cl_num_shrunk, nb_cls_shrunk); + if (nb_cls_shrunk > 0) { + bitmap_set(iscsilun->allocmap_valid, cl_num_shrunk, nb_cls_shrunk); + } } else { bitmap_clear(iscsilun->allocmap_valid, cl_num_expanded, nb_cls_expanded); diff --git a/cpu-exec.c b/cpu-exec.c index 4188fed3c6..fa08c733da 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -508,8 +508,8 @@ static inline void cpu_handle_interrupt(CPUState *cpu, True when it is, and we should restart on a new TB, and via longjmp via cpu_loop_exit. */ else { - replay_interrupt(); if (cc->cpu_exec_interrupt(cpu, interrupt_request)) { + replay_interrupt(); *last_tb = NULL; } /* The target hook may have updated the 'cpu->interrupt_request'; diff --git a/docs/replay.txt b/docs/replay.txt index 347b2ff055..03e193193f 100644 --- a/docs/replay.txt +++ b/docs/replay.txt @@ -196,6 +196,22 @@ is recorded to the log. In replay phase the queue is matched with events read from the log. Therefore block devices requests are processed deterministically. +Snapshotting +------------ + +New VM snapshots may be created in replay mode. They can be used later +to recover the desired VM state. All VM states created in replay mode +are associated with the moment of time in the replay scenario. +After recovering the VM state replay will start from that position. + +Default starting snapshot name may be specified with icount field +rrsnapshot as follows: + -icount shift=7,rr=record,rrfile=replay.bin,rrsnapshot=snapshot_name + +This snapshot is created at start of recording and restored at start +of replaying. It also can be loaded while replaying to roll back +the execution. + Network devices --------------- @@ -2630,7 +2630,7 @@ static MemTxResult address_space_write_continue(AddressSpace *as, hwaddr addr, break; case 4: /* 32 bit write access */ - val = ldl_p(buf); + val = (uint32_t)ldl_p(buf); result |= memory_region_dispatch_write(mr, addr1, val, 4, attrs); break; @@ -305,7 +305,7 @@ typedef struct GDBState { int running_state; #else CharBackend chr; - CharDriverState *mon_chr; + Chardev *mon_chr; #endif char syscall_buf[256]; gdb_syscall_complete_cb current_syscall_cb; @@ -1473,7 +1473,7 @@ void gdb_exit(CPUArchState *env, int code) GDBState *s; char buf[4]; #ifndef CONFIG_USER_ONLY - CharDriverState *chr; + Chardev *chr; #endif s = gdbserver_state; @@ -1698,7 +1698,7 @@ static void gdb_monitor_output(GDBState *s, const char *msg, int len) put_packet(s, buf); } -static int gdb_monitor_write(CharDriverState *chr, const uint8_t *buf, int len) +static int gdb_monitor_write(Chardev *chr, const uint8_t *buf, int len) { const char *p = (const char *)buf; int max_sz; @@ -1725,13 +1725,35 @@ static void gdb_sigterm_handler(int signal) } #endif +static void gdb_monitor_open(Chardev *chr, ChardevBackend *backend, + bool *be_opened, Error **errp) +{ + *be_opened = false; +} + +static void char_gdb_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->internal = true; + cc->open = gdb_monitor_open; + cc->chr_write = gdb_monitor_write; +} + +#define TYPE_CHARDEV_GDB "chardev-gdb" + +static const TypeInfo char_gdb_type_info = { + .name = TYPE_CHARDEV_GDB, + .parent = TYPE_CHARDEV, + .class_init = char_gdb_class_init, +}; + int gdbserver_start(const char *device) { GDBState *s; char gdbstub_device_name[128]; - CharDriverState *chr = NULL; - CharDriverState *mon_chr; - ChardevCommon common = { 0 }; + Chardev *chr = NULL; + Chardev *mon_chr; if (!first_cpu) { error_report("gdbstub: meaningless to attach gdb to a " @@ -1770,8 +1792,8 @@ int gdbserver_start(const char *device) qemu_add_vm_change_state_handler(gdb_vm_state_change, NULL); /* Initialize a monitor terminal for gdb */ - mon_chr = qemu_chr_alloc(&common, &error_abort); - mon_chr->chr_write = gdb_monitor_write; + mon_chr = qemu_chardev_new(NULL, TYPE_CHARDEV_GDB, + NULL, &error_abort); monitor_init(mon_chr, 0); } else { if (qemu_chr_fe_get_driver(&s->chr)) { @@ -1794,4 +1816,11 @@ int gdbserver_start(const char *device) return 0; } + +static void register_types(void) +{ + type_register_static(&char_gdb_type_info); +} + +type_init(register_types); #endif diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 55d50c42c6..b0f35e6829 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -249,9 +249,9 @@ ETEXI { .name = "mtree", - .args_type = "", - .params = "", - .help = "show memory tree", + .args_type = "flatview:-f", + .params = "[-f]", + .help = "show memory tree (-f: dump flat view for address spaces)", .cmd = hmp_info_mtree, }, diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index 7bb7be76b6..2126f73ca0 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -118,7 +118,7 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) }; if (i < MAX_SERIAL_PORTS) { - CharDriverState *chr; + Chardev *chr; chr = serial_hds[i]; diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index f23672b222..dd1c713ae3 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -107,7 +107,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) }; if (i < MAX_SERIAL_PORTS) { - CharDriverState *chr; + Chardev *chr; chr = serial_hds[i]; diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index e93532fb57..76dd8a48ca 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -187,7 +187,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) }; if (i < MAX_SERIAL_PORTS) { - CharDriverState *chr; + Chardev *chr; chr = serial_hds[i]; diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c index c86cf80514..503a3b6d24 100644 --- a/hw/arm/nseries.c +++ b/hw/arm/nseries.c @@ -786,7 +786,7 @@ static void n8x0_cbus_setup(struct n800_s *s) static void n8x0_uart_setup(struct n800_s *s) { - CharDriverState *radio = uart_hci_init(); + Chardev *radio = uart_hci_init(); qdev_connect_gpio_out(s->mpu->gpio, N8X0_BT_RESET_GPIO, csrhci_pins_get(radio)[csrhci_pin_reset]); diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c index 6f05c98d3e..cf1b4ba58f 100644 --- a/hw/arm/omap2.c +++ b/hw/arm/omap2.c @@ -792,7 +792,7 @@ static const MemoryRegionOps omap_sti_fifo_ops = { static struct omap_sti_s *omap_sti_init(struct omap_target_agent_s *ta, MemoryRegion *sysmem, hwaddr channel_base, qemu_irq irq, omap_clk clk, - CharDriverState *chr) + Chardev *chr) { struct omap_sti_s *s = g_new0(struct omap_sti_s, 1); diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index d31b4577f0..cfee3929d9 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -2024,7 +2024,7 @@ static PXA2xxFIrState *pxa2xx_fir_init(MemoryRegion *sysmem, hwaddr base, qemu_irq irq, qemu_irq rx_dma, qemu_irq tx_dma, - CharDriverState *chr) + Chardev *chr) { DeviceState *dev; SysBusDevice *sbd; diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 6c9e8985bf..1f216cf3b1 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -613,7 +613,7 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic) } static void create_uart(const VirtMachineState *vms, qemu_irq *pic, int uart, - MemoryRegion *mem, CharDriverState *chr) + MemoryRegion *mem, Chardev *chr) { char *nodename; hwaddr base = vms->memmap[uart].base; diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c index fbb3109cc1..3c193848fc 100644 --- a/hw/bt/hci-csr.c +++ b/hw/bt/hci-csr.c @@ -26,13 +26,14 @@ #include "hw/irq.h" #include "sysemu/bt.h" #include "hw/bt.h" +#include "qapi/error.h" struct csrhci_s { + Chardev parent; int enable; qemu_irq *pins; int pin_state; int modem_state; - CharDriverState chr; #define FIFO_LEN 4096 int out_start; int out_len; @@ -54,6 +55,9 @@ struct csrhci_s { struct HCIInfo *hci; }; +#define TYPE_CHARDEV_HCI "chardev-hci" +#define HCI_CHARDEV(obj) OBJECT_CHECK(struct csrhci_s, (obj), TYPE_CHARDEV_HCI) + /* H4+ packet types */ enum { H4_CMD_PKT = 1, @@ -78,7 +82,8 @@ enum { static inline void csrhci_fifo_wake(struct csrhci_s *s) { - CharBackend *be = s->chr.be; + Chardev *chr = (Chardev *)s; + CharBackend *be = chr->be; if (!s->enable || !s->out_len) return; @@ -311,10 +316,10 @@ static void csrhci_ready_for_next_inpkt(struct csrhci_s *s) s->in_hdr = INT_MAX; } -static int csrhci_write(struct CharDriverState *chr, +static int csrhci_write(struct Chardev *chr, const uint8_t *buf, int len) { - struct csrhci_s *s = (struct csrhci_s *) chr->opaque; + struct csrhci_s *s = (struct csrhci_s *)chr; int total = 0; if (!s->enable) @@ -384,10 +389,10 @@ static void csrhci_out_hci_packet_acl(void *opaque, csrhci_fifo_wake(s); } -static int csrhci_ioctl(struct CharDriverState *chr, int cmd, void *arg) +static int csrhci_ioctl(struct Chardev *chr, int cmd, void *arg) { QEMUSerialSetParams *ssp; - struct csrhci_s *s = (struct csrhci_s *) chr->opaque; + struct csrhci_s *s = (struct csrhci_s *) chr; int prev_state = s->modem_state; switch (cmd) { @@ -453,21 +458,19 @@ static void csrhci_pins(void *opaque, int line, int level) } } -qemu_irq *csrhci_pins_get(CharDriverState *chr) +qemu_irq *csrhci_pins_get(Chardev *chr) { - struct csrhci_s *s = (struct csrhci_s *) chr->opaque; + struct csrhci_s *s = (struct csrhci_s *) chr; return s->pins; } -CharDriverState *uart_hci_init(void) +static void csrhci_open(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { - struct csrhci_s *s = (struct csrhci_s *) - g_malloc0(sizeof(struct csrhci_s)); - - s->chr.opaque = s; - s->chr.chr_write = csrhci_write; - s->chr.chr_ioctl = csrhci_ioctl; + struct csrhci_s *s = HCI_CHARDEV(chr); s->hci = qemu_next_hci(); s->hci->opaque = s; @@ -477,6 +480,35 @@ CharDriverState *uart_hci_init(void) s->out_tm = timer_new_ns(QEMU_CLOCK_VIRTUAL, csrhci_out_tick, s); s->pins = qemu_allocate_irqs(csrhci_pins, s, __csrhci_pins); csrhci_reset(s); + *be_opened = false; +} + +static void char_hci_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->internal = true; + cc->open = csrhci_open; + cc->chr_write = csrhci_write; + cc->chr_ioctl = csrhci_ioctl; +} - return &s->chr; +static const TypeInfo char_hci_type_info = { + .name = TYPE_CHARDEV_HCI, + .parent = TYPE_CHARDEV, + .instance_size = sizeof(struct csrhci_s), + .class_init = char_hci_class_init, +}; + +Chardev *uart_hci_init(void) +{ + return qemu_chardev_new(NULL, TYPE_CHARDEV_HCI, + NULL, &error_abort); } + +static void register_types(void) +{ + type_register_static(&char_hci_type_info); +} + +type_init(register_types); diff --git a/hw/char/escc.c b/hw/char/escc.c index d6662dc77d..9228091cec 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -689,7 +689,7 @@ static const VMStateDescription vmstate_escc = { }; MemoryRegion *escc_init(hwaddr base, qemu_irq irqA, qemu_irq irqB, - CharDriverState *chrA, CharDriverState *chrB, + Chardev *chrA, Chardev *chrB, int clock, int it_shift) { DeviceState *dev; diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index 0cd3dd3958..7c16e894e2 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -582,7 +582,7 @@ static const VMStateDescription vmstate_exynos4210_uart = { DeviceState *exynos4210_uart_create(hwaddr addr, int fifo_size, int channel, - CharDriverState *chr, + Chardev *chr, qemu_irq irq) { DeviceState *dev; diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index 99545fc359..52e67f8dc9 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -170,7 +170,7 @@ static void imx_serial_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { IMXSerialState *s = (IMXSerialState *)opaque; - CharDriverState *chr = qemu_chr_fe_get_driver(&s->chr); + Chardev *chr = qemu_chr_fe_get_driver(&s->chr); unsigned char ch; DPRINTF("write(offset=0x%" HWADDR_PRIx ", value = 0x%x) to %s\n", diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c index ecaa091190..80c380e077 100644 --- a/hw/char/mcf_uart.c +++ b/hw/char/mcf_uart.c @@ -275,7 +275,7 @@ static void mcf_uart_receive(void *opaque, const uint8_t *buf, int size) mcf_uart_push_byte(s, buf[0]); } -void *mcf_uart_init(qemu_irq irq, CharDriverState *chr) +void *mcf_uart_init(qemu_irq irq, Chardev *chr) { mcf_uart_state *s; @@ -300,7 +300,7 @@ static const MemoryRegionOps mcf_uart_ops = { void mcf_uart_mm_init(MemoryRegion *sysmem, hwaddr base, qemu_irq irq, - CharDriverState *chr) + Chardev *chr) { mcf_uart_state *s; diff --git a/hw/char/omap_uart.c b/hw/char/omap_uart.c index 893ab108bc..31ebb1592c 100644 --- a/hw/char/omap_uart.c +++ b/hw/char/omap_uart.c @@ -54,7 +54,7 @@ void omap_uart_reset(struct omap_uart_s *s) struct omap_uart_s *omap_uart_init(hwaddr base, qemu_irq irq, omap_clk fclk, omap_clk iclk, qemu_irq txdma, qemu_irq rxdma, - const char *label, CharDriverState *chr) + const char *label, Chardev *chr) { struct omap_uart_s *s = g_new0(struct omap_uart_s, 1); @@ -163,7 +163,7 @@ struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem, struct omap_target_agent_s *ta, qemu_irq irq, omap_clk fclk, omap_clk iclk, qemu_irq txdma, qemu_irq rxdma, - const char *label, CharDriverState *chr) + const char *label, Chardev *chr) { hwaddr base = omap_l4_attach(ta, 0, NULL); struct omap_uart_s *s = omap_uart_init(base, irq, @@ -178,7 +178,7 @@ struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem, return s; } -void omap_uart_attach(struct omap_uart_s *s, CharDriverState *chr) +void omap_uart_attach(struct omap_uart_s *s, Chardev *chr) { /* TODO: Should reuse or destroy current s->serial */ s->serial = serial_mm_init(get_system_memory(), s->base, 2, s->irq, diff --git a/hw/char/parallel.c b/hw/char/parallel.c index f2d56666b7..c71a4a0f4f 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -603,7 +603,7 @@ static const MemoryRegionOps parallel_mm_ops = { /* If fd is zero, it means that the parallel device uses the console */ bool parallel_mm_init(MemoryRegion *address_space, hwaddr base, int it_shift, qemu_irq irq, - CharDriverState *chr) + Chardev *chr) { ParallelState *s; diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c index 54d3a12f51..d7c5cc11fe 100644 --- a/hw/char/serial-isa.c +++ b/hw/char/serial-isa.c @@ -121,7 +121,7 @@ static void serial_register_types(void) type_init(serial_register_types) -static void serial_isa_init(ISABus *bus, int index, CharDriverState *chr) +static void serial_isa_init(ISABus *bus, int index, Chardev *chr) { DeviceState *dev; ISADevice *isadev; diff --git a/hw/char/serial.c b/hw/char/serial.c index 67b18eda12..03d890ca24 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -937,7 +937,7 @@ const MemoryRegionOps serial_io_ops = { }; SerialState *serial_init(int base, qemu_irq irq, int baudbase, - CharDriverState *chr, MemoryRegion *system_io) + Chardev *chr, MemoryRegion *system_io) { SerialState *s; @@ -993,7 +993,7 @@ static const MemoryRegionOps serial_mm_ops[3] = { SerialState *serial_mm_init(MemoryRegion *address_space, hwaddr base, int it_shift, qemu_irq irq, int baudbase, - CharDriverState *chr, enum device_endian end) + Chardev *chr, enum device_endian end) { SerialState *s; diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 9d35564bcf..303eb0a678 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -356,7 +356,7 @@ static const MemoryRegionOps sh_serial_ops = { void sh_serial_init(MemoryRegion *sysmem, hwaddr base, int feat, - uint32_t freq, CharDriverState *chr, + uint32_t freq, Chardev *chr, qemu_irq eri_source, qemu_irq rxi_source, qemu_irq txi_source, diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c index 7c22b8bd0e..e30c8da57c 100644 --- a/hw/char/spapr_vty.c +++ b/hw/char/spapr_vty.c @@ -141,7 +141,7 @@ static target_ulong h_get_term_char(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_SUCCESS; } -void spapr_vty_create(VIOsPAPRBus *bus, CharDriverState *chardev) +void spapr_vty_create(VIOsPAPRBus *bus, Chardev *chardev) { DeviceState *dev; diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c index 776205b4a9..798d9b69fd 100644 --- a/hw/char/virtio-console.c +++ b/hw/char/virtio-console.c @@ -168,7 +168,7 @@ static void virtconsole_realize(DeviceState *dev, Error **errp) VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev); VirtConsole *vcon = VIRTIO_CONSOLE(dev); VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(dev); - CharDriverState *chr = qemu_chr_fe_get_driver(&vcon->chr); + Chardev *chr = qemu_chr_fe_get_driver(&vcon->chr); if (port->id == 0 && !k->is_console) { error_setg(errp, "Port number 0 on virtio-serial devices reserved " diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 1b7ea50e9f..94f4d8bde4 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -179,7 +179,7 @@ static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque, Error *local_err = NULL; Property *prop = opaque; CharBackend *be = qdev_get_prop_ptr(dev, prop); - CharDriverState *s; + Chardev *s; char *str; if (dev->realized) { @@ -411,7 +411,7 @@ void qdev_prop_set_drive(DeviceState *dev, const char *name, } void qdev_prop_set_chr(DeviceState *dev, const char *name, - CharDriverState *value) + Chardev *value) { assert(!value || value->label); object_property_set_str(OBJECT(dev), diff --git a/hw/cpu/core.c b/hw/cpu/core.c index eff90c12be..2bf960d6a8 100644 --- a/hw/cpu/core.c +++ b/hw/cpu/core.c @@ -72,10 +72,18 @@ static void cpu_core_instance_init(Object *obj) core->nr_threads = smp_threads; } +static void cpu_core_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + set_bit(DEVICE_CATEGORY_CPU, dc->categories); +} + static const TypeInfo cpu_core_type_info = { .name = TYPE_CPU_CORE, .parent = TYPE_DEVICE, .abstract = true, + .class_init = cpu_core_class_init, .instance_size = sizeof(CPUCore), .instance_init = cpu_core_instance_init, }; diff --git a/hw/display/milkymist-tmu2.c b/hw/display/milkymist-tmu2.c index 920374b985..7528665510 100644 --- a/hw/display/milkymist-tmu2.c +++ b/hw/display/milkymist-tmu2.c @@ -85,7 +85,7 @@ struct MilkymistTMU2State { SysBusDevice parent_obj; MemoryRegion regs_region; - CharDriverState *chr; + Chardev *chr; qemu_irq irq; uint32_t regs[R_MAX]; diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 5f71012108..040a0b93f2 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -1392,7 +1392,7 @@ static const GraphicHwOps sm501_ops = { }; void sm501_init(MemoryRegion *address_space_mem, uint32_t base, - uint32_t local_mem_bytes, qemu_irq irq, CharDriverState *chr) + uint32_t local_mem_bytes, qemu_irq irq, Chardev *chr) { SM501State * s; DeviceState *dev; diff --git a/hw/i386/kvmvapic.c b/hw/i386/kvmvapic.c index 702e281dc8..7135633863 100644 --- a/hw/i386/kvmvapic.c +++ b/hw/i386/kvmvapic.c @@ -413,6 +413,12 @@ static void patch_instruction(VAPICROMState *s, X86CPU *cpu, target_ulong ip) if (!kvm_enabled()) { cpu_get_tb_cpu_state(env, ¤t_pc, ¤t_cs_base, ¤t_flags); + /* Account this instruction, because we will exit the tb. + This is the first instruction in the block. Therefore + there is no need in restoring CPU state. */ + if (use_icount) { + --cs->icount_decr.u16.low; + } } pause_all_vcpus(); diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index 3945dfd7b9..17df24c9d0 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -385,6 +385,25 @@ static bool apic_common_sipi_needed(void *opaque) return s->wait_for_sipi != 0; } +static bool apic_irq_delivered_needed(void *opaque) +{ + APICCommonState *s = APIC_COMMON(opaque); + return s->cpu == X86_CPU(first_cpu) && apic_irq_delivered != 0; +} + +static void apic_irq_delivered_pre_save(void *opaque) +{ + APICCommonState *s = APIC_COMMON(opaque); + s->apic_irq_delivered = apic_irq_delivered; +} + +static int apic_irq_delivered_post_load(void *opaque, int version_id) +{ + APICCommonState *s = APIC_COMMON(opaque); + apic_irq_delivered = s->apic_irq_delivered; + return 0; +} + static const VMStateDescription vmstate_apic_common_sipi = { .name = "apic_sipi", .version_id = 1, @@ -397,6 +416,19 @@ static const VMStateDescription vmstate_apic_common_sipi = { } }; +static const VMStateDescription vmstate_apic_irq_delivered = { + .name = "apic_irq_delivered", + .version_id = 1, + .minimum_version_id = 1, + .needed = apic_irq_delivered_needed, + .pre_save = apic_irq_delivered_pre_save, + .post_load = apic_irq_delivered_post_load, + .fields = (VMStateField[]) { + VMSTATE_INT32(apic_irq_delivered, APICCommonState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_apic_common = { .name = "apic", .version_id = 3, @@ -431,6 +463,7 @@ static const VMStateDescription vmstate_apic_common = { }, .subsections = (const VMStateDescription*[]) { &vmstate_apic_common_sipi, + &vmstate_apic_irq_delivered, NULL } }; diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c index 0ffbc8dd28..348e0eab9d 100644 --- a/hw/isa/isa-bus.c +++ b/hw/isa/isa-bus.c @@ -288,7 +288,7 @@ MemoryRegion *isa_address_space_io(ISADevice *dev) type_init(isabus_register_types) -static void parallel_init(ISABus *bus, int index, CharDriverState *chr) +static void parallel_init(ISABus *bus, int index, Chardev *chr) { DeviceState *dev; ISADevice *isadev; diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 10d1ee8b93..59930dd9d0 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -48,6 +48,8 @@ #include "exec/address-spaces.h" #include "sysemu/sysemu.h" #include "qom/cpu.h" +#include "hw/nvram/fw_cfg.h" +#include "qemu/cutils.h" /*****************************************************************************/ /* ICH9 LPC PCI to ISA bridge */ @@ -360,13 +362,62 @@ static void ich9_set_sci(void *opaque, int irq_num, int level) } } +static void smi_features_ok_callback(void *opaque) +{ + ICH9LPCState *lpc = opaque; + uint64_t guest_features; + + if (lpc->smi_features_ok) { + /* negotiation already complete, features locked */ + return; + } + + memcpy(&guest_features, lpc->smi_guest_features_le, sizeof guest_features); + le64_to_cpus(&guest_features); + if (guest_features & ~lpc->smi_host_features) { + /* guest requests invalid features, leave @features_ok at zero */ + return; + } + + /* valid feature subset requested, lock it down, report success */ + lpc->smi_negotiated_features = guest_features; + lpc->smi_features_ok = 1; +} + void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool smm_enabled) { ICH9LPCState *lpc = ICH9_LPC_DEVICE(lpc_pci); qemu_irq sci_irq; + FWCfgState *fw_cfg = fw_cfg_find(); sci_irq = qemu_allocate_irq(ich9_set_sci, lpc, 0); ich9_pm_init(lpc_pci, &lpc->pm, smm_enabled, sci_irq); + + if (lpc->smi_host_features && fw_cfg) { + uint64_t host_features_le; + + host_features_le = cpu_to_le64(lpc->smi_host_features); + memcpy(lpc->smi_host_features_le, &host_features_le, + sizeof host_features_le); + fw_cfg_add_file(fw_cfg, "etc/smi/supported-features", + lpc->smi_host_features_le, + sizeof lpc->smi_host_features_le); + + /* The other two guest-visible fields are cleared on device reset, we + * just link them into fw_cfg here. + */ + fw_cfg_add_file_callback(fw_cfg, "etc/smi/requested-features", + NULL, NULL, + lpc->smi_guest_features_le, + sizeof lpc->smi_guest_features_le, + false); + fw_cfg_add_file_callback(fw_cfg, "etc/smi/features-ok", + smi_features_ok_callback, lpc, + &lpc->smi_features_ok, + sizeof lpc->smi_features_ok, + true); + } + ich9_lpc_reset(&lpc->d.qdev); } @@ -386,7 +437,15 @@ static void ich9_apm_ctrl_changed(uint32_t val, void *arg) /* SMI_EN = PMBASE + 30. SMI control and enable register */ if (lpc->pm.smi_en & ICH9_PMIO_SMI_EN_APMC_EN) { - cpu_interrupt(current_cpu, CPU_INTERRUPT_SMI); + if (lpc->smi_negotiated_features & + (UINT64_C(1) << ICH9_LPC_SMI_F_BROADCAST_BIT)) { + CPUState *cs; + CPU_FOREACH(cs) { + cpu_interrupt(cs, CPU_INTERRUPT_SMI); + } + } else { + cpu_interrupt(current_cpu, CPU_INTERRUPT_SMI); + } } } @@ -507,6 +566,10 @@ static void ich9_lpc_reset(DeviceState *qdev) lpc->sci_level = 0; lpc->rst_cnt = 0; + + memset(lpc->smi_guest_features_le, 0, sizeof lpc->smi_guest_features_le); + lpc->smi_features_ok = 0; + lpc->smi_negotiated_features = 0; } /* root complex register block is mapped into memory space */ @@ -668,6 +731,29 @@ static const VMStateDescription vmstate_ich9_rst_cnt = { } }; +static bool ich9_smi_feat_needed(void *opaque) +{ + ICH9LPCState *lpc = opaque; + + return !buffer_is_zero(lpc->smi_guest_features_le, + sizeof lpc->smi_guest_features_le) || + lpc->smi_features_ok; +} + +static const VMStateDescription vmstate_ich9_smi_feat = { + .name = "ICH9LPC/smi_feat", + .version_id = 1, + .minimum_version_id = 1, + .needed = ich9_smi_feat_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT8_ARRAY(smi_guest_features_le, ICH9LPCState, + sizeof(uint64_t)), + VMSTATE_UINT8(smi_features_ok, ICH9LPCState), + VMSTATE_UINT64(smi_negotiated_features, ICH9LPCState), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_ich9_lpc = { .name = "ICH9LPC", .version_id = 1, @@ -683,12 +769,15 @@ static const VMStateDescription vmstate_ich9_lpc = { }, .subsections = (const VMStateDescription*[]) { &vmstate_ich9_rst_cnt, + &vmstate_ich9_smi_feat, NULL } }; static Property ich9_lpc_properties[] = { DEFINE_PROP_BOOL("noreboot", ICH9LPCState, pin_strap.spkr_hi, true), + DEFINE_PROP_BIT64("x-smi-broadcast", ICH9LPCState, smi_host_features, + ICH9_LPC_SMI_F_BROADCAST_BIT, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/isa/pc87312.c b/hw/isa/pc87312.c index b1c1a0acb1..c707d24db4 100644 --- a/hw/isa/pc87312.c +++ b/hw/isa/pc87312.c @@ -268,7 +268,7 @@ static void pc87312_realize(DeviceState *dev, Error **errp) DeviceState *d; ISADevice *isa; ISABus *bus; - CharDriverState *chr; + Chardev *chr; DriveInfo *drive; char name[5]; int i; diff --git a/hw/lm32/lm32.h b/hw/lm32/lm32.h index db9eb29ea4..d1514a61b3 100644 --- a/hw/lm32/lm32.h +++ b/hw/lm32/lm32.h @@ -16,7 +16,7 @@ static inline DeviceState *lm32_pic_init(qemu_irq cpu_irq) return dev; } -static inline DeviceState *lm32_juart_init(CharDriverState *chr) +static inline DeviceState *lm32_juart_init(Chardev *chr) { DeviceState *dev; @@ -29,7 +29,7 @@ static inline DeviceState *lm32_juart_init(CharDriverState *chr) static inline DeviceState *lm32_uart_create(hwaddr addr, qemu_irq irq, - CharDriverState *chr) + Chardev *chr) { DeviceState *dev; SysBusDevice *s; diff --git a/hw/lm32/milkymist-hw.h b/hw/lm32/milkymist-hw.h index 4418b44ca9..d3be0cfb3a 100644 --- a/hw/lm32/milkymist-hw.h +++ b/hw/lm32/milkymist-hw.h @@ -6,7 +6,7 @@ static inline DeviceState *milkymist_uart_create(hwaddr base, qemu_irq irq, - CharDriverState *chr) + Chardev *chr) { DeviceState *dev; diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index cf48f420cc..75877de11c 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -551,10 +551,10 @@ static void malta_fgpa_display_event(void *opaque, int event) } static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space, - hwaddr base, qemu_irq uart_irq, CharDriverState *uart_chr) + hwaddr base, qemu_irq uart_irq, Chardev *uart_chr) { MaltaFPGAState *s; - CharDriverState *chr; + Chardev *chr; s = (MaltaFPGAState *)g_malloc0(sizeof(MaltaFPGAState)); diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index fd14d7a07e..846e903eb2 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -869,7 +869,7 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp) s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem, &error_abort); } else { - CharDriverState *chr = qemu_chr_fe_get_driver(&s->server_chr); + Chardev *chr = qemu_chr_fe_get_driver(&s->server_chr); assert(chr); IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n", diff --git a/hw/misc/milkymist-pfpu.c b/hw/misc/milkymist-pfpu.c index 3ca25894f1..86f5e383b0 100644 --- a/hw/misc/milkymist-pfpu.c +++ b/hw/misc/milkymist-pfpu.c @@ -125,7 +125,7 @@ struct MilkymistPFPUState { SysBusDevice parent_obj; MemoryRegion regs_region; - CharDriverState *chr; + Chardev *chr; qemu_irq irq; uint32_t regs[R_MAX]; diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 7a588a7ad4..92f091a613 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -246,7 +246,7 @@ static void scsi_read_data(SCSIRequest *req) SCSIDevice *s = r->req.dev; int ret; - DPRINTF("scsi_read_data 0x%x\n", req->tag); + DPRINTF("scsi_read_data tag=0x%x\n", req->tag); /* The request is used as the AIO opaque value, so add a ref. */ scsi_req_ref(&r->req); @@ -294,7 +294,7 @@ static void scsi_write_data(SCSIRequest *req) SCSIDevice *s = r->req.dev; int ret; - DPRINTF("scsi_write_data 0x%x\n", req->tag); + DPRINTF("scsi_write_data tag=0x%x\n", req->tag); if (r->len == 0) { r->len = r->buflen; scsi_req_data(&r->req, r->len); @@ -329,6 +329,7 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) int ret; #ifdef DEBUG_SCSI + DPRINTF("Command: data=0x%02x", cmd[0]); { int i; for (i = 1; i < r->req.cmd.len; i++) { diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index 637f8722a7..4165450250 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -27,6 +27,7 @@ #include "hw/hw.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" +#include "sysemu/replay.h" #include "hw/timer/mc146818rtc.h" #include "qapi/visitor.h" #include "qapi-event.h" @@ -734,10 +735,16 @@ static int rtc_post_load(void *opaque, int version_id) check_update_timer(s); } - uint64_t now = qemu_clock_get_ns(rtc_clock); - if (now < s->next_periodic_time || - now > (s->next_periodic_time + get_max_clock_jump())) { - periodic_timer_update(s, qemu_clock_get_ns(rtc_clock)); + /* The periodic timer is deterministic in record/replay mode, + * so there is no need to update it after loading the vmstate. + * Reading RTC here would misalign record and replay. + */ + if (replay_mode == REPLAY_MODE_NONE) { + uint64_t now = qemu_clock_get_ns(rtc_clock); + if (now < s->next_periodic_time || + now > (s->next_periodic_time + get_max_clock_jump())) { + periodic_timer_update(s, qemu_clock_get_ns(rtc_clock)); + } } #ifdef TARGET_I386 diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c index 88cb6d8978..daab0d56cf 100644 --- a/hw/usb/ccid-card-passthru.c +++ b/hw/usb/ccid-card-passthru.c @@ -264,7 +264,7 @@ static void ccid_card_vscard_handle_message(PassthruState *card, static void ccid_card_vscard_drop_connection(PassthruState *card) { - CharDriverState *chr = qemu_chr_fe_get_driver(&card->cs); + Chardev *chr = qemu_chr_fe_get_driver(&card->cs); qemu_chr_fe_deinit(&card->cs); qemu_chr_delete(chr); diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index 6066d9b0f7..6d5137383b 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -483,7 +483,7 @@ static void usb_serial_realize(USBDevice *dev, Error **errp) { USBSerialState *s = USB_SERIAL_DEV(dev); Error *local_err = NULL; - CharDriverState *chr = qemu_chr_fe_get_driver(&s->cs); + Chardev *chr = qemu_chr_fe_get_driver(&s->cs); usb_desc_create_serial(dev); usb_desc_init(dev); @@ -512,7 +512,7 @@ static void usb_serial_realize(USBDevice *dev, Error **errp) static USBDevice *usb_serial_init(USBBus *bus, const char *filename) { USBDevice *dev; - CharDriverState *cdrv; + Chardev *cdrv; uint32_t vendorid = 0, productid = 0; char label[32]; static int index; @@ -564,7 +564,7 @@ static USBDevice *usb_serial_init(USBBus *bus, const char *filename) static USBDevice *usb_braille_init(USBBus *bus, const char *unused) { USBDevice *dev; - CharDriverState *cdrv; + Chardev *cdrv; cdrv = qemu_chr_new("braille", "braille"); if (!cdrv) diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 4a0ebbfb32..860f5c35eb 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -284,7 +284,7 @@ static gboolean usbredir_write_unblocked(GIOChannel *chan, GIOCondition cond, static int usbredir_write(void *priv, uint8_t *data, int count) { USBRedirDevice *dev = priv; - CharDriverState *chr = qemu_chr_fe_get_driver(&dev->cs); + Chardev *chr = qemu_chr_fe_get_driver(&dev->cs); int r; if (!chr->be_open) { @@ -1430,7 +1430,7 @@ static void usbredir_cleanup_device_queues(USBRedirDevice *dev) static void usbredir_handle_destroy(USBDevice *udev) { USBRedirDevice *dev = USB_REDIRECT(udev); - CharDriverState *chr = qemu_chr_fe_get_driver(&dev->cs); + Chardev *chr = qemu_chr_fe_get_driver(&dev->cs); qemu_chr_fe_deinit(&dev->cs); qemu_chr_delete(chr); diff --git a/include/block/block.h b/include/block/block.h index 8b0dcdaa70..4e81f2069b 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -116,6 +116,7 @@ typedef struct HDGeometry { #define BDRV_REQUEST_MAX_SECTORS MIN(SIZE_MAX >> BDRV_SECTOR_BITS, \ INT_MAX >> BDRV_SECTOR_BITS) +#define BDRV_REQUEST_MAX_BYTES (BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS) /* * Allocation status flags diff --git a/include/exec/memory.h b/include/exec/memory.h index a10044f08f..987f9251c6 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -1250,7 +1250,7 @@ void memory_global_dirty_log_start(void); */ void memory_global_dirty_log_stop(void); -void mtree_info(fprintf_function mon_printf, void *f); +void mtree_info(fprintf_function mon_printf, void *f, bool flatview); /** * memory_region_dispatch_read: perform a read directly to the specified diff --git a/include/hw/arm/exynos4210.h b/include/hw/arm/exynos4210.h index 76bb6d4203..d9e08014d6 100644 --- a/include/hw/arm/exynos4210.h +++ b/include/hw/arm/exynos4210.h @@ -131,7 +131,7 @@ void exynos4210_combiner_get_gpioin(Exynos4210Irq *irqs, DeviceState *dev, DeviceState *exynos4210_uart_create(hwaddr addr, int fifo_size, int channel, - CharDriverState *chr, + Chardev *chr, qemu_irq irq); #endif /* EXYNOS4210_H */ diff --git a/include/hw/arm/omap.h b/include/hw/arm/omap.h index f25870b718..cac1b2ba43 100644 --- a/include/hw/arm/omap.h +++ b/include/hw/arm/omap.h @@ -664,14 +664,14 @@ struct omap_uart_s; struct omap_uart_s *omap_uart_init(hwaddr base, qemu_irq irq, omap_clk fclk, omap_clk iclk, qemu_irq txdma, qemu_irq rxdma, - const char *label, CharDriverState *chr); + const char *label, Chardev *chr); struct omap_uart_s *omap2_uart_init(MemoryRegion *sysmem, struct omap_target_agent_s *ta, qemu_irq irq, omap_clk fclk, omap_clk iclk, qemu_irq txdma, qemu_irq rxdma, - const char *label, CharDriverState *chr); + const char *label, Chardev *chr); void omap_uart_reset(struct omap_uart_s *s); -void omap_uart_attach(struct omap_uart_s *s, CharDriverState *chr); +void omap_uart_attach(struct omap_uart_s *s, Chardev *chr); struct omap_mpuio_s; qemu_irq *omap_mpuio_in_get(struct omap_mpuio_s *s); diff --git a/include/hw/bt.h b/include/hw/bt.h index 2fa22bdab6..b5e11d4d43 100644 --- a/include/hw/bt.h +++ b/include/hw/bt.h @@ -127,8 +127,8 @@ enum { csrhci_pin_wakeup, __csrhci_pins, }; -qemu_irq *csrhci_pins_get(CharDriverState *chr); -CharDriverState *uart_hci_init(void); +qemu_irq *csrhci_pins_get(Chardev *chr); +Chardev *uart_hci_init(void); /* bt-l2cap.c */ struct bt_l2cap_device_s; diff --git a/include/hw/char/cadence_uart.h b/include/hw/char/cadence_uart.h index ca75eb5e32..c836db4b74 100644 --- a/include/hw/char/cadence_uart.h +++ b/include/hw/char/cadence_uart.h @@ -51,7 +51,7 @@ typedef struct { static inline DeviceState *cadence_uart_create(hwaddr addr, qemu_irq irq, - CharDriverState *chr) + Chardev *chr) { DeviceState *dev; SysBusDevice *s; diff --git a/include/hw/char/escc.h b/include/hw/char/escc.h index 297e2ebcda..08ae122386 100644 --- a/include/hw/char/escc.h +++ b/include/hw/char/escc.h @@ -5,7 +5,7 @@ #define TYPE_ESCC "escc" #define ESCC_SIZE 4 MemoryRegion *escc_init(hwaddr base, qemu_irq irqA, qemu_irq irqB, - CharDriverState *chrA, CharDriverState *chrB, + Chardev *chrA, Chardev *chrB, int clock, int it_shift); void slavio_serial_ms_kbd_init(hwaddr base, qemu_irq irq, diff --git a/include/hw/char/pl011.h b/include/hw/char/pl011.h index 0ca7c19410..83649324b6 100644 --- a/include/hw/char/pl011.h +++ b/include/hw/char/pl011.h @@ -17,7 +17,7 @@ static inline DeviceState *pl011_create(hwaddr addr, qemu_irq irq, - CharDriverState *chr) + Chardev *chr) { DeviceState *dev; SysBusDevice *s; @@ -34,7 +34,7 @@ static inline DeviceState *pl011_create(hwaddr addr, static inline DeviceState *pl011_luminary_create(hwaddr addr, qemu_irq irq, - CharDriverState *chr) + Chardev *chr) { DeviceState *dev; SysBusDevice *s; diff --git a/include/hw/char/serial.h b/include/hw/char/serial.h index c928d7d907..daebb076c2 100644 --- a/include/hw/char/serial.h +++ b/include/hw/char/serial.h @@ -88,11 +88,11 @@ void serial_set_frequency(SerialState *s, uint32_t frequency); /* legacy pre qom */ SerialState *serial_init(int base, qemu_irq irq, int baudbase, - CharDriverState *chr, MemoryRegion *system_io); + Chardev *chr, MemoryRegion *system_io); SerialState *serial_mm_init(MemoryRegion *address_space, hwaddr base, int it_shift, qemu_irq irq, int baudbase, - CharDriverState *chr, enum device_endian end); + Chardev *chr, enum device_endian end); /* serial-isa.c */ #define TYPE_ISA_SERIAL "isa-serial" diff --git a/include/hw/char/xilinx_uartlite.h b/include/hw/char/xilinx_uartlite.h index 8b4fc54953..634086b657 100644 --- a/include/hw/char/xilinx_uartlite.h +++ b/include/hw/char/xilinx_uartlite.h @@ -17,7 +17,7 @@ static inline DeviceState *xilinx_uartlite_create(hwaddr addr, qemu_irq irq, - CharDriverState *chr) + Chardev *chr) { DeviceState *dev; SysBusDevice *s; diff --git a/include/hw/cris/etraxfs.h b/include/hw/cris/etraxfs.h index 723a2753c8..8da965addb 100644 --- a/include/hw/cris/etraxfs.h +++ b/include/hw/cris/etraxfs.h @@ -48,7 +48,7 @@ etraxfs_eth_init(NICInfo *nd, hwaddr base, int phyaddr, static inline DeviceState *etraxfs_ser_create(hwaddr addr, qemu_irq irq, - CharDriverState *chr) + Chardev *chr) { DeviceState *dev; SysBusDevice *s; diff --git a/include/hw/devices.h b/include/hw/devices.h index c60bcabae3..7475b714de 100644 --- a/include/hw/devices.h +++ b/include/hw/devices.h @@ -65,6 +65,6 @@ qemu_irq tc6393xb_l3v_get(TC6393xbState *s); /* sm501.c */ void sm501_init(struct MemoryRegion *address_space_mem, uint32_t base, uint32_t local_mem_bytes, qemu_irq irq, - CharDriverState *chr); + Chardev *chr); #endif diff --git a/include/hw/i386/apic_internal.h b/include/hw/i386/apic_internal.h index 1209eb483a..20ad28c95b 100644 --- a/include/hw/i386/apic_internal.h +++ b/include/hw/i386/apic_internal.h @@ -189,6 +189,8 @@ struct APICCommonState { DeviceState *vapic; hwaddr vapic_paddr; /* note: persistence via kvmvapic */ bool legacy_instance_id; + + int apic_irq_delivered; /* for saving static variable */ }; typedef struct VAPICState { diff --git a/include/hw/i386/ich9.h b/include/hw/i386/ich9.h index 5fd7e97d23..18dcca7ebc 100644 --- a/include/hw/i386/ich9.h +++ b/include/hw/i386/ich9.h @@ -64,6 +64,16 @@ typedef struct ICH9LPCState { uint8_t rst_cnt; MemoryRegion rst_cnt_mem; + /* SMI feature negotiation via fw_cfg */ + uint64_t smi_host_features; /* guest-invisible, host endian */ + uint8_t smi_host_features_le[8]; /* guest-visible, read-only, little + * endian uint64_t */ + uint8_t smi_guest_features_le[8]; /* guest-visible, read-write, little + * endian uint64_t */ + uint8_t smi_features_ok; /* guest-visible, read-only; selecting it + * triggers feature lockdown */ + uint64_t smi_negotiated_features; /* guest-invisible, host endian */ + /* isa bus */ ISABus *isa_bus; MemoryRegion rcrb_mem; /* root complex register block */ @@ -240,4 +250,7 @@ Object *ich9_lpc_find(void); #define ICH9_SMB_HST_D1 0x06 #define ICH9_SMB_HOST_BLOCK_DB 0x07 +/* bit positions used in fw_cfg SMI feature negotiation */ +#define ICH9_LPC_SMI_F_BROADCAST_BIT 0 + #endif /* HW_ICH9_H */ diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 5a20c5e38e..079e8d9393 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -181,7 +181,7 @@ void parallel_hds_isa_init(ISABus *bus, int n); bool parallel_mm_init(MemoryRegion *address_space, hwaddr base, int it_shift, qemu_irq irq, - CharDriverState *chr); + Chardev *chr); /* i8259.c */ @@ -381,6 +381,16 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); .property = "x-mach-use-reliable-get-clock",\ .value = "off",\ },\ + {\ + .driver = "ICH9-LPC",\ + .property = "x-smi-broadcast",\ + .value = "off",\ + },\ + {\ + .driver = TYPE_X86_CPU,\ + .property = "vmware-cpuid-freq",\ + .value = "off",\ + }, #define PC_COMPAT_2_7 \ HW_COMPAT_2_7 \ diff --git a/include/hw/m68k/mcf.h b/include/hw/m68k/mcf.h index bf43998d9b..9a0bcfa0f4 100644 --- a/include/hw/m68k/mcf.h +++ b/include/hw/m68k/mcf.h @@ -11,10 +11,10 @@ uint64_t mcf_uart_read(void *opaque, hwaddr addr, unsigned size); void mcf_uart_write(void *opaque, hwaddr addr, uint64_t val, unsigned size); -void *mcf_uart_init(qemu_irq irq, CharDriverState *chr); +void *mcf_uart_init(qemu_irq irq, Chardev *chr); void mcf_uart_mm_init(struct MemoryRegion *sysmem, hwaddr base, - qemu_irq irq, CharDriverState *chr); + qemu_irq irq, Chardev *chr); /* mcf_intc.c */ qemu_irq *mcf_intc_init(struct MemoryRegion *sysmem, diff --git a/include/hw/ppc/spapr_vio.h b/include/hw/ppc/spapr_vio.h index 14f502240e..fc6f673ea0 100644 --- a/include/hw/ppc/spapr_vio.h +++ b/include/hw/ppc/spapr_vio.h @@ -127,7 +127,7 @@ int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq); VIOsPAPRDevice *vty_lookup(sPAPRMachineState *spapr, target_ulong reg); void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len); -void spapr_vty_create(VIOsPAPRBus *bus, CharDriverState *chardev); +void spapr_vty_create(VIOsPAPRBus *bus, Chardev *chardev); void spapr_vlan_create(VIOsPAPRBus *bus, NICInfo *nd); void spapr_vscsi_create(VIOsPAPRBus *bus); diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 2c973473f7..b44b476765 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -26,6 +26,7 @@ typedef enum DeviceCategory { DEVICE_CATEGORY_DISPLAY, DEVICE_CATEGORY_SOUND, DEVICE_CATEGORY_MISC, + DEVICE_CATEGORY_CPU, DEVICE_CATEGORY_MAX } DeviceCategory; diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 306bbab088..7ac315331a 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -184,7 +184,7 @@ void qdev_prop_set_uint32(DeviceState *dev, const char *name, uint32_t value); void qdev_prop_set_int32(DeviceState *dev, const char *name, int32_t value); void qdev_prop_set_uint64(DeviceState *dev, const char *name, uint64_t value); void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value); -void qdev_prop_set_chr(DeviceState *dev, const char *name, CharDriverState *value); +void qdev_prop_set_chr(DeviceState *dev, const char *name, Chardev *value); void qdev_prop_set_netdev(DeviceState *dev, const char *name, NetClientState *value); void qdev_prop_set_drive(DeviceState *dev, const char *name, BlockBackend *value, Error **errp); diff --git a/include/hw/sh4/sh.h b/include/hw/sh4/sh.h index e59b9e7c45..767a2df7e2 100644 --- a/include/hw/sh4/sh.h +++ b/include/hw/sh4/sh.h @@ -42,7 +42,7 @@ void tmu012_init(struct MemoryRegion *sysmem, hwaddr base, #define SH_SERIAL_FEAT_SCIF (1 << 0) void sh_serial_init(MemoryRegion *sysmem, hwaddr base, int feat, - uint32_t freq, CharDriverState *chr, + uint32_t freq, Chardev *chr, qemu_irq eri_source, qemu_irq rxi_source, qemu_irq txi_source, diff --git a/include/hw/sparc/grlib.h b/include/hw/sparc/grlib.h index afbb9bc07c..61a345c269 100644 --- a/include/hw/sparc/grlib.h +++ b/include/hw/sparc/grlib.h @@ -100,7 +100,7 @@ DeviceState *grlib_gptimer_create(hwaddr base, static inline DeviceState *grlib_apbuart_create(hwaddr base, - CharDriverState *serial, + Chardev *serial, qemu_irq irq) { DeviceState *dev; diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h index a8f3afb03b..09c2ce5170 100644 --- a/include/hw/xen/xen.h +++ b/include/hw/xen/xen.h @@ -37,7 +37,7 @@ int xen_is_pirq_msi(uint32_t msi_data); qemu_irq *xen_interrupt_controller_init(void); -void xenstore_store_pv_console_info(int i, struct CharDriverState *chr); +void xenstore_store_pv_console_info(int i, struct Chardev *chr); void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory); diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 8cc532ec0e..e64b944d7c 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -16,7 +16,7 @@ extern Monitor *cur_mon; bool monitor_cur_is_qmp(void); -void monitor_init(CharDriverState *chr, int flags); +void monitor_init(Chardev *chr, int flags); void monitor_cleanup(void); int monitor_suspend(Monitor *mon); diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 9a8bcbde36..e95f28cfec 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -17,7 +17,7 @@ typedef struct BlockBackendRootState BlockBackendRootState; typedef struct BlockDriverState BlockDriverState; typedef struct BusClass BusClass; typedef struct BusState BusState; -typedef struct CharDriverState CharDriverState; +typedef struct Chardev Chardev; typedef struct CompatProperty CompatProperty; typedef struct CPUAddressSpace CPUAddressSpace; typedef struct CPUState CPUState; diff --git a/include/sysemu/char.h b/include/sysemu/char.h index 0a149428cf..da0e7dd494 100644 --- a/include/sysemu/char.h +++ b/include/sysemu/char.h @@ -10,6 +10,7 @@ #include "qapi/qmp/qstring.h" #include "qemu/main-loop.h" #include "qemu/bitmap.h" +#include "qom/object.h" /* character device */ @@ -69,14 +70,16 @@ typedef enum { /* Whether it is possible to send/recv file descriptors * over the data channel */ QEMU_CHAR_FEATURE_FD_PASS, + /* Whether replay or record mode is enabled */ + QEMU_CHAR_FEATURE_REPLAY, QEMU_CHAR_FEATURE_LAST, } CharDriverFeature; /* This is the backend as seen by frontend, the actual backend is - * CharDriverState */ + * Chardev */ typedef struct CharBackend { - CharDriverState *chr; + Chardev *chr; IOEventHandler *chr_event; IOCanReadHandler *chr_can_read; IOReadHandler *chr_read; @@ -85,49 +88,23 @@ typedef struct CharBackend { int fe_open; } CharBackend; -struct CharDriverState { +typedef struct CharDriver CharDriver; + +struct Chardev { + Object parent_obj; + QemuMutex chr_write_lock; - int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len); - int (*chr_sync_read)(struct CharDriverState *s, - const uint8_t *buf, int len); - GSource *(*chr_add_watch)(struct CharDriverState *s, GIOCondition cond); - void (*chr_update_read_handler)(struct CharDriverState *s, - GMainContext *context); - int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg); - int (*get_msgfds)(struct CharDriverState *s, int* fds, int num); - int (*set_msgfds)(struct CharDriverState *s, int *fds, int num); - int (*chr_add_client)(struct CharDriverState *chr, int fd); - int (*chr_wait_connected)(struct CharDriverState *chr, Error **errp); - void (*chr_free)(struct CharDriverState *chr); - void (*chr_disconnect)(struct CharDriverState *chr); - void (*chr_accept_input)(struct CharDriverState *chr); - void (*chr_set_echo)(struct CharDriverState *chr, bool echo); - void (*chr_set_fe_open)(struct CharDriverState *chr, int fe_open); CharBackend *be; - void *opaque; char *label; char *filename; int logfd; int be_open; - int is_mux; guint fd_in_tag; - bool replay; DECLARE_BITMAP(features, QEMU_CHAR_FEATURE_LAST); - QTAILQ_ENTRY(CharDriverState) next; + QTAILQ_ENTRY(Chardev) next; }; /** - * qemu_chr_alloc: - * @backend: the common backend config - * @errp: pointer to a NULL-initialized error object - * - * Allocate and initialize a new CharDriverState. - * - * Returns: a newly allocated CharDriverState, or NULL on error. - */ -CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp); - -/** * @qemu_chr_new_from_opts: * * Create a new character backend from a QemuOpts list. @@ -136,8 +113,8 @@ CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp); * * Returns: a new character backend */ -CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, - Error **errp); +Chardev *qemu_chr_new_from_opts(QemuOpts *opts, + Error **errp); /** * @qemu_chr_parse_common: @@ -159,7 +136,7 @@ void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend); * * Returns: a new character backend */ -CharDriverState *qemu_chr_new(const char *label, const char *filename); +Chardev *qemu_chr_new(const char *label, const char *filename); /** @@ -197,7 +174,7 @@ int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp); * * Returns: a new character backend */ -CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename); +Chardev *qemu_chr_new_noreplay(const char *label, const char *filename); /** * @qemu_chr_delete: @@ -205,14 +182,14 @@ CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename); * Destroy a character backend and remove it from the list of * identified character backends. */ -void qemu_chr_delete(CharDriverState *chr); +void qemu_chr_delete(Chardev *chr); /** * @qemu_chr_free: * * Destroy a character backend. */ -void qemu_chr_free(CharDriverState *chr); +void qemu_chr_free(Chardev *chr); /** * @qemu_chr_fe_set_echo: @@ -258,6 +235,8 @@ void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...) * @cond the condition to poll for * @func the function to call when the condition happens * @user_data the opaque pointer to pass to @func + * + * Returns: the source tag */ guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond, GIOFunc func, void *user_data); @@ -366,7 +345,7 @@ int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num); * * Returns: the number of bytes the front end can receive via @qemu_chr_be_write */ -int qemu_chr_be_can_write(CharDriverState *s); +int qemu_chr_be_can_write(Chardev *s); /** * @qemu_chr_be_write: @@ -378,7 +357,7 @@ int qemu_chr_be_can_write(CharDriverState *s); * @buf a buffer to receive data from the front end * @len the number of bytes to receive from the front end */ -void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len); +void qemu_chr_be_write(Chardev *s, uint8_t *buf, int len); /** * @qemu_chr_be_write_impl: @@ -388,7 +367,7 @@ void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len); * @buf a buffer to receive data from the front end * @len the number of bytes to receive from the front end */ -void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len); +void qemu_chr_be_write_impl(Chardev *s, uint8_t *buf, int len); /** * @qemu_chr_be_event: @@ -397,7 +376,7 @@ void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len); * * @event the event to send */ -void qemu_chr_be_event(CharDriverState *s, int event); +void qemu_chr_be_event(Chardev *s, int event); /** * @qemu_chr_fe_init: @@ -408,7 +387,7 @@ void qemu_chr_be_event(CharDriverState *s, int event); * * Returns: false on error. */ -bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp); +bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp); /** * @qemu_chr_fe_get_driver: @@ -416,7 +395,7 @@ bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp); * Returns the driver associated with a CharBackend or NULL if no * associated CharDriver. */ -CharDriverState *qemu_chr_fe_get_driver(CharBackend *be); +Chardev *qemu_chr_fe_get_driver(CharBackend *be); /** * @qemu_chr_fe_deinit: @@ -461,33 +440,83 @@ void qemu_chr_fe_set_handlers(CharBackend *b, */ void qemu_chr_fe_take_focus(CharBackend *b); -void qemu_chr_be_generic_open(CharDriverState *s); +void qemu_chr_be_generic_open(Chardev *s); void qemu_chr_fe_accept_input(CharBackend *be); -int qemu_chr_add_client(CharDriverState *s, int fd); -CharDriverState *qemu_chr_find(const char *name); -bool chr_is_ringbuf(const CharDriverState *chr); +int qemu_chr_add_client(Chardev *s, int fd); +Chardev *qemu_chr_find(const char *name); -bool qemu_chr_has_feature(CharDriverState *chr, +bool qemu_chr_has_feature(Chardev *chr, CharDriverFeature feature); -void qemu_chr_set_feature(CharDriverState *chr, +void qemu_chr_set_feature(Chardev *chr, CharDriverFeature feature); QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename); -typedef void CharDriverParse(QemuOpts *opts, ChardevBackend *backend, - Error **errp); -typedef CharDriverState *CharDriverCreate(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, bool *be_opened, - Error **errp); +#define TYPE_CHARDEV "chardev" +#define CHARDEV(obj) OBJECT_CHECK(Chardev, (obj), TYPE_CHARDEV) +#define CHARDEV_CLASS(klass) \ + OBJECT_CLASS_CHECK(ChardevClass, (klass), TYPE_CHARDEV) +#define CHARDEV_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ChardevClass, (obj), TYPE_CHARDEV) + +#define TYPE_CHARDEV_NULL "chardev-null" +#define TYPE_CHARDEV_MUX "chardev-mux" +#define TYPE_CHARDEV_RINGBUF "chardev-ringbuf" +#define TYPE_CHARDEV_PTY "chardev-pty" +#define TYPE_CHARDEV_CONSOLE "chardev-console" +#define TYPE_CHARDEV_STDIO "chardev-stdio" +#define TYPE_CHARDEV_PIPE "chardev-pipe" +#define TYPE_CHARDEV_MEMORY "chardev-memory" +#define TYPE_CHARDEV_PARALLEL "chardev-parallel" +#define TYPE_CHARDEV_FILE "chardev-file" +#define TYPE_CHARDEV_SERIAL "chardev-serial" +#define TYPE_CHARDEV_SOCKET "chardev-socket" +#define TYPE_CHARDEV_UDP "chardev-udp" + +#define CHARDEV_IS_MUX(chr) \ + object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_MUX) +#define CHARDEV_IS_RINGBUF(chr) \ + object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_RINGBUF) +#define CHARDEV_IS_PTY(chr) \ + object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_PTY) + +typedef struct ChardevClass { + ObjectClass parent_class; + + bool internal; /* TODO: eventually use TYPE_USER_CREATABLE */ + + void (*open)(Chardev *chr, ChardevBackend *backend, + bool *be_opened, Error **errp); + + int (*chr_write)(Chardev *s, const uint8_t *buf, int len); + int (*chr_sync_read)(Chardev *s, const uint8_t *buf, int len); + GSource *(*chr_add_watch)(Chardev *s, GIOCondition cond); + void (*chr_update_read_handler)(Chardev *s, GMainContext *context); + int (*chr_ioctl)(Chardev *s, int cmd, void *arg); + int (*get_msgfds)(Chardev *s, int* fds, int num); + int (*set_msgfds)(Chardev *s, int *fds, int num); + int (*chr_add_client)(Chardev *chr, int fd); + int (*chr_wait_connected)(Chardev *chr, Error **errp); + void (*chr_free)(Chardev *chr); + void (*chr_disconnect)(Chardev *chr); + void (*chr_accept_input)(Chardev *chr); + void (*chr_set_echo)(Chardev *chr, bool echo); + void (*chr_set_fe_open)(Chardev *chr, int fe_open); +} ChardevClass; + +struct CharDriver { + ChardevBackendKind kind; + const char *alias; + void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp); +}; -void register_char_driver(const char *name, ChardevBackendKind kind, - CharDriverParse *parse, CharDriverCreate *create); +Chardev *qemu_chardev_new(const char *id, const char *typename, + ChardevBackend *backend, Error **errp); -extern int term_escape_char; +void register_char_driver(const CharDriver *driver); +extern int term_escape_char; /* console.c */ -typedef CharDriverState *(VcHandler)(ChardevVC *vc, Error **errp); -void register_vc_handler(VcHandler *handler); +void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp); #endif diff --git a/include/sysemu/replay.h b/include/sysemu/replay.h index abb35ca8c9..7aad20b07f 100644 --- a/include/sysemu/replay.h +++ b/include/sysemu/replay.h @@ -43,6 +43,9 @@ typedef struct ReplayNetState ReplayNetState; extern ReplayMode replay_mode; +/* Name of the initial VM snapshot */ +extern char *replay_snapshot; + /* Replay process control functions */ /*! Enables recording or saving event log with specified parameters */ @@ -125,9 +128,9 @@ uint64_t blkreplay_next_id(void); /* Character device */ /*! Registers char driver to save it's events */ -void replay_register_char_driver(struct CharDriverState *chr); +void replay_register_char_driver(struct Chardev *chr); /*! Saves write to char device event to the log */ -void replay_chr_be_write(struct CharDriverState *s, uint8_t *buf, int len); +void replay_chr_be_write(struct Chardev *s, uint8_t *buf, int len); /*! Writes char write return value to the replay log. */ void replay_char_write_event_save(int res, int offset); /*! Reads char write return value from the replay log. */ @@ -149,4 +152,10 @@ void replay_unregister_net(ReplayNetState *rns); void replay_net_packet_event(ReplayNetState *rns, unsigned flags, const struct iovec *iov, int iovcnt); +/* VM state operations */ + +/*! Called at the start of execution. + Loads or saves initial vmstate depending on execution mode. */ +void replay_vmstate_init(void); + #endif diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index ff8ffb5e47..4d50694930 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -74,6 +74,7 @@ void qemu_add_machine_init_done_notifier(Notifier *notify); void qemu_remove_machine_init_done_notifier(Notifier *notify); void hmp_savevm(Monitor *mon, const QDict *qdict); +int save_vmstate(Monitor *mon, const char *name); int load_vmstate(const char *name); void hmp_delvm(Monitor *mon, const QDict *qdict); void hmp_info_snapshots(Monitor *mon, const QDict *qdict); @@ -189,13 +190,13 @@ void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict); #define MAX_SERIAL_PORTS 4 -extern CharDriverState *serial_hds[MAX_SERIAL_PORTS]; +extern Chardev *serial_hds[MAX_SERIAL_PORTS]; /* parallel ports */ #define MAX_PARALLEL_PORTS 3 -extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; +extern Chardev *parallel_hds[MAX_PARALLEL_PORTS]; void hmp_usb_add(Monitor *mon, const QDict *qdict); void hmp_usb_del(Monitor *mon, const QDict *qdict); diff --git a/include/ui/console.h b/include/ui/console.h index b59e7b8c15..af6350e96f 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -383,6 +383,8 @@ void graphic_hw_invalidate(QemuConsole *con); void graphic_hw_text_update(QemuConsole *con, console_ch_t *chardata); void graphic_hw_gl_block(QemuConsole *con, bool block); +void qemu_console_early_init(void); + QemuConsole *qemu_console_lookup_by_index(unsigned int index); QemuConsole *qemu_console_lookup_by_device(DeviceState *dev, uint32_t head); QemuConsole *qemu_console_lookup_by_device_name(const char *device_id, diff --git a/include/ui/gtk.h b/include/ui/gtk.h index b3b50059c7..47ffddb5b4 100644 --- a/include/ui/gtk.h +++ b/include/ui/gtk.h @@ -64,7 +64,7 @@ typedef struct VirtualVteConsole { GtkWidget *box; GtkWidget *scrollbar; GtkWidget *terminal; - CharDriverState *chr; + Chardev *chr; bool echo; } VirtualVteConsole; #endif diff --git a/include/ui/qemu-spice.h b/include/ui/qemu-spice.h index 75e12396bb..52a9f8808b 100644 --- a/include/ui/qemu-spice.h +++ b/include/ui/qemu-spice.h @@ -51,7 +51,7 @@ int qemu_spice_migrate_info(const char *hostname, int port, int tls_port, #if SPICE_SERVER_VERSION >= 0x000c02 void qemu_spice_register_ports(void); #else -static inline CharDriverState *qemu_chr_open_spice_port(const char *name) +static inline Chardev *qemu_chr_open_spice_port(const char *name) { return NULL; } #endif @@ -2450,6 +2450,21 @@ void address_space_destroy(AddressSpace *as) call_rcu(as, do_address_space_destroy, rcu); } +static const char *memory_region_type(MemoryRegion *mr) +{ + if (memory_region_is_ram_device(mr)) { + return "ramd"; + } else if (memory_region_is_romd(mr)) { + return "romd"; + } else if (memory_region_is_rom(mr)) { + return "rom"; + } else if (memory_region_is_ram(mr)) { + return "ram"; + } else { + return "i/o"; + } +} + typedef struct MemoryRegionList MemoryRegionList; struct MemoryRegionList { @@ -2459,6 +2474,10 @@ struct MemoryRegionList { typedef QTAILQ_HEAD(queue, MemoryRegionList) MemoryRegionListHead; +#define MR_SIZE(size) (int128_nz(size) ? (hwaddr)int128_get64( \ + int128_sub((size), int128_one())) : 0) +#define MTREE_INDENT " " + static void mtree_print_mr(fprintf_function mon_printf, void *f, const MemoryRegion *mr, unsigned int level, hwaddr base, @@ -2474,7 +2493,7 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f, } for (i = 0; i < level; i++) { - mon_printf(f, " "); + mon_printf(f, MTREE_INDENT); } if (mr->alias) { @@ -2494,37 +2513,24 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f, QTAILQ_INSERT_TAIL(alias_print_queue, ml, queue); } mon_printf(f, TARGET_FMT_plx "-" TARGET_FMT_plx - " (prio %d, %c%c): alias %s @%s " TARGET_FMT_plx + " (prio %d, %s): alias %s @%s " TARGET_FMT_plx "-" TARGET_FMT_plx "%s\n", base + mr->addr, - base + mr->addr - + (int128_nz(mr->size) ? - (hwaddr)int128_get64(int128_sub(mr->size, - int128_one())) : 0), + base + mr->addr + MR_SIZE(mr->size), mr->priority, - mr->romd_mode ? 'R' : '-', - !mr->readonly && !(mr->rom_device && mr->romd_mode) ? 'W' - : '-', + memory_region_type((MemoryRegion *)mr), memory_region_name(mr), memory_region_name(mr->alias), mr->alias_offset, - mr->alias_offset - + (int128_nz(mr->size) ? - (hwaddr)int128_get64(int128_sub(mr->size, - int128_one())) : 0), + mr->alias_offset + MR_SIZE(mr->size), mr->enabled ? "" : " [disabled]"); } else { mon_printf(f, - TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d, %c%c): %s%s\n", + TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d, %s): %s%s\n", base + mr->addr, - base + mr->addr - + (int128_nz(mr->size) ? - (hwaddr)int128_get64(int128_sub(mr->size, - int128_one())) : 0), + base + mr->addr + MR_SIZE(mr->size), mr->priority, - mr->romd_mode ? 'R' : '-', - !mr->readonly && !(mr->rom_device && mr->romd_mode) ? 'W' - : '-', + memory_region_type((MemoryRegion *)mr), memory_region_name(mr), mr->enabled ? "" : " [disabled]"); } @@ -2558,12 +2564,51 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f, } } -void mtree_info(fprintf_function mon_printf, void *f) +static void mtree_print_flatview(fprintf_function p, void *f, + AddressSpace *as) +{ + FlatView *view = address_space_get_flatview(as); + FlatRange *range = &view->ranges[0]; + MemoryRegion *mr; + int n = view->nr; + + if (n <= 0) { + p(f, MTREE_INDENT "No rendered FlatView for " + "address space '%s'\n", as->name); + flatview_unref(view); + return; + } + + while (n--) { + mr = range->mr; + p(f, MTREE_INDENT TARGET_FMT_plx "-" + TARGET_FMT_plx " (prio %d, %s): %s\n", + int128_get64(range->addr.start), + int128_get64(range->addr.start) + MR_SIZE(range->addr.size), + mr->priority, + memory_region_type(mr), + memory_region_name(mr)); + range++; + } + + flatview_unref(view); +} + +void mtree_info(fprintf_function mon_printf, void *f, bool flatview) { MemoryRegionListHead ml_head; MemoryRegionList *ml, *ml2; AddressSpace *as; + if (flatview) { + QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) { + mon_printf(f, "address-space (flat view): %s\n", as->name); + mtree_print_flatview(mon_printf, f, as); + mon_printf(f, "\n"); + } + return; + } + QTAILQ_INIT(&ml_head); QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) { diff --git a/migration/savevm.c b/migration/savevm.c index 455d5bac1e..204012ecef 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2042,38 +2042,40 @@ int qemu_loadvm_state(QEMUFile *f) return ret; } -void hmp_savevm(Monitor *mon, const QDict *qdict) +int save_vmstate(Monitor *mon, const char *name) { BlockDriverState *bs, *bs1; QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1; - int ret; + int ret = -1; QEMUFile *f; int saved_vm_running; uint64_t vm_state_size; qemu_timeval tv; struct tm tm; - const char *name = qdict_get_try_str(qdict, "name"); Error *local_err = NULL; AioContext *aio_context; if (!bdrv_all_can_snapshot(&bs)) { monitor_printf(mon, "Device '%s' is writable but does not " "support snapshots.\n", bdrv_get_device_name(bs)); - return; + return ret; } /* Delete old snapshots of the same name */ - if (name && bdrv_all_delete_snapshot(name, &bs1, &local_err) < 0) { - error_reportf_err(local_err, - "Error while deleting snapshot on device '%s': ", - bdrv_get_device_name(bs1)); - return; + if (name) { + ret = bdrv_all_delete_snapshot(name, &bs1, &local_err); + if (ret < 0) { + error_reportf_err(local_err, + "Error while deleting snapshot on device '%s': ", + bdrv_get_device_name(bs1)); + return ret; + } } bs = bdrv_all_find_vmstate_bs(); if (bs == NULL) { monitor_printf(mon, "No block device can accept snapshots\n"); - return; + return ret; } aio_context = bdrv_get_aio_context(bs); @@ -2082,7 +2084,7 @@ void hmp_savevm(Monitor *mon, const QDict *qdict) ret = global_state_store(); if (ret) { monitor_printf(mon, "Error saving global state\n"); - return; + return ret; } vm_stop(RUN_STATE_SAVE_VM); @@ -2128,13 +2130,22 @@ void hmp_savevm(Monitor *mon, const QDict *qdict) if (ret < 0) { monitor_printf(mon, "Error while creating snapshot on '%s'\n", bdrv_get_device_name(bs)); + goto the_end; } + ret = 0; + the_end: aio_context_release(aio_context); if (saved_vm_running) { vm_start(); } + return ret; +} + +void hmp_savevm(Monitor *mon, const QDict *qdict) +{ + save_vmstate(mon, qdict_get_try_str(qdict, "name")); } void qmp_xen_save_devices_state(const char *filename, Error **errp) @@ -1529,7 +1529,9 @@ static void hmp_boot_set(Monitor *mon, const QDict *qdict) static void hmp_info_mtree(Monitor *mon, const QDict *qdict) { - mtree_info((fprintf_function)monitor_printf, mon); + bool flatview = qdict_get_try_bool(qdict, "flatview", false); + + mtree_info((fprintf_function)monitor_printf, mon, flatview); } static void hmp_info_numa(Monitor *mon, const QDict *qdict) @@ -3193,8 +3195,8 @@ static void ringbuf_completion(ReadLineState *rs, const char *str) ChardevInfo *chr_info = list->value; if (!strncmp(chr_info->label, str, len)) { - CharDriverState *chr = qemu_chr_find(chr_info->label); - if (chr && chr_is_ringbuf(chr)) { + Chardev *chr = qemu_chr_find(chr_info->label); + if (chr && CHARDEV_IS_RINGBUF(chr)) { readline_add_completion(rs, chr_info->label); } } @@ -3983,7 +3985,7 @@ static void __attribute__((constructor)) monitor_lock_init(void) qemu_mutex_init(&monitor_lock); } -void monitor_init(CharDriverState *chr, int flags) +void monitor_init(Chardev *chr, int flags) { static int is_first_init = 1; Monitor *mon; diff --git a/net/colo-compare.c b/net/colo-compare.c index 9bfc736f55..4962976c22 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -564,7 +564,7 @@ static void compare_sec_rs_finalize(SocketReadState *sec_rs) * Return 0 is success. * Return 1 is failed. */ -static int find_and_check_chardev(CharDriverState **chr, +static int find_and_check_chardev(Chardev **chr, char *chr_name, Error **errp) { @@ -611,7 +611,7 @@ static void check_old_packet_regular(void *opaque) static void colo_compare_complete(UserCreatable *uc, Error **errp) { CompareState *s = COLO_COMPARE(uc); - CharDriverState *chr; + Chardev *chr; char thread_name[64]; static int compare_id; diff --git a/net/filter-mirror.c b/net/filter-mirror.c index b7d645617c..aa0aa98fa5 100644 --- a/net/filter-mirror.c +++ b/net/filter-mirror.c @@ -191,7 +191,7 @@ static void filter_redirector_cleanup(NetFilterState *nf) static void filter_mirror_setup(NetFilterState *nf, Error **errp) { MirrorState *s = FILTER_MIRROR(nf); - CharDriverState *chr; + Chardev *chr; if (!s->outdev) { error_setg(errp, "filter mirror needs 'outdev' " @@ -220,7 +220,7 @@ static void redirector_rs_finalize(SocketReadState *rs) static void filter_redirector_setup(NetFilterState *nf, Error **errp) { MirrorState *s = FILTER_REDIRECTOR(nf); - CharDriverState *chr; + Chardev *chr; if (!s->indev && !s->outdev) { error_setg(errp, "filter redirector needs 'indev' or " diff --git a/net/slirp.c b/net/slirp.c index bcd1c5f57d..f97ec23345 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -748,7 +748,7 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str, } } else { Error *err = NULL; - CharDriverState *chr = qemu_chr_new(buf, p); + Chardev *chr = qemu_chr_new(buf, p); if (!chr) { error_report("could not open guest forwarding device '%s'", buf); diff --git a/net/vhost-user.c b/net/vhost-user.c index 7aff77ee4a..b0f0ab6cc8 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -195,7 +195,7 @@ static void net_vhost_user_event(void *opaque, int event) const char *name = opaque; NetClientState *ncs[MAX_QUEUE_NUM]; VhostUserState *s; - CharDriverState *chr; + Chardev *chr; Error *err = NULL; int queues; @@ -232,7 +232,7 @@ static void net_vhost_user_event(void *opaque, int event) } static int net_vhost_user_init(NetClientState *peer, const char *device, - const char *name, CharDriverState *chr, + const char *name, Chardev *chr, int queues) { Error *err = NULL; @@ -274,10 +274,10 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, return 0; } -static CharDriverState *net_vhost_claim_chardev( +static Chardev *net_vhost_claim_chardev( const NetdevVhostUserOptions *opts, Error **errp) { - CharDriverState *chr = qemu_chr_find(opts->chardev); + Chardev *chr = qemu_chr_find(opts->chardev); if (chr == NULL) { error_setg(errp, "chardev \"%s\" not found", opts->chardev); @@ -324,7 +324,7 @@ int net_init_vhost_user(const Netdev *netdev, const char *name, { int queues; const NetdevVhostUserOptions *vhost_user_opts; - CharDriverState *chr; + Chardev *chr; assert(netdev->type == NET_CLIENT_DRIVER_VHOST_USER); vhost_user_opts = &netdev->u.vhost_user; diff --git a/qdev-monitor.c b/qdev-monitor.c index 81d01df928..549f45f066 100644 --- a/qdev-monitor.c +++ b/qdev-monitor.c @@ -137,6 +137,7 @@ static void qdev_print_devinfos(bool show_no_user) [DEVICE_CATEGORY_DISPLAY] = "Display", [DEVICE_CATEGORY_SOUND] = "Sound", [DEVICE_CATEGORY_MISC] = "Misc", + [DEVICE_CATEGORY_CPU] = "CPU", [DEVICE_CATEGORY_MAX] = "Uncategorized", }; GSList *list, *elt; diff --git a/qemu-char.c b/qemu-char.c index d8da1677ff..6b4a299702 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -89,7 +89,7 @@ #define READ_RETRIES 10 #define TCP_MAX_FDS 16 -typedef struct MuxDriver MuxDriver; +typedef struct MuxChardev MuxChardev; /***********************************************************/ /* Socket address helpers */ @@ -157,40 +157,10 @@ static char *sockaddr_to_str(struct sockaddr_storage *ss, socklen_t ss_len, /***********************************************************/ /* character device */ -static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs = +static QTAILQ_HEAD(ChardevHead, Chardev) chardevs = QTAILQ_HEAD_INITIALIZER(chardevs); -static void qemu_chr_free_common(CharDriverState *chr); - -CharDriverState *qemu_chr_alloc(ChardevCommon *backend, Error **errp) -{ - CharDriverState *chr = g_malloc0(sizeof(CharDriverState)); - qemu_mutex_init(&chr->chr_write_lock); - - if (backend->has_logfile) { - int flags = O_WRONLY | O_CREAT; - if (backend->has_logappend && - backend->logappend) { - flags |= O_APPEND; - } else { - flags |= O_TRUNC; - } - chr->logfd = qemu_open(backend->logfile, flags, 0666); - if (chr->logfd < 0) { - error_setg_errno(errp, errno, - "Unable to open logfile %s", - backend->logfile); - g_free(chr); - return NULL; - } - } else { - chr->logfd = -1; - } - - return chr; -} - -void qemu_chr_be_event(CharDriverState *s, int event) +void qemu_chr_be_event(Chardev *s, int event) { CharBackend *be = s->be; @@ -211,7 +181,7 @@ void qemu_chr_be_event(CharDriverState *s, int event) be->chr_event(be->opaque, event); } -void qemu_chr_be_generic_open(CharDriverState *s) +void qemu_chr_be_generic_open(Chardev *s) { qemu_chr_be_event(s, CHR_EVENT_OPENED); } @@ -219,7 +189,7 @@ void qemu_chr_be_generic_open(CharDriverState *s) /* Not reporting errors from writing to logfile, as logs are * defined to be "best effort" only */ -static void qemu_chr_fe_write_log(CharDriverState *s, +static void qemu_chr_fe_write_log(Chardev *s, const uint8_t *buf, size_t len) { size_t done = 0; @@ -244,15 +214,17 @@ static void qemu_chr_fe_write_log(CharDriverState *s, } } -static int qemu_chr_fe_write_buffer(CharDriverState *s, const uint8_t *buf, int len, int *offset) +static int qemu_chr_fe_write_buffer(Chardev *s, + const uint8_t *buf, int len, int *offset) { + ChardevClass *cc = CHARDEV_GET_CLASS(s); int res = 0; *offset = 0; qemu_mutex_lock(&s->chr_write_lock); while (*offset < len) { retry: - res = s->chr_write(s, buf + *offset, len - *offset); + res = cc->chr_write(s, buf + *offset, len - *offset); if (res < 0 && errno == EAGAIN) { g_usleep(100); goto retry; @@ -272,16 +244,22 @@ static int qemu_chr_fe_write_buffer(CharDriverState *s, const uint8_t *buf, int return res; } +static bool qemu_chr_replay(Chardev *chr) +{ + return qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_REPLAY); +} + int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len) { - CharDriverState *s = be->chr; + Chardev *s = be->chr; + ChardevClass *cc; int ret; if (!s) { return 0; } - if (s->replay && replay_mode == REPLAY_MODE_PLAY) { + if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) { int offset; replay_char_write_event_load(&ret, &offset); assert(offset <= len); @@ -289,8 +267,9 @@ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len) return ret; } + cc = CHARDEV_GET_CLASS(s); qemu_mutex_lock(&s->chr_write_lock); - ret = s->chr_write(s, buf, len); + ret = cc->chr_write(s, buf, len); if (ret > 0) { qemu_chr_fe_write_log(s, buf, ret); @@ -298,19 +277,19 @@ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len) qemu_mutex_unlock(&s->chr_write_lock); - if (s->replay && replay_mode == REPLAY_MODE_RECORD) { + if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) { replay_char_write_event_save(ret, ret < 0 ? 0 : ret); } return ret; } -static int qemu_chr_write_all(CharDriverState *s, const uint8_t *buf, int len) +static int qemu_chr_write_all(Chardev *s, const uint8_t *buf, int len) { int offset; int res; - if (s->replay && replay_mode == REPLAY_MODE_PLAY) { + if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) { replay_char_write_event_load(&res, &offset); assert(offset <= len); qemu_chr_fe_write_buffer(s, buf, offset, &offset); @@ -319,7 +298,7 @@ static int qemu_chr_write_all(CharDriverState *s, const uint8_t *buf, int len) res = qemu_chr_fe_write_buffer(s, buf, len, &offset); - if (s->replay && replay_mode == REPLAY_MODE_RECORD) { + if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) { replay_char_write_event_save(res, offset); } @@ -331,7 +310,7 @@ static int qemu_chr_write_all(CharDriverState *s, const uint8_t *buf, int len) int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len) { - CharDriverState *s = be->chr; + Chardev *s = be->chr; if (!s) { return 0; @@ -342,21 +321,22 @@ int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len) int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len) { - CharDriverState *s = be->chr; + Chardev *s = be->chr; int offset = 0, counter = 10; int res; - if (!s || !s->chr_sync_read) { + if (!s || !CHARDEV_GET_CLASS(s)->chr_sync_read) { return 0; } - if (s->replay && replay_mode == REPLAY_MODE_PLAY) { + if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_PLAY) { return replay_char_read_all_load(buf); } while (offset < len) { retry: - res = s->chr_sync_read(s, buf + offset, len - offset); + res = CHARDEV_GET_CLASS(s)->chr_sync_read(s, buf + offset, + len - offset); if (res == -1 && errno == EAGAIN) { g_usleep(100); goto retry; @@ -367,7 +347,7 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len) } if (res < 0) { - if (s->replay && replay_mode == REPLAY_MODE_RECORD) { + if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) { replay_char_read_all_save_error(res); } return res; @@ -380,7 +360,7 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len) } } - if (s->replay && replay_mode == REPLAY_MODE_RECORD) { + if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) { replay_char_read_all_save_buf(buf, offset); } return offset; @@ -388,19 +368,19 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len) int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg) { - CharDriverState *s = be->chr; + Chardev *s = be->chr; int res; - if (!s || !s->chr_ioctl || s->replay) { + if (!s || !CHARDEV_GET_CLASS(s)->chr_ioctl || qemu_chr_replay(s)) { res = -ENOTSUP; } else { - res = s->chr_ioctl(s, cmd, arg); + res = CHARDEV_GET_CLASS(s)->chr_ioctl(s, cmd, arg); } return res; } -int qemu_chr_be_can_write(CharDriverState *s) +int qemu_chr_be_can_write(Chardev *s) { CharBackend *be = s->be; @@ -411,7 +391,7 @@ int qemu_chr_be_can_write(CharDriverState *s) return be->chr_can_read(be->opaque); } -void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len) +void qemu_chr_be_write_impl(Chardev *s, uint8_t *buf, int len) { CharBackend *be = s->be; @@ -420,9 +400,9 @@ void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len) } } -void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len) +void qemu_chr_be_write(Chardev *s, uint8_t *buf, int len) { - if (s->replay) { + if (qemu_chr_replay(s)) { if (replay_mode == REPLAY_MODE_PLAY) { return; } @@ -434,12 +414,12 @@ void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len) int qemu_chr_fe_get_msgfd(CharBackend *be) { - CharDriverState *s = be->chr; + Chardev *s = be->chr; int fd; int res = (qemu_chr_fe_get_msgfds(be, &fd, 1) == 1) ? fd : -1; - if (s && s->replay) { - fprintf(stderr, - "Replay: get msgfd is not supported for serial devices yet\n"); + if (s && qemu_chr_replay(s)) { + error_report("Replay: get msgfd is not supported " + "for serial devices yet"); exit(1); } return res; @@ -447,41 +427,45 @@ int qemu_chr_fe_get_msgfd(CharBackend *be) int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int len) { - CharDriverState *s = be->chr; + Chardev *s = be->chr; if (!s) { return -1; } - return s->get_msgfds ? s->get_msgfds(s, fds, len) : -1; + return CHARDEV_GET_CLASS(s)->get_msgfds ? + CHARDEV_GET_CLASS(s)->get_msgfds(s, fds, len) : -1; } int qemu_chr_fe_set_msgfds(CharBackend *be, int *fds, int num) { - CharDriverState *s = be->chr; + Chardev *s = be->chr; if (!s) { return -1; } - return s->set_msgfds ? s->set_msgfds(s, fds, num) : -1; + return CHARDEV_GET_CLASS(s)->set_msgfds ? + CHARDEV_GET_CLASS(s)->set_msgfds(s, fds, num) : -1; } -int qemu_chr_add_client(CharDriverState *s, int fd) +int qemu_chr_add_client(Chardev *s, int fd) { - return s->chr_add_client ? s->chr_add_client(s, fd) : -1; + return CHARDEV_GET_CLASS(s)->chr_add_client ? + CHARDEV_GET_CLASS(s)->chr_add_client(s, fd) : -1; } void qemu_chr_fe_accept_input(CharBackend *be) { - CharDriverState *s = be->chr; + Chardev *s = be->chr; if (!s) { return; } - if (s->chr_accept_input) - s->chr_accept_input(s); + if (CHARDEV_GET_CLASS(s)->chr_accept_input) { + CHARDEV_GET_CLASS(s)->chr_accept_input(s); + } qemu_notify_event(); } @@ -497,38 +481,110 @@ void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...) va_end(ap); } -static void remove_fd_in_watch(CharDriverState *chr); -static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context); -static void mux_set_focus(CharDriverState *chr, int focus); +static void remove_fd_in_watch(Chardev *chr); +static void mux_chr_set_handlers(Chardev *chr, GMainContext *context); +static void mux_set_focus(Chardev *chr, int focus); -static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +static void qemu_char_open(Chardev *chr, ChardevBackend *backend, + bool *be_opened, Error **errp) { - return len; + ChardevClass *cc = CHARDEV_GET_CLASS(chr); + /* Any ChardevCommon member would work */ + ChardevCommon *common = backend ? backend->u.null.data : NULL; + + if (common && common->has_logfile) { + int flags = O_WRONLY | O_CREAT; + if (common->has_logappend && + common->logappend) { + flags |= O_APPEND; + } else { + flags |= O_TRUNC; + } + chr->logfd = qemu_open(common->logfile, flags, 0666); + if (chr->logfd < 0) { + error_setg_errno(errp, errno, + "Unable to open logfile %s", + common->logfile); + return; + } + } + + if (cc->open) { + cc->open(chr, backend, be_opened, errp); + } } -static CharDriverState *qemu_chr_open_null(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, - bool *be_opened, - Error **errp) +static void char_init(Object *obj) { - CharDriverState *chr; - ChardevCommon *common = backend->u.null.data; + Chardev *chr = CHARDEV(obj); - chr = qemu_chr_alloc(common, errp); - if (!chr) { - return NULL; + chr->logfd = -1; + qemu_mutex_init(&chr->chr_write_lock); +} + +static void char_finalize(Object *obj) +{ + Chardev *chr = CHARDEV(obj); + + if (chr->be) { + chr->be->chr = NULL; + } + g_free(chr->filename); + g_free(chr->label); + if (chr->logfd != -1) { + close(chr->logfd); } - chr->chr_write = null_chr_write; + qemu_mutex_destroy(&chr->chr_write_lock); +} + +static const TypeInfo char_type_info = { + .name = TYPE_CHARDEV, + .parent = TYPE_OBJECT, + .instance_size = sizeof(Chardev), + .instance_init = char_init, + .instance_finalize = char_finalize, + .abstract = true, + .class_size = sizeof(ChardevClass), +}; + +static int null_chr_write(Chardev *chr, const uint8_t *buf, int len) +{ + return len; +} + +static void null_chr_open(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) +{ *be_opened = false; - return chr; } +static const CharDriver null_driver = { + .kind = CHARDEV_BACKEND_KIND_NULL, +}; + +static void char_null_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->open = null_chr_open; + cc->chr_write = null_chr_write; +} + +static const TypeInfo char_null_type_info = { + .name = TYPE_CHARDEV_NULL, + .parent = TYPE_CHARDEV, + .instance_size = sizeof(Chardev), + .class_init = char_null_class_init, +}; + /* MUX driver for serial I/O splitting */ #define MAX_MUX 4 #define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */ #define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1) -struct MuxDriver { +struct MuxChardev { + Chardev parent; CharBackend *backends[MAX_MUX]; CharBackend chr; int focus; @@ -543,15 +599,17 @@ struct MuxDriver { int cons[MAX_MUX]; int timestamps; - /* Protected by the CharDriverState chr_write_lock. */ + /* Protected by the Chardev chr_write_lock. */ int linestart; int64_t timestamps_start; }; +#define MUX_CHARDEV(obj) OBJECT_CHECK(MuxChardev, (obj), TYPE_CHARDEV_MUX) + /* Called with chr_write_lock held. */ -static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +static int mux_chr_write(Chardev *chr, const uint8_t *buf, int len) { - MuxDriver *d = chr->opaque; + MuxChardev *d = MUX_CHARDEV(chr); int ret; if (!d->timestamps) { ret = qemu_chr_fe_write(&d->chr, buf, len); @@ -603,7 +661,7 @@ static const char * const mux_help[] = { }; int term_escape_char = 0x01; /* ctrl-a is used for escape */ -static void mux_print_help(CharDriverState *chr) +static void mux_print_help(Chardev *chr) { int i, j; char ebuf[15] = "Escape-Char"; @@ -630,7 +688,7 @@ static void mux_print_help(CharDriverState *chr) } } -static void mux_chr_send_event(MuxDriver *d, int mux_nr, int event) +static void mux_chr_send_event(MuxChardev *d, int mux_nr, int event) { CharBackend *be = d->backends[mux_nr]; @@ -639,7 +697,7 @@ static void mux_chr_send_event(MuxDriver *d, int mux_nr, int event) } } -static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch) +static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch) { if (d->term_got_escape) { d->term_got_escape = 0; @@ -683,9 +741,9 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch) return 0; } -static void mux_chr_accept_input(CharDriverState *chr) +static void mux_chr_accept_input(Chardev *chr) { - MuxDriver *d = chr->opaque; + MuxChardev *d = MUX_CHARDEV(chr); int m = d->focus; CharBackend *be = d->backends[m]; @@ -698,8 +756,7 @@ static void mux_chr_accept_input(CharDriverState *chr) static int mux_chr_can_read(void *opaque) { - CharDriverState *chr = opaque; - MuxDriver *d = chr->opaque; + MuxChardev *d = MUX_CHARDEV(opaque); int m = d->focus; CharBackend *be = d->backends[m]; @@ -716,8 +773,8 @@ static int mux_chr_can_read(void *opaque) static void mux_chr_read(void *opaque, const uint8_t *buf, int size) { - CharDriverState *chr = opaque; - MuxDriver *d = chr->opaque; + Chardev *chr = CHARDEV(opaque); + MuxChardev *d = MUX_CHARDEV(opaque); int m = d->focus; CharBackend *be = d->backends[m]; int i; @@ -739,8 +796,7 @@ static bool muxes_realized; static void mux_chr_event(void *opaque, int event) { - CharDriverState *chr = opaque; - MuxDriver *d = chr->opaque; + MuxChardev *d = MUX_CHARDEV(opaque); int i; if (!muxes_realized) { @@ -766,11 +822,11 @@ static void mux_chr_event(void *opaque, int event) */ static void muxes_realize_done(Notifier *notifier, void *unused) { - CharDriverState *chr; + Chardev *chr; QTAILQ_FOREACH(chr, &chardevs, next) { - if (chr->is_mux) { - MuxDriver *d = chr->opaque; + if (CHARDEV_IS_MUX(chr)) { + MuxChardev *d = MUX_CHARDEV(chr); int i; /* send OPENED to all already-attached FEs */ @@ -790,17 +846,22 @@ static Notifier muxes_realize_notify = { .notify = muxes_realize_done, }; -static GSource *mux_chr_add_watch(CharDriverState *s, GIOCondition cond) +static GSource *mux_chr_add_watch(Chardev *s, GIOCondition cond) { - MuxDriver *d = s->opaque; - CharDriverState *chr = qemu_chr_fe_get_driver(&d->chr); + MuxChardev *d = MUX_CHARDEV(s); + Chardev *chr = qemu_chr_fe_get_driver(&d->chr); + ChardevClass *cc = CHARDEV_GET_CLASS(chr); + + if (!cc->chr_add_watch) { + return NULL; + } - return chr->chr_add_watch(chr, cond); + return cc->chr_add_watch(chr, cond); } -static void mux_chr_free(struct CharDriverState *chr) +static void mux_chr_free(struct Chardev *chr) { - MuxDriver *d = chr->opaque; + MuxChardev *d = MUX_CHARDEV(chr); int i; for (i = 0; i < d->mux_cnt; i++) { @@ -810,12 +871,11 @@ static void mux_chr_free(struct CharDriverState *chr) } } qemu_chr_fe_deinit(&d->chr); - g_free(d); } -static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context) +static void mux_chr_set_handlers(Chardev *chr, GMainContext *context) { - MuxDriver *d = chr->opaque; + MuxChardev *d = MUX_CHARDEV(chr); /* Fix up the real driver with mux routines */ qemu_chr_fe_set_handlers(&d->chr, @@ -826,9 +886,9 @@ static void mux_chr_set_handlers(CharDriverState *chr, GMainContext *context) context, true); } -static void mux_set_focus(CharDriverState *chr, int focus) +static void mux_set_focus(Chardev *chr, int focus) { - MuxDriver *d = chr->opaque; + MuxChardev *d = MUX_CHARDEV(chr); assert(focus >= 0); assert(focus < d->mux_cnt); @@ -842,63 +902,40 @@ static void mux_set_focus(CharDriverState *chr, int focus) mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN); } -static CharDriverState *qemu_chr_open_mux(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, - bool *be_opened, - Error **errp) +static void qemu_chr_open_mux(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { ChardevMux *mux = backend->u.mux.data; - CharDriverState *chr, *drv; - MuxDriver *d; - ChardevCommon *common = qapi_ChardevMux_base(mux); + Chardev *drv; + MuxChardev *d = MUX_CHARDEV(chr); drv = qemu_chr_find(mux->chardev); if (drv == NULL) { error_setg(errp, "mux: base chardev %s not found", mux->chardev); - return NULL; - } - - chr = qemu_chr_alloc(common, errp); - if (!chr) { - return NULL; + return; } - d = g_new0(MuxDriver, 1); - chr->opaque = d; d->focus = -1; - chr->chr_free = mux_chr_free; - chr->chr_write = mux_chr_write; - chr->chr_accept_input = mux_chr_accept_input; - /* Frontend guest-open / -close notification is not support with muxes */ - chr->chr_set_fe_open = NULL; - if (drv->chr_add_watch) { - chr->chr_add_watch = mux_chr_add_watch; - } /* only default to opened state if we've realized the initial * set of muxes */ *be_opened = muxes_realized; - chr->is_mux = 1; - if (!qemu_chr_fe_init(&d->chr, drv, errp)) { - qemu_chr_free(chr); - return NULL; - } - - return chr; + qemu_chr_fe_init(&d->chr, drv, errp); } -CharDriverState *qemu_chr_fe_get_driver(CharBackend *be) +Chardev *qemu_chr_fe_get_driver(CharBackend *be) { return be->chr; } -bool qemu_chr_fe_init(CharBackend *b, CharDriverState *s, Error **errp) +bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp) { int tag = 0; - if (s->is_mux) { - MuxDriver *d = s->opaque; + if (CHARDEV_IS_MUX(s)) { + MuxChardev *d = MUX_CHARDEV(s); if (d->mux_cnt >= MAX_MUX) { goto unavailable; @@ -922,10 +959,10 @@ unavailable: return false; } -static bool qemu_chr_is_busy(CharDriverState *s) +static bool qemu_chr_is_busy(Chardev *s) { - if (s->is_mux) { - MuxDriver *d = s->opaque; + if (CHARDEV_IS_MUX(s)) { + MuxChardev *d = MUX_CHARDEV(s); return d->mux_cnt >= 0; } else { return s->be != NULL; @@ -941,8 +978,8 @@ void qemu_chr_fe_deinit(CharBackend *b) if (b->chr->be == b) { b->chr->be = NULL; } - if (b->chr->is_mux) { - MuxDriver *d = b->chr->opaque; + if (CHARDEV_IS_MUX(b->chr)) { + MuxChardev *d = MUX_CHARDEV(b->chr); d->backends[b->tag] = NULL; } b->chr = NULL; @@ -957,7 +994,8 @@ void qemu_chr_fe_set_handlers(CharBackend *b, GMainContext *context, bool set_open) { - CharDriverState *s; + Chardev *s; + ChardevClass *cc; int fe_open; s = b->chr; @@ -965,6 +1003,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b, return; } + cc = CHARDEV_GET_CLASS(s); if (!opaque && !fd_can_read && !fd_read && !fd_event) { fe_open = 0; remove_fd_in_watch(s); @@ -975,8 +1014,8 @@ void qemu_chr_fe_set_handlers(CharBackend *b, b->chr_read = fd_read; b->chr_event = fd_event; b->opaque = opaque; - if (s->chr_update_read_handler) { - s->chr_update_read_handler(s, context); + if (cc->chr_update_read_handler) { + cc->chr_update_read_handler(s, context); } if (set_open) { @@ -992,7 +1031,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b, } } - if (s->is_mux) { + if (CHARDEV_IS_MUX(s)) { mux_chr_set_handlers(s, context); } } @@ -1003,7 +1042,7 @@ void qemu_chr_fe_take_focus(CharBackend *b) return; } - if (b->chr->is_mux) { + if (CHARDEV_IS_MUX(b->chr)) { mux_set_focus(b->chr, b->tag); } } @@ -1084,7 +1123,7 @@ static GSourceFuncs io_watch_poll_funcs = { }; /* Can only be used for read */ -static guint io_add_watch_poll(CharDriverState *chr, +static guint io_add_watch_poll(Chardev *chr, QIOChannel *ioc, IOCanReadHandler *fd_can_read, QIOChannelFunc fd_read, @@ -1132,7 +1171,7 @@ static void io_remove_watch_poll(guint tag) g_source_destroy(&iwp->parent); } -static void remove_fd_in_watch(CharDriverState *chr) +static void remove_fd_in_watch(Chardev *chr) { if (chr->fd_in_tag) { io_remove_watch_poll(chr->fd_in_tag); @@ -1180,25 +1219,28 @@ static int io_channel_send(QIOChannel *ioc, const void *buf, size_t len) return io_channel_send_full(ioc, buf, len, NULL, 0); } - -typedef struct FDCharDriver { - CharDriverState *chr; +typedef struct FDChardev { + Chardev parent; + Chardev *chr; QIOChannel *ioc_in, *ioc_out; int max_size; -} FDCharDriver; +} FDChardev; + +#define TYPE_CHARDEV_FD "chardev-fd" +#define FD_CHARDEV(obj) OBJECT_CHECK(FDChardev, (obj), TYPE_CHARDEV_FD) /* Called with chr_write_lock held. */ -static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +static int fd_chr_write(Chardev *chr, const uint8_t *buf, int len) { - FDCharDriver *s = chr->opaque; - + FDChardev *s = FD_CHARDEV(chr); + return io_channel_send(s->ioc_out, buf, len); } static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) { - CharDriverState *chr = opaque; - FDCharDriver *s = chr->opaque; + Chardev *chr = CHARDEV(opaque); + FDChardev *s = FD_CHARDEV(opaque); int len; uint8_t buf[READ_BUF_LEN]; ssize_t ret; @@ -1227,23 +1269,23 @@ static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) static int fd_chr_read_poll(void *opaque) { - CharDriverState *chr = opaque; - FDCharDriver *s = chr->opaque; + Chardev *chr = CHARDEV(opaque); + FDChardev *s = FD_CHARDEV(opaque); s->max_size = qemu_chr_be_can_write(chr); return s->max_size; } -static GSource *fd_chr_add_watch(CharDriverState *chr, GIOCondition cond) +static GSource *fd_chr_add_watch(Chardev *chr, GIOCondition cond) { - FDCharDriver *s = chr->opaque; + FDChardev *s = FD_CHARDEV(chr); return qio_channel_create_watch(s->ioc_out, cond); } -static void fd_chr_update_read_handler(CharDriverState *chr, +static void fd_chr_update_read_handler(Chardev *chr, GMainContext *context) { - FDCharDriver *s = chr->opaque; + FDChardev *s = FD_CHARDEV(chr); remove_fd_in_watch(chr); if (s->ioc_in) { @@ -1254,9 +1296,9 @@ static void fd_chr_update_read_handler(CharDriverState *chr, } } -static void fd_chr_free(struct CharDriverState *chr) +static void fd_chr_free(struct Chardev *chr) { - FDCharDriver *s = chr->opaque; + FDChardev *s = FD_CHARDEV(chr); remove_fd_in_watch(chr); if (s->ioc_in) { @@ -1266,23 +1308,16 @@ static void fd_chr_free(struct CharDriverState *chr) object_unref(OBJECT(s->ioc_out)); } - g_free(s); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } /* open a character device to a unix fd */ -static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out, - ChardevCommon *backend, Error **errp) +static void qemu_chr_open_fd(Chardev *chr, + int fd_in, int fd_out) { - CharDriverState *chr; - FDCharDriver *s; + FDChardev *s = FD_CHARDEV(chr); char *name; - chr = qemu_chr_alloc(backend, errp); - if (!chr) { - return NULL; - } - s = g_new0(FDCharDriver, 1); s->ioc_in = QIO_CHANNEL(qio_channel_file_new_fd(fd_in)); name = g_strdup_printf("chardev-file-in-%s", chr->label); qio_channel_set_name(QIO_CHANNEL(s->ioc_in), name); @@ -1293,28 +1328,36 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out, g_free(name); qemu_set_nonblock(fd_out); s->chr = chr; - chr->opaque = s; - chr->chr_add_watch = fd_chr_add_watch; - chr->chr_write = fd_chr_write; - chr->chr_update_read_handler = fd_chr_update_read_handler; - chr->chr_free = fd_chr_free; +} - return chr; +static void char_fd_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->chr_add_watch = fd_chr_add_watch; + cc->chr_write = fd_chr_write; + cc->chr_update_read_handler = fd_chr_update_read_handler; + cc->chr_free = fd_chr_free; } -static CharDriverState *qemu_chr_open_pipe(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, - bool *be_opened, - Error **errp) +static const TypeInfo char_fd_type_info = { + .name = TYPE_CHARDEV_FD, + .parent = TYPE_CHARDEV, + .instance_size = sizeof(FDChardev), + .class_init = char_fd_class_init, + .abstract = true, +}; + +static void qemu_chr_open_pipe(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { ChardevHostdev *opts = backend->u.pipe.data; int fd_in, fd_out; char *filename_in; char *filename_out; const char *filename = opts->device; - ChardevCommon *common = qapi_ChardevHostdev_base(opts); - filename_in = g_strdup_printf("%s.in", filename); filename_out = g_strdup_printf("%s.out", filename); @@ -1330,10 +1373,10 @@ static CharDriverState *qemu_chr_open_pipe(const char *id, TFR(fd_in = fd_out = qemu_open(filename, O_RDWR | O_BINARY)); if (fd_in < 0) { error_setg_file_open(errp, errno, filename); - return NULL; + return; } } - return qemu_chr_open_fd(fd_in, fd_out, common, errp); + qemu_chr_open_fd(chr, fd_in, fd_out); } /* init terminal so that we can grab keys */ @@ -1343,7 +1386,7 @@ static bool stdio_in_use; static bool stdio_allow_signal; static bool stdio_echo_state; -static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo); +static void qemu_chr_set_echo_stdio(Chardev *chr, bool echo); static void term_exit(void) { @@ -1357,7 +1400,7 @@ static void term_stdio_handler(int sig) qemu_chr_set_echo_stdio(NULL, stdio_echo_state); } -static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo) +static void qemu_chr_set_echo_stdio(Chardev *chr, bool echo) { struct termios tty; @@ -1379,31 +1422,28 @@ static void qemu_chr_set_echo_stdio(CharDriverState *chr, bool echo) tcsetattr (0, TCSANOW, &tty); } -static void qemu_chr_free_stdio(struct CharDriverState *chr) +static void qemu_chr_free_stdio(struct Chardev *chr) { term_exit(); fd_chr_free(chr); } -static CharDriverState *qemu_chr_open_stdio(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, - bool *be_opened, - Error **errp) +static void qemu_chr_open_stdio(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { ChardevStdio *opts = backend->u.stdio.data; - CharDriverState *chr; struct sigaction act; - ChardevCommon *common = qapi_ChardevStdio_base(opts); if (is_daemonized()) { error_setg(errp, "cannot use stdio with -daemonize"); - return NULL; + return; } if (stdio_in_use) { error_setg(errp, "cannot use stdio by multiple character devices"); - return NULL; + return; } stdio_in_use = true; @@ -1416,18 +1456,12 @@ static CharDriverState *qemu_chr_open_stdio(const char *id, act.sa_handler = term_stdio_handler; sigaction(SIGCONT, &act, NULL); - chr = qemu_chr_open_fd(0, 1, common, errp); - if (!chr) { - return NULL; - } - chr->chr_free = qemu_chr_free_stdio; - chr->chr_set_echo = qemu_chr_set_echo_stdio; + qemu_chr_open_fd(chr, 0, 1); + if (opts->has_signal) { stdio_allow_signal = opts->signal; } qemu_chr_set_echo_stdio(chr, false); - - return chr; } #if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ @@ -1438,22 +1472,25 @@ static CharDriverState *qemu_chr_open_stdio(const char *id, #define HAVE_CHARDEV_PTY 1 typedef struct { + Chardev parent; QIOChannel *ioc; int read_bytes; - /* Protected by the CharDriverState chr_write_lock. */ + /* Protected by the Chardev chr_write_lock. */ int connected; guint timer_tag; guint open_tag; -} PtyCharDriver; +} PtyChardev; -static void pty_chr_update_read_handler_locked(CharDriverState *chr); -static void pty_chr_state(CharDriverState *chr, int connected); +#define PTY_CHARDEV(obj) OBJECT_CHECK(PtyChardev, (obj), TYPE_CHARDEV_PTY) + +static void pty_chr_update_read_handler_locked(Chardev *chr); +static void pty_chr_state(Chardev *chr, int connected); static gboolean pty_chr_timer(gpointer opaque) { - struct CharDriverState *chr = opaque; - PtyCharDriver *s = chr->opaque; + struct Chardev *chr = CHARDEV(opaque); + PtyChardev *s = PTY_CHARDEV(opaque); qemu_mutex_lock(&chr->chr_write_lock); s->timer_tag = 0; @@ -1467,9 +1504,9 @@ static gboolean pty_chr_timer(gpointer opaque) } /* Called with chr_write_lock held. */ -static void pty_chr_rearm_timer(CharDriverState *chr, int ms) +static void pty_chr_rearm_timer(Chardev *chr, int ms) { - PtyCharDriver *s = chr->opaque; + PtyChardev *s = PTY_CHARDEV(chr); char *name; if (s->timer_tag) { @@ -1489,9 +1526,9 @@ static void pty_chr_rearm_timer(CharDriverState *chr, int ms) } /* Called with chr_write_lock held. */ -static void pty_chr_update_read_handler_locked(CharDriverState *chr) +static void pty_chr_update_read_handler_locked(Chardev *chr) { - PtyCharDriver *s = chr->opaque; + PtyChardev *s = PTY_CHARDEV(chr); GPollFD pfd; int rc; QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc); @@ -1511,7 +1548,7 @@ static void pty_chr_update_read_handler_locked(CharDriverState *chr) } } -static void pty_chr_update_read_handler(CharDriverState *chr, +static void pty_chr_update_read_handler(Chardev *chr, GMainContext *context) { qemu_mutex_lock(&chr->chr_write_lock); @@ -1520,9 +1557,9 @@ static void pty_chr_update_read_handler(CharDriverState *chr, } /* Called with chr_write_lock held. */ -static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +static int char_pty_chr_write(Chardev *chr, const uint8_t *buf, int len) { - PtyCharDriver *s = chr->opaque; + PtyChardev *s = PTY_CHARDEV(chr); if (!s->connected) { /* guest sends data, check for (re-)connect */ @@ -1534,9 +1571,9 @@ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len) return io_channel_send(s->ioc, buf, len); } -static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond) +static GSource *pty_chr_add_watch(Chardev *chr, GIOCondition cond) { - PtyCharDriver *s = chr->opaque; + PtyChardev *s = PTY_CHARDEV(chr); if (!s->connected) { return NULL; } @@ -1545,8 +1582,8 @@ static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond) static int pty_chr_read_poll(void *opaque) { - CharDriverState *chr = opaque; - PtyCharDriver *s = chr->opaque; + Chardev *chr = CHARDEV(opaque); + PtyChardev *s = PTY_CHARDEV(opaque); s->read_bytes = qemu_chr_be_can_write(chr); return s->read_bytes; @@ -1554,8 +1591,8 @@ static int pty_chr_read_poll(void *opaque) static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) { - CharDriverState *chr = opaque; - PtyCharDriver *s = chr->opaque; + Chardev *chr = CHARDEV(opaque); + PtyChardev *s = PTY_CHARDEV(opaque); gsize len; uint8_t buf[READ_BUF_LEN]; ssize_t ret; @@ -1579,8 +1616,8 @@ static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) static gboolean qemu_chr_be_generic_open_func(gpointer opaque) { - CharDriverState *chr = opaque; - PtyCharDriver *s = chr->opaque; + Chardev *chr = CHARDEV(opaque); + PtyChardev *s = PTY_CHARDEV(opaque); s->open_tag = 0; qemu_chr_be_generic_open(chr); @@ -1588,9 +1625,9 @@ static gboolean qemu_chr_be_generic_open_func(gpointer opaque) } /* Called with chr_write_lock held. */ -static void pty_chr_state(CharDriverState *chr, int connected) +static void pty_chr_state(Chardev *chr, int connected) { - PtyCharDriver *s = chr->opaque; + PtyChardev *s = PTY_CHARDEV(chr); if (!connected) { if (s->open_tag) { @@ -1622,9 +1659,9 @@ static void pty_chr_state(CharDriverState *chr, int connected) } } -static void pty_chr_free(struct CharDriverState *chr) +static void pty_chr_free(struct Chardev *chr) { - PtyCharDriver *s = chr->opaque; + PtyChardev *s = PTY_CHARDEV(chr); qemu_mutex_lock(&chr->chr_write_lock); pty_chr_state(chr, 0); @@ -1634,62 +1671,63 @@ static void pty_chr_free(struct CharDriverState *chr) s->timer_tag = 0; } qemu_mutex_unlock(&chr->chr_write_lock); - g_free(s); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_pty(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, - bool *be_opened, - Error **errp) +static void char_pty_open(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { - CharDriverState *chr; - PtyCharDriver *s; + PtyChardev *s; int master_fd, slave_fd; char pty_name[PATH_MAX]; - ChardevCommon *common = backend->u.pty.data; char *name; master_fd = qemu_openpty_raw(&slave_fd, pty_name); if (master_fd < 0) { error_setg_errno(errp, errno, "Failed to create PTY"); - return NULL; + return; } close(slave_fd); qemu_set_nonblock(master_fd); - chr = qemu_chr_alloc(common, errp); - if (!chr) { - close(master_fd); - return NULL; - } - chr->filename = g_strdup_printf("pty:%s", pty_name); - ret->pty = g_strdup(pty_name); - ret->has_pty = true; - - fprintf(stderr, "char device redirected to %s (label %s)\n", - pty_name, id); - - s = g_new0(PtyCharDriver, 1); - chr->opaque = s; - chr->chr_write = pty_chr_write; - chr->chr_update_read_handler = pty_chr_update_read_handler; - chr->chr_free = pty_chr_free; - chr->chr_add_watch = pty_chr_add_watch; - *be_opened = false; + error_report("char device redirected to %s (label %s)", + pty_name, chr->label); + s = PTY_CHARDEV(chr); s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd)); name = g_strdup_printf("chardev-pty-%s", chr->label); qio_channel_set_name(QIO_CHANNEL(s->ioc), name); g_free(name); s->timer_tag = 0; + *be_opened = false; +} - return chr; +static const CharDriver pty_driver = { + .kind = CHARDEV_BACKEND_KIND_PTY, +}; + +static void char_pty_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->open = char_pty_open; + cc->chr_write = char_pty_chr_write; + cc->chr_update_read_handler = pty_chr_update_read_handler; + cc->chr_add_watch = pty_chr_add_watch; + cc->chr_free = pty_chr_free; } +static const TypeInfo char_pty_type_info = { + .name = TYPE_CHARDEV_PTY, + .parent = TYPE_CHARDEV, + .instance_size = sizeof(PtyChardev), + .class_init = char_pty_class_init, +}; + static void tty_serial_init(int fd, int speed, int parity, int data_bits, int stop_bits) { @@ -1805,9 +1843,9 @@ static void tty_serial_init(int fd, int speed, tcsetattr (fd, TCSANOW, &tty); } -static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) +static int tty_serial_ioctl(Chardev *chr, int cmd, void *arg) { - FDCharDriver *s = chr->opaque; + FDChardev *s = FD_CHARDEV(chr); QIOChannelFile *fioc = QIO_CHANNEL_FILE(s->ioc_in); switch(cmd) { @@ -1875,27 +1913,10 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) return 0; } -static void qemu_chr_free_tty(CharDriverState *chr) +static void qemu_chr_free_tty(Chardev *chr) { fd_chr_free(chr); } - -static CharDriverState *qemu_chr_open_tty_fd(int fd, - ChardevCommon *backend, - bool *be_opened, - Error **errp) -{ - CharDriverState *chr; - - tty_serial_init(fd, 115200, 'N', 8, 1); - chr = qemu_chr_open_fd(fd, fd, backend, errp); - if (!chr) { - return NULL; - } - chr->chr_ioctl = tty_serial_ioctl; - chr->chr_free = qemu_chr_free_tty; - return chr; -} #endif /* __linux__ || __sun__ */ #if defined(__linux__) @@ -1903,11 +1924,15 @@ static CharDriverState *qemu_chr_open_tty_fd(int fd, #define HAVE_CHARDEV_PARPORT 1 typedef struct { + Chardev parent; int fd; int mode; -} ParallelCharDriver; +} ParallelChardev; + +#define PARALLEL_CHARDEV(obj) \ + OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL) -static int pp_hw_mode(ParallelCharDriver *s, uint16_t mode) +static int pp_hw_mode(ParallelChardev *s, uint16_t mode) { if (s->mode != mode) { int m = mode; @@ -1918,9 +1943,9 @@ static int pp_hw_mode(ParallelCharDriver *s, uint16_t mode) return 1; } -static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) +static int pp_ioctl(Chardev *chr, int cmd, void *arg) { - ParallelCharDriver *drv = chr->opaque; + ParallelChardev *drv = PARALLEL_CHARDEV(chr); int fd = drv->fd; uint8_t b; @@ -1999,47 +2024,32 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) return 0; } -static void pp_free(CharDriverState *chr) +static void pp_free(Chardev *chr) { - ParallelCharDriver *drv = chr->opaque; + ParallelChardev *drv = PARALLEL_CHARDEV(chr); int fd = drv->fd; pp_hw_mode(drv, IEEE1284_MODE_COMPAT); ioctl(fd, PPRELEASE); close(fd); - g_free(drv); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_pp_fd(int fd, - ChardevCommon *backend, - bool *be_opened, - Error **errp) +static void qemu_chr_open_pp_fd(Chardev *chr, + int fd, + bool *be_opened, + Error **errp) { - CharDriverState *chr; - ParallelCharDriver *drv; + ParallelChardev *drv = PARALLEL_CHARDEV(chr); if (ioctl(fd, PPCLAIM) < 0) { error_setg_errno(errp, errno, "not a parallel port"); close(fd); - return NULL; - } - - chr = qemu_chr_alloc(backend, errp); - if (!chr) { - return NULL; + return; } - drv = g_new0(ParallelCharDriver, 1); - chr->opaque = drv; - chr->chr_write = null_chr_write; - chr->chr_ioctl = pp_ioctl; - chr->chr_free = pp_free; - drv->fd = fd; drv->mode = IEEE1284_MODE_COMPAT; - - return chr; } #endif /* __linux__ */ @@ -2047,35 +2057,48 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd, #define HAVE_CHARDEV_PARPORT 1 -static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) +typedef struct { + Chardev parent; + int fd; +} ParallelChardev; + +#define PARALLEL_CHARDEV(obj) \ + OBJECT_CHECK(ParallelChardev, (obj), TYPE_CHARDEV_PARALLEL) + +static int pp_ioctl(Chardev *chr, int cmd, void *arg) { - int fd = (int)(intptr_t)chr->opaque; + ParallelChardev *drv = PARALLEL_CHARDEV(chr); uint8_t b; - switch(cmd) { + switch (cmd) { case CHR_IOCTL_PP_READ_DATA: - if (ioctl(fd, PPIGDATA, &b) < 0) + if (ioctl(drv->fd, PPIGDATA, &b) < 0) { return -ENOTSUP; + } *(uint8_t *)arg = b; break; case CHR_IOCTL_PP_WRITE_DATA: b = *(uint8_t *)arg; - if (ioctl(fd, PPISDATA, &b) < 0) + if (ioctl(drv->fd, PPISDATA, &b) < 0) { return -ENOTSUP; + } break; case CHR_IOCTL_PP_READ_CONTROL: - if (ioctl(fd, PPIGCTRL, &b) < 0) + if (ioctl(drv->fd, PPIGCTRL, &b) < 0) { return -ENOTSUP; + } *(uint8_t *)arg = b; break; case CHR_IOCTL_PP_WRITE_CONTROL: b = *(uint8_t *)arg; - if (ioctl(fd, PPISCTRL, &b) < 0) + if (ioctl(drv->fd, PPISCTRL, &b) < 0) { return -ENOTSUP; + } break; case CHR_IOCTL_PP_READ_STATUS: - if (ioctl(fd, PPIGSTATUS, &b) < 0) + if (ioctl(drv->fd, PPIGSTATUS, &b) < 0) { return -ENOTSUP; + } *(uint8_t *)arg = b; break; default: @@ -2084,22 +2107,14 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) return 0; } -static CharDriverState *qemu_chr_open_pp_fd(int fd, - ChardevCommon *backend, - bool *be_opened, - Error **errp) +static void qemu_chr_open_pp_fd(Chardev *chr, + int fd, + bool *be_opened, + Error **errp) { - CharDriverState *chr; - - chr = qemu_chr_alloc(backend, errp); - if (!chr) { - return NULL; - } - chr->opaque = (void *)(intptr_t)fd; - chr->chr_write = null_chr_write; - chr->chr_ioctl = pp_ioctl; + ParallelChardev *drv = PARALLEL_CHARDEV(chr); + drv->fd = fd; *be_opened = false; - return chr; } #endif @@ -2108,23 +2123,32 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd, #define HAVE_CHARDEV_SERIAL 1 typedef struct { + Chardev parent; int max_size; HANDLE hcom, hrecv, hsend; OVERLAPPED orecv; BOOL fpipe; DWORD len; - /* Protected by the CharDriverState chr_write_lock. */ + /* Protected by the Chardev chr_write_lock. */ OVERLAPPED osend; -} WinCharState; +} WinChardev; + +#define TYPE_CHARDEV_WIN "chardev-win" +#define WIN_CHARDEV(obj) OBJECT_CHECK(WinChardev, (obj), TYPE_CHARDEV_WIN) typedef struct { + Chardev parent; HANDLE hStdIn; HANDLE hInputReadyEvent; HANDLE hInputDoneEvent; HANDLE hInputThread; uint8_t win_stdio_buf; -} WinStdioCharState; +} WinStdioChardev; + +#define TYPE_CHARDEV_WIN_STDIO "chardev-win-stdio" +#define WIN_STDIO_CHARDEV(obj) \ + OBJECT_CHECK(WinStdioChardev, (obj), TYPE_CHARDEV_WIN_STDIO) #define NSENDBUF 2048 #define NRECVBUF 2048 @@ -2134,9 +2158,9 @@ typedef struct { static int win_chr_poll(void *opaque); static int win_chr_pipe_poll(void *opaque); -static void win_chr_free(CharDriverState *chr) +static void win_chr_free(Chardev *chr) { - WinCharState *s = chr->opaque; + WinChardev *s = WIN_CHARDEV(chr); if (s->hsend) { CloseHandle(s->hsend); @@ -2158,9 +2182,9 @@ static void win_chr_free(CharDriverState *chr) qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static int win_chr_init(CharDriverState *chr, const char *filename, Error **errp) +static int win_chr_init(Chardev *chr, const char *filename, Error **errp) { - WinCharState *s = chr->opaque; + WinChardev *s = WIN_CHARDEV(chr); COMMCONFIG comcfg; COMMTIMEOUTS cto = { 0, 0, 0, 0, 0}; COMSTAT comstat; @@ -2226,9 +2250,9 @@ static int win_chr_init(CharDriverState *chr, const char *filename, Error **errp } /* Called with chr_write_lock held. */ -static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1) +static int win_chr_write(Chardev *chr, const uint8_t *buf, int len1) { - WinCharState *s = chr->opaque; + WinChardev *s = WIN_CHARDEV(chr); DWORD len, ret, size, err; len = len1; @@ -2260,17 +2284,18 @@ static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1) return len1 - len; } -static int win_chr_read_poll(CharDriverState *chr) +static int win_chr_read_poll(Chardev *chr) { - WinCharState *s = chr->opaque; + WinChardev *s = WIN_CHARDEV(chr); s->max_size = qemu_chr_be_can_write(chr); return s->max_size; } -static void win_chr_readfile(CharDriverState *chr) +static void win_chr_readfile(Chardev *chr) { - WinCharState *s = chr->opaque; + WinChardev *s = WIN_CHARDEV(chr); + int ret, err; uint8_t buf[READ_BUF_LEN]; DWORD size; @@ -2290,9 +2315,9 @@ static void win_chr_readfile(CharDriverState *chr) } } -static void win_chr_read(CharDriverState *chr) +static void win_chr_read(Chardev *chr) { - WinCharState *s = chr->opaque; + WinChardev *s = WIN_CHARDEV(chr); if (s->len > s->max_size) s->len = s->max_size; @@ -2304,8 +2329,8 @@ static void win_chr_read(CharDriverState *chr) static int win_chr_poll(void *opaque) { - CharDriverState *chr = opaque; - WinCharState *s = chr->opaque; + Chardev *chr = CHARDEV(opaque); + WinChardev *s = WIN_CHARDEV(opaque); COMSTAT status; DWORD comerr; @@ -2319,34 +2344,10 @@ static int win_chr_poll(void *opaque) return 0; } -static CharDriverState *qemu_chr_open_win_path(const char *filename, - ChardevCommon *backend, - Error **errp) -{ - CharDriverState *chr; - WinCharState *s; - - chr = qemu_chr_alloc(backend, errp); - if (!chr) { - return NULL; - } - s = g_new0(WinCharState, 1); - chr->opaque = s; - chr->chr_write = win_chr_write; - chr->chr_free = win_chr_free; - - if (win_chr_init(chr, filename, errp) < 0) { - g_free(s); - qemu_chr_free_common(chr); - return NULL; - } - return chr; -} - static int win_chr_pipe_poll(void *opaque) { - CharDriverState *chr = opaque; - WinCharState *s = chr->opaque; + Chardev *chr = CHARDEV(opaque); + WinChardev *s = WIN_CHARDEV(opaque); DWORD size; PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL); @@ -2359,10 +2360,10 @@ static int win_chr_pipe_poll(void *opaque) return 0; } -static int win_chr_pipe_init(CharDriverState *chr, const char *filename, +static int win_chr_pipe_init(Chardev *chr, const char *filename, Error **errp) { - WinCharState *s = chr->opaque; + WinChardev *s = WIN_CHARDEV(chr); OVERLAPPED ov; int ret; DWORD size; @@ -2424,65 +2425,69 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename, } -static CharDriverState *qemu_chr_open_pipe(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, - bool *be_opened, - Error **errp) +static void qemu_chr_open_pipe(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { ChardevHostdev *opts = backend->u.pipe.data; const char *filename = opts->device; - CharDriverState *chr; - WinCharState *s; - ChardevCommon *common = qapi_ChardevHostdev_base(opts); - - chr = qemu_chr_alloc(common, errp); - if (!chr) { - return NULL; - } - s = g_new0(WinCharState, 1); - chr->opaque = s; - chr->chr_write = win_chr_write; - chr->chr_free = win_chr_free; if (win_chr_pipe_init(chr, filename, errp) < 0) { - g_free(s); - qemu_chr_free_common(chr); - return NULL; + return; } - return chr; } -static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out, - ChardevCommon *backend, - Error **errp) +static void qemu_chr_open_win_file(Chardev *chr, HANDLE fd_out) { - CharDriverState *chr; - WinCharState *s; + WinChardev *s = WIN_CHARDEV(chr); - chr = qemu_chr_alloc(backend, errp); - if (!chr) { - return NULL; - } - s = g_new0(WinCharState, 1); s->hcom = fd_out; - chr->opaque = s; - chr->chr_write = win_chr_write; - return chr; } -static CharDriverState *qemu_chr_open_win_con(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, - bool *be_opened, - Error **errp) +static void char_win_class_init(ObjectClass *oc, void *data) { - ChardevCommon *common = backend->u.console.data; - return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE), - common, errp); + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->chr_write = win_chr_write; + cc->chr_free = win_chr_free; } -static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len) +static const TypeInfo char_win_type_info = { + .name = TYPE_CHARDEV_WIN, + .parent = TYPE_CHARDEV, + .instance_size = sizeof(WinChardev), + .class_init = char_win_class_init, + .abstract = true, +}; + +static void qemu_chr_open_win_con(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) +{ + qemu_chr_open_win_file(chr, GetStdHandle(STD_OUTPUT_HANDLE)); +} + +static const CharDriver console_driver = { + .kind = CHARDEV_BACKEND_KIND_CONSOLE, +}; + +static void char_console_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->open = qemu_chr_open_win_con; + cc->chr_free = NULL; +} + +static const TypeInfo char_console_type_info = { + .name = TYPE_CHARDEV_CONSOLE, + .parent = TYPE_CHARDEV_WIN, + .class_init = char_console_class_init, +}; + +static int win_stdio_write(Chardev *chr, const uint8_t *buf, int len) { HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); DWORD dwSize; @@ -2503,8 +2508,8 @@ static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len) static void win_stdio_wait_func(void *opaque) { - CharDriverState *chr = opaque; - WinStdioCharState *stdio = chr->opaque; + Chardev *chr = CHARDEV(opaque); + WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque); INPUT_RECORD buf[4]; int ret; DWORD dwSize; @@ -2537,8 +2542,7 @@ static void win_stdio_wait_func(void *opaque) static DWORD WINAPI win_stdio_thread(LPVOID param) { - CharDriverState *chr = param; - WinStdioCharState *stdio = chr->opaque; + WinStdioChardev *stdio = WIN_STDIO_CHARDEV(param); int ret; DWORD dwSize; @@ -2576,8 +2580,8 @@ static DWORD WINAPI win_stdio_thread(LPVOID param) static void win_stdio_thread_wait_func(void *opaque) { - CharDriverState *chr = opaque; - WinStdioCharState *stdio = chr->opaque; + Chardev *chr = CHARDEV(opaque); + WinStdioChardev *stdio = WIN_STDIO_CHARDEV(opaque); if (qemu_chr_be_can_write(chr)) { qemu_chr_be_write(chr, &stdio->win_stdio_buf, 1); @@ -2586,9 +2590,9 @@ static void win_stdio_thread_wait_func(void *opaque) SetEvent(stdio->hInputDoneEvent); } -static void qemu_chr_set_echo_win_stdio(CharDriverState *chr, bool echo) +static void qemu_chr_set_echo_win_stdio(Chardev *chr, bool echo) { - WinStdioCharState *stdio = chr->opaque; + WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr); DWORD dwMode = 0; GetConsoleMode(stdio->hStdIn, &dwMode); @@ -2600,9 +2604,9 @@ static void qemu_chr_set_echo_win_stdio(CharDriverState *chr, bool echo) } } -static void win_stdio_free(CharDriverState *chr) +static void win_stdio_free(Chardev *chr) { - WinStdioCharState *stdio = chr->opaque; + WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr); if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) { CloseHandle(stdio->hInputReadyEvent); @@ -2613,40 +2617,32 @@ static void win_stdio_free(CharDriverState *chr) if (stdio->hInputThread != INVALID_HANDLE_VALUE) { TerminateThread(stdio->hInputThread, 0); } - - g_free(chr->opaque); } -static CharDriverState *qemu_chr_open_stdio(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, - bool *be_opened, - Error **errp) +static const TypeInfo char_win_stdio_type_info = { + .name = TYPE_CHARDEV_WIN_STDIO, + .parent = TYPE_CHARDEV, + .instance_size = sizeof(WinStdioChardev), + .abstract = true, +}; + +static void qemu_chr_open_stdio(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { - CharDriverState *chr; - WinStdioCharState *stdio; + WinStdioChardev *stdio = WIN_STDIO_CHARDEV(chr); DWORD dwMode; int is_console = 0; - ChardevCommon *common = qapi_ChardevStdio_base(backend->u.stdio.data); - - chr = qemu_chr_alloc(common, errp); - if (!chr) { - return NULL; - } - stdio = g_new0(WinStdioCharState, 1); stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE); if (stdio->hStdIn == INVALID_HANDLE_VALUE) { error_setg(errp, "cannot open stdio: invalid handle"); - return NULL; + return; } is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0; - chr->opaque = stdio; - chr->chr_write = win_stdio_write; - chr->chr_free = win_stdio_free; - if (is_console) { if (qemu_add_wait_object(stdio->hStdIn, win_stdio_wait_func, chr)) { @@ -2687,10 +2683,9 @@ static CharDriverState *qemu_chr_open_stdio(const char *id, SetConsoleMode(stdio->hStdIn, dwMode); - chr->chr_set_echo = qemu_chr_set_echo_win_stdio; qemu_chr_set_echo_win_stdio(chr, false); - return chr; + return; err3: qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL); @@ -2699,7 +2694,6 @@ err2: CloseHandle(stdio->hInputDoneEvent); err1: qemu_del_wait_object(stdio->hStdIn, NULL, NULL); - return NULL; } #endif /* !_WIN32 */ @@ -2707,17 +2701,20 @@ err1: /* UDP Net console */ typedef struct { + Chardev parent; QIOChannel *ioc; uint8_t buf[READ_BUF_LEN]; int bufcnt; int bufptr; int max_size; -} NetCharDriver; +} UdpChardev; + +#define UDP_CHARDEV(obj) OBJECT_CHECK(UdpChardev, (obj), TYPE_CHARDEV_UDP) /* Called with chr_write_lock held. */ -static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +static int udp_chr_write(Chardev *chr, const uint8_t *buf, int len) { - NetCharDriver *s = chr->opaque; + UdpChardev *s = UDP_CHARDEV(chr); return qio_channel_write( s->ioc, (const char *)buf, len, NULL); @@ -2725,8 +2722,8 @@ static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) static int udp_chr_read_poll(void *opaque) { - CharDriverState *chr = opaque; - NetCharDriver *s = chr->opaque; + Chardev *chr = CHARDEV(opaque); + UdpChardev *s = UDP_CHARDEV(opaque); s->max_size = qemu_chr_be_can_write(chr); @@ -2743,8 +2740,8 @@ static int udp_chr_read_poll(void *opaque) static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) { - CharDriverState *chr = opaque; - NetCharDriver *s = chr->opaque; + Chardev *chr = CHARDEV(opaque); + UdpChardev *s = UDP_CHARDEV(opaque); ssize_t ret; if (s->max_size == 0) { @@ -2768,10 +2765,10 @@ static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) return TRUE; } -static void udp_chr_update_read_handler(CharDriverState *chr, +static void udp_chr_update_read_handler(Chardev *chr, GMainContext *context) { - NetCharDriver *s = chr->opaque; + UdpChardev *s = UDP_CHARDEV(chr); remove_fd_in_watch(chr); if (s->ioc) { @@ -2782,48 +2779,22 @@ static void udp_chr_update_read_handler(CharDriverState *chr, } } -static void udp_chr_free(CharDriverState *chr) +static void udp_chr_free(Chardev *chr) { - NetCharDriver *s = chr->opaque; + UdpChardev *s = UDP_CHARDEV(chr); remove_fd_in_watch(chr); if (s->ioc) { object_unref(OBJECT(s->ioc)); } - g_free(s); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc, - ChardevCommon *backend, - bool *be_opened, - Error **errp) -{ - CharDriverState *chr = NULL; - NetCharDriver *s = NULL; - - chr = qemu_chr_alloc(backend, errp); - if (!chr) { - return NULL; - } - s = g_new0(NetCharDriver, 1); - - s->ioc = QIO_CHANNEL(sioc); - s->bufcnt = 0; - s->bufptr = 0; - chr->opaque = s; - chr->chr_write = udp_chr_write; - chr->chr_update_read_handler = udp_chr_update_read_handler; - chr->chr_free = udp_chr_free; - /* be isn't opened until we get a connection */ - *be_opened = false; - return chr; -} - /***********************************************************/ /* TCP Net console */ typedef struct { + Chardev parent; QIOChannel *ioc; /* Client I/O channel */ QIOChannelSocket *sioc; /* Client master channel */ QIOChannelSocket *listen_ioc; @@ -2846,14 +2817,18 @@ typedef struct { guint reconnect_timer; int64_t reconnect_time; bool connect_err_reported; -} TCPCharDriver; +} SocketChardev; + +#define SOCKET_CHARDEV(obj) \ + OBJECT_CHECK(SocketChardev, (obj), TYPE_CHARDEV_SOCKET) static gboolean socket_reconnect_timeout(gpointer opaque); -static void qemu_chr_socket_restart_timer(CharDriverState *chr) +static void qemu_chr_socket_restart_timer(Chardev *chr) { - TCPCharDriver *s = chr->opaque; + SocketChardev *s = SOCKET_CHARDEV(chr); char *name; + assert(s->connected == 0); s->reconnect_timer = g_timeout_add_seconds(s->reconnect_time, socket_reconnect_timeout, chr); @@ -2862,10 +2837,10 @@ static void qemu_chr_socket_restart_timer(CharDriverState *chr) g_free(name); } -static void check_report_connect_error(CharDriverState *chr, +static void check_report_connect_error(Chardev *chr, Error *err) { - TCPCharDriver *s = chr->opaque; + SocketChardev *s = SOCKET_CHARDEV(chr); if (!s->connect_err_reported) { error_report("Unable to connect character device %s: %s", @@ -2880,9 +2855,10 @@ static gboolean tcp_chr_accept(QIOChannel *chan, void *opaque); /* Called with chr_write_lock held. */ -static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len) { - TCPCharDriver *s = chr->opaque; + SocketChardev *s = SOCKET_CHARDEV(chr); + if (s->connected) { int ret = io_channel_send_full(s->ioc, buf, len, s->write_msgfds, @@ -2904,8 +2880,8 @@ static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) static int tcp_chr_read_poll(void *opaque) { - CharDriverState *chr = opaque; - TCPCharDriver *s = chr->opaque; + Chardev *chr = CHARDEV(opaque); + SocketChardev *s = SOCKET_CHARDEV(opaque); if (!s->connected) return 0; s->max_size = qemu_chr_be_can_write(chr); @@ -2914,8 +2890,8 @@ static int tcp_chr_read_poll(void *opaque) #define IAC 255 #define IAC_BREAK 243 -static void tcp_chr_process_IAC_bytes(CharDriverState *chr, - TCPCharDriver *s, +static void tcp_chr_process_IAC_bytes(Chardev *chr, + SocketChardev *s, uint8_t *buf, int *size) { /* Handle any telnet client's basic IAC options to satisfy char by @@ -2962,9 +2938,10 @@ static void tcp_chr_process_IAC_bytes(CharDriverState *chr, *size = j; } -static int tcp_get_msgfds(CharDriverState *chr, int *fds, int num) +static int tcp_get_msgfds(Chardev *chr, int *fds, int num) { - TCPCharDriver *s = chr->opaque; + SocketChardev *s = SOCKET_CHARDEV(chr); + int to_copy = (s->read_msgfds_num < num) ? s->read_msgfds_num : num; assert(num <= TCP_MAX_FDS); @@ -2987,9 +2964,9 @@ static int tcp_get_msgfds(CharDriverState *chr, int *fds, int num) return to_copy; } -static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num) +static int tcp_set_msgfds(Chardev *chr, int *fds, int num) { - TCPCharDriver *s = chr->opaque; + SocketChardev *s = SOCKET_CHARDEV(chr); /* clear old pending fd array */ g_free(s->write_msgfds); @@ -3012,9 +2989,9 @@ static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num) return 0; } -static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) +static ssize_t tcp_chr_recv(Chardev *chr, char *buf, size_t len) { - TCPCharDriver *s = chr->opaque; + SocketChardev *s = SOCKET_CHARDEV(chr); struct iovec iov = { .iov_base = buf, .iov_len = len }; int ret; size_t i; @@ -3069,15 +3046,15 @@ static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) return ret; } -static GSource *tcp_chr_add_watch(CharDriverState *chr, GIOCondition cond) +static GSource *tcp_chr_add_watch(Chardev *chr, GIOCondition cond) { - TCPCharDriver *s = chr->opaque; + SocketChardev *s = SOCKET_CHARDEV(chr); return qio_channel_create_watch(s->ioc, cond); } -static void tcp_chr_free_connection(CharDriverState *chr) +static void tcp_chr_free_connection(Chardev *chr) { - TCPCharDriver *s = chr->opaque; + SocketChardev *s = SOCKET_CHARDEV(chr); int i; if (!s->connected) { @@ -3104,9 +3081,9 @@ static void tcp_chr_free_connection(CharDriverState *chr) s->connected = 0; } -static void tcp_chr_disconnect(CharDriverState *chr) +static void tcp_chr_disconnect(Chardev *chr) { - TCPCharDriver *s = chr->opaque; + SocketChardev *s = SOCKET_CHARDEV(chr); if (!s->connected) { return; @@ -3128,8 +3105,8 @@ static void tcp_chr_disconnect(CharDriverState *chr) static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) { - CharDriverState *chr = opaque; - TCPCharDriver *s = chr->opaque; + Chardev *chr = CHARDEV(opaque); + SocketChardev *s = SOCKET_CHARDEV(opaque); uint8_t buf[READ_BUF_LEN]; int len, size; @@ -3153,9 +3130,9 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) return TRUE; } -static int tcp_chr_sync_read(CharDriverState *chr, const uint8_t *buf, int len) +static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len) { - TCPCharDriver *s = chr->opaque; + SocketChardev *s = SOCKET_CHARDEV(chr); int size; if (!s->connected) { @@ -3173,8 +3150,8 @@ static int tcp_chr_sync_read(CharDriverState *chr, const uint8_t *buf, int len) static void tcp_chr_connect(void *opaque) { - CharDriverState *chr = opaque; - TCPCharDriver *s = chr->opaque; + Chardev *chr = CHARDEV(opaque); + SocketChardev *s = SOCKET_CHARDEV(opaque); g_free(chr->filename); chr->filename = sockaddr_to_str( @@ -3192,10 +3169,10 @@ static void tcp_chr_connect(void *opaque) qemu_chr_be_generic_open(chr); } -static void tcp_chr_update_read_handler(CharDriverState *chr, +static void tcp_chr_update_read_handler(Chardev *chr, GMainContext *context) { - TCPCharDriver *s = chr->opaque; + SocketChardev *s = SOCKET_CHARDEV(chr); if (!s->connected) { return; @@ -3211,7 +3188,7 @@ static void tcp_chr_update_read_handler(CharDriverState *chr, } typedef struct { - CharDriverState *chr; + Chardev *chr; char buf[12]; size_t buflen; } TCPCharDriverTelnetInit; @@ -3244,9 +3221,9 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc, return TRUE; } -static void tcp_chr_telnet_init(CharDriverState *chr) +static void tcp_chr_telnet_init(Chardev *chr) { - TCPCharDriver *s = chr->opaque; + SocketChardev *s = SOCKET_CHARDEV(chr); TCPCharDriverTelnetInit *init = g_new0(TCPCharDriverTelnetInit, 1); size_t n = 0; @@ -3280,8 +3257,8 @@ static void tcp_chr_telnet_init(CharDriverState *chr) static void tcp_chr_tls_handshake(QIOTask *task, gpointer user_data) { - CharDriverState *chr = user_data; - TCPCharDriver *s = chr->opaque; + Chardev *chr = user_data; + SocketChardev *s = user_data; if (qio_task_propagate_error(task, NULL)) { tcp_chr_disconnect(chr); @@ -3295,9 +3272,9 @@ static void tcp_chr_tls_handshake(QIOTask *task, } -static void tcp_chr_tls_init(CharDriverState *chr) +static void tcp_chr_tls_init(Chardev *chr) { - TCPCharDriver *s = chr->opaque; + SocketChardev *s = SOCKET_CHARDEV(chr); QIOChannelTLS *tioc; Error *err = NULL; gchar *name; @@ -3333,10 +3310,10 @@ static void tcp_chr_tls_init(CharDriverState *chr) } -static void tcp_chr_set_client_ioc_name(CharDriverState *chr, +static void tcp_chr_set_client_ioc_name(Chardev *chr, QIOChannelSocket *sioc) { - TCPCharDriver *s = chr->opaque; + SocketChardev *s = SOCKET_CHARDEV(chr); char *name; name = g_strdup_printf("chardev-tcp-%s-%s", s->is_listen ? "server" : "client", @@ -3346,9 +3323,10 @@ static void tcp_chr_set_client_ioc_name(CharDriverState *chr, } -static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc) +static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc) { - TCPCharDriver *s = chr->opaque; + SocketChardev *s = SOCKET_CHARDEV(chr); + if (s->ioc != NULL) { return -1; } @@ -3382,7 +3360,7 @@ static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc) } -static int tcp_chr_add_client(CharDriverState *chr, int fd) +static int tcp_chr_add_client(Chardev *chr, int fd) { int ret; QIOChannelSocket *sioc; @@ -3401,7 +3379,7 @@ static gboolean tcp_chr_accept(QIOChannel *channel, GIOCondition cond, void *opaque) { - CharDriverState *chr = opaque; + Chardev *chr = CHARDEV(opaque); QIOChannelSocket *sioc; sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(channel), @@ -3417,17 +3395,17 @@ static gboolean tcp_chr_accept(QIOChannel *channel, return TRUE; } -static int tcp_chr_wait_connected(CharDriverState *chr, Error **errp) +static int tcp_chr_wait_connected(Chardev *chr, Error **errp) { - TCPCharDriver *s = chr->opaque; + SocketChardev *s = SOCKET_CHARDEV(chr); QIOChannelSocket *sioc; /* It can't wait on s->connected, since it is set asynchronously * in TLS and telnet cases, only wait for an accepted socket */ while (!s->ioc) { if (s->is_listen) { - fprintf(stderr, "QEMU waiting for connection on: %s\n", - chr->filename); + error_report("QEMU waiting for connection on: %s", + chr->filename); qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), true, NULL); tcp_chr_accept(QIO_CHANNEL(s->listen_ioc), G_IO_IN, chr); qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), false, NULL); @@ -3446,10 +3424,12 @@ static int tcp_chr_wait_connected(CharDriverState *chr, Error **errp) return 0; } -static int qemu_chr_wait_connected(CharDriverState *chr, Error **errp) +static int qemu_chr_wait_connected(Chardev *chr, Error **errp) { - if (chr->chr_wait_connected) { - return chr->chr_wait_connected(chr, errp); + ChardevClass *cc = CHARDEV_GET_CLASS(chr); + + if (cc->chr_wait_connected) { + return cc->chr_wait_connected(chr, errp); } return 0; @@ -3465,9 +3445,9 @@ int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp) return qemu_chr_wait_connected(be->chr, errp); } -static void tcp_chr_free(CharDriverState *chr) +static void tcp_chr_free(Chardev *chr) { - TCPCharDriver *s = chr->opaque; + SocketChardev *s = SOCKET_CHARDEV(chr); tcp_chr_free_connection(chr); @@ -3486,7 +3466,7 @@ static void tcp_chr_free(CharDriverState *chr) if (s->tls_creds) { object_unref(OBJECT(s->tls_creds)); } - g_free(s); + qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } @@ -3494,8 +3474,8 @@ static void tcp_chr_free(CharDriverState *chr) static void qemu_chr_socket_connected(QIOTask *task, void *opaque) { QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task)); - CharDriverState *chr = opaque; - TCPCharDriver *s = chr->opaque; + Chardev *chr = CHARDEV(opaque); + SocketChardev *s = SOCKET_CHARDEV(chr); Error *err = NULL; if (qio_task_propagate_error(task, &err)) { @@ -3516,23 +3496,27 @@ static void qemu_chr_socket_connected(QIOTask *task, void *opaque) /* Ring buffer chardev */ typedef struct { + Chardev parent; size_t size; size_t prod; size_t cons; uint8_t *cbuf; -} RingBufCharDriver; +} RingBufChardev; + +#define RINGBUF_CHARDEV(obj) \ + OBJECT_CHECK(RingBufChardev, (obj), TYPE_CHARDEV_RINGBUF) -static size_t ringbuf_count(const CharDriverState *chr) +static size_t ringbuf_count(const Chardev *chr) { - const RingBufCharDriver *d = chr->opaque; + const RingBufChardev *d = RINGBUF_CHARDEV(chr); return d->prod - d->cons; } /* Called with chr_write_lock held. */ -static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +static int ringbuf_chr_write(Chardev *chr, const uint8_t *buf, int len) { - RingBufCharDriver *d = chr->opaque; + RingBufChardev *d = RINGBUF_CHARDEV(chr); int i; if (!buf || (len < 0)) { @@ -3549,9 +3533,9 @@ static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len) return len; } -static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len) +static int ringbuf_chr_read(Chardev *chr, uint8_t *buf, int len) { - RingBufCharDriver *d = chr->opaque; + RingBufChardev *d = RINGBUF_CHARDEV(chr); int i; qemu_mutex_lock(&chr->chr_write_lock); @@ -3563,66 +3547,39 @@ static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len) return i; } -static void ringbuf_chr_free(struct CharDriverState *chr) +static void ringbuf_chr_free(struct Chardev *chr) { - RingBufCharDriver *d = chr->opaque; + RingBufChardev *d = RINGBUF_CHARDEV(chr); g_free(d->cbuf); - g_free(d); - chr->opaque = NULL; } -static CharDriverState *qemu_chr_open_ringbuf(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, - bool *be_opened, - Error **errp) +static void qemu_chr_open_ringbuf(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { ChardevRingbuf *opts = backend->u.ringbuf.data; - ChardevCommon *common = qapi_ChardevRingbuf_base(opts); - CharDriverState *chr; - RingBufCharDriver *d; - - chr = qemu_chr_alloc(common, errp); - if (!chr) { - return NULL; - } - d = g_malloc(sizeof(*d)); + RingBufChardev *d = RINGBUF_CHARDEV(chr); d->size = opts->has_size ? opts->size : 65536; /* The size must be power of 2 */ if (d->size & (d->size - 1)) { error_setg(errp, "size of ringbuf chardev must be power of two"); - goto fail; + return; } d->prod = 0; d->cons = 0; d->cbuf = g_malloc0(d->size); - - chr->opaque = d; - chr->chr_write = ringbuf_chr_write; - chr->chr_free = ringbuf_chr_free; - - return chr; - -fail: - g_free(d); - qemu_chr_free_common(chr); - return NULL; -} - -bool chr_is_ringbuf(const CharDriverState *chr) -{ - return chr->chr_write == ringbuf_chr_write; } void qmp_ringbuf_write(const char *device, const char *data, bool has_format, enum DataFormat format, Error **errp) { - CharDriverState *chr; + Chardev *chr; const uint8_t *write_data; int ret; gsize write_count; @@ -3633,7 +3590,7 @@ void qmp_ringbuf_write(const char *device, const char *data, return; } - if (!chr_is_ringbuf(chr)) { + if (!CHARDEV_IS_RINGBUF(chr)) { error_setg(errp,"%s is not a ringbuf device", device); return; } @@ -3666,7 +3623,7 @@ char *qmp_ringbuf_read(const char *device, int64_t size, bool has_format, enum DataFormat format, Error **errp) { - CharDriverState *chr; + Chardev *chr; uint8_t *read_data; size_t count; char *data; @@ -3677,7 +3634,7 @@ char *qmp_ringbuf_read(const char *device, int64_t size, return NULL; } - if (!chr_is_ringbuf(chr)) { + if (!CHARDEV_IS_RINGBUF(chr)) { error_setg(errp,"%s is not a ringbuf device", device); return NULL; } @@ -3894,6 +3851,36 @@ static void qemu_chr_parse_stdio(QemuOpts *opts, ChardevBackend *backend, stdio->signal = qemu_opt_get_bool(opts, "signal", true); } +static const CharDriver stdio_driver = { + .kind = CHARDEV_BACKEND_KIND_STDIO, + .parse = qemu_chr_parse_stdio, +}; + +static void char_stdio_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->open = qemu_chr_open_stdio; +#ifdef _WIN32 + cc->chr_write = win_stdio_write; + cc->chr_set_echo = qemu_chr_set_echo_win_stdio; + cc->chr_free = win_stdio_free; +#else + cc->chr_set_echo = qemu_chr_set_echo_stdio; + cc->chr_free = qemu_chr_free_stdio; +#endif +} + +static const TypeInfo char_stdio_type_info = { + .name = TYPE_CHARDEV_STDIO, +#ifdef _WIN32 + .parent = TYPE_CHARDEV_WIN_STDIO, +#else + .parent = TYPE_CHARDEV_FD, +#endif + .class_init = char_stdio_class_init, +}; + #ifdef HAVE_CHARDEV_SERIAL static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend, Error **errp) @@ -3943,6 +3930,28 @@ static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend, dev->device = g_strdup(device); } +static const CharDriver pipe_driver = { + .kind = CHARDEV_BACKEND_KIND_PIPE, + .parse = qemu_chr_parse_pipe, +}; + +static void char_pipe_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->open = qemu_chr_open_pipe; +} + +static const TypeInfo char_pipe_type_info = { + .name = TYPE_CHARDEV_PIPE, +#ifdef _WIN32 + .parent = TYPE_CHARDEV_WIN, +#else + .parent = TYPE_CHARDEV_FD, +#endif + .class_init = char_pipe_class_init, +}; + static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend, Error **errp) { @@ -3959,6 +3968,38 @@ static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend, } } +static const CharDriver ringbuf_driver = { + .kind = CHARDEV_BACKEND_KIND_RINGBUF, + .parse = qemu_chr_parse_ringbuf, +}; + +static void char_ringbuf_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->open = qemu_chr_open_ringbuf; + cc->chr_write = ringbuf_chr_write; + cc->chr_free = ringbuf_chr_free; +} + +static const TypeInfo char_ringbuf_type_info = { + .name = TYPE_CHARDEV_RINGBUF, + .parent = TYPE_CHARDEV, + .class_init = char_ringbuf_class_init, + .instance_size = sizeof(RingBufChardev), +}; + +/* Bug-compatibility: */ +static const CharDriver memory_driver = { + .kind = CHARDEV_BACKEND_KIND_MEMORY, + .parse = qemu_chr_parse_ringbuf, +}; + +static const TypeInfo char_memory_type_info = { + .name = TYPE_CHARDEV_MEMORY, + .parent = TYPE_CHARDEV_RINGBUF, +}; + static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend, Error **errp) { @@ -3974,6 +4015,29 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend, mux->chardev = g_strdup(chardev); } +static const CharDriver mux_driver = { + .kind = CHARDEV_BACKEND_KIND_MUX, + .parse = qemu_chr_parse_mux, +}; + +static void char_mux_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->open = qemu_chr_open_mux; + cc->chr_free = mux_chr_free; + cc->chr_write = mux_chr_write; + cc->chr_accept_input = mux_chr_accept_input; + cc->chr_add_watch = mux_chr_add_watch; +} + +static const TypeInfo char_mux_type_info = { + .name = TYPE_CHARDEV_MUX, + .parent = TYPE_CHARDEV, + .class_init = char_mux_class_init, + .instance_size = sizeof(MuxChardev), +}; + static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, Error **errp) { @@ -4101,54 +4165,47 @@ static void qemu_chr_parse_udp(QemuOpts *opts, ChardevBackend *backend, } } -typedef struct CharDriver { - const char *name; - ChardevBackendKind kind; - CharDriverParse *parse; - CharDriverCreate *create; -} CharDriver; - -static GSList *backends; +static const CharDriver *backends[CHARDEV_BACKEND_KIND__MAX]; -void register_char_driver(const char *name, ChardevBackendKind kind, - CharDriverParse *parse, CharDriverCreate *create) +void register_char_driver(const CharDriver *driver) { - CharDriver *s; - - s = g_malloc0(sizeof(*s)); - s->name = g_strdup(name); - s->kind = kind; - s->parse = parse; - s->create = create; - - backends = g_slist_append(backends, s); + backends[driver->kind] = driver; } -CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, - Error **errp) +Chardev *qemu_chr_new_from_opts(QemuOpts *opts, + Error **errp) { Error *local_err = NULL; - CharDriver *cd; - CharDriverState *chr; - GSList *i; + const CharDriver *cd = NULL; + Chardev *chr; + int i; ChardevReturn *ret = NULL; ChardevBackend *backend; + const char *name = qemu_opt_get(opts, "backend"); const char *id = qemu_opts_id(opts); char *bid = NULL; - if (qemu_opt_get(opts, "backend") == NULL) { + if (name == NULL) { error_setg(errp, "chardev: \"%s\" missing backend", qemu_opts_id(opts)); goto err; } - if (is_help_option(qemu_opt_get(opts, "backend"))) { - fprintf(stderr, "Available chardev backend types:\n"); - for (i = backends; i; i = i->next) { - cd = i->data; - fprintf(stderr, "%s\n", cd->name); + if (is_help_option(name)) { + GString *str = g_string_new(""); + for (i = 0; i < ARRAY_SIZE(backends); i++) { + cd = backends[i]; + if (cd) { + g_string_append_printf(str, "\n%s", ChardevBackendKind_lookup[cd->kind]); + if (cd->alias) { + g_string_append_printf(str, "\n%s", cd->alias); + } + } } - exit(!is_help_option(qemu_opt_get(opts, "backend"))); + + error_report("Available chardev backend types: %s", str->str); + g_string_free(str, true); + exit(0); } if (id == NULL) { @@ -4156,16 +4213,18 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, goto err; } - for (i = backends; i; i = i->next) { - cd = i->data; - - if (strcmp(cd->name, qemu_opt_get(opts, "backend")) == 0) { + for (i = 0; i < ARRAY_SIZE(backends); i++) { + cd = backends[i]; + if (!cd) { + continue; + } + if (g_strcmp0(ChardevBackendKind_lookup[cd->kind], name) == 0 || + g_strcmp0(cd->alias, name) == 0) { break; } } - if (i == NULL) { - error_setg(errp, "chardev: backend \"%s\" not found", - qemu_opt_get(opts, "backend")); + if (i == ARRAY_SIZE(backends)) { + error_setg(errp, "chardev: backend \"%s\" not found", name); goto err; } @@ -4222,10 +4281,10 @@ err: return NULL; } -CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename) +Chardev *qemu_chr_new_noreplay(const char *label, const char *filename) { const char *p; - CharDriverState *chr; + Chardev *chr; QemuOpts *opts; Error *err = NULL; @@ -4248,15 +4307,17 @@ CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename) return chr; } -CharDriverState *qemu_chr_new(const char *label, const char *filename) +Chardev *qemu_chr_new(const char *label, const char *filename) { - CharDriverState *chr; + Chardev *chr; chr = qemu_chr_new_noreplay(label, filename); if (chr) { - chr->replay = replay_mode != REPLAY_MODE_NONE; - if (chr->replay && chr->chr_ioctl) { - fprintf(stderr, - "Replay: ioctl is not supported for serial devices yet\n"); + if (replay_mode != REPLAY_MODE_NONE) { + qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY); + } + if (qemu_chr_replay(chr) && CHARDEV_GET_CLASS(chr)->chr_ioctl) { + error_report("Replay: ioctl is not supported " + "for serial devices yet"); } replay_register_char_driver(chr); } @@ -4265,16 +4326,16 @@ CharDriverState *qemu_chr_new(const char *label, const char *filename) void qemu_chr_fe_set_echo(CharBackend *be, bool echo) { - CharDriverState *chr = be->chr; + Chardev *chr = be->chr; - if (chr && chr->chr_set_echo) { - chr->chr_set_echo(chr, echo); + if (chr && CHARDEV_GET_CLASS(chr)->chr_set_echo) { + CHARDEV_GET_CLASS(chr)->chr_set_echo(chr, echo); } } void qemu_chr_fe_set_open(CharBackend *be, int fe_open) { - CharDriverState *chr = be->chr; + Chardev *chr = be->chr; if (!chr) { return; @@ -4284,23 +4345,23 @@ void qemu_chr_fe_set_open(CharBackend *be, int fe_open) return; } be->fe_open = fe_open; - if (chr->chr_set_fe_open) { - chr->chr_set_fe_open(chr, fe_open); + if (CHARDEV_GET_CLASS(chr)->chr_set_fe_open) { + CHARDEV_GET_CLASS(chr)->chr_set_fe_open(chr, fe_open); } } guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond, GIOFunc func, void *user_data) { - CharDriverState *s = be->chr; + Chardev *s = be->chr; GSource *src; guint tag; - if (!s || s->chr_add_watch == NULL) { + if (!s || CHARDEV_GET_CLASS(s)->chr_add_watch == NULL) { return 0; } - src = s->chr_add_watch(s, cond); + src = CHARDEV_GET_CLASS(s)->chr_add_watch(s, cond); if (!src) { return 0; } @@ -4314,36 +4375,22 @@ guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond, void qemu_chr_fe_disconnect(CharBackend *be) { - CharDriverState *chr = be->chr; + Chardev *chr = be->chr; - if (chr && chr->chr_disconnect) { - chr->chr_disconnect(chr); + if (chr && CHARDEV_GET_CLASS(chr)->chr_disconnect) { + CHARDEV_GET_CLASS(chr)->chr_disconnect(chr); } } -static void qemu_chr_free_common(CharDriverState *chr) +void qemu_chr_free(Chardev *chr) { - if (chr->be) { - chr->be->chr = NULL; + if (CHARDEV_GET_CLASS(chr)->chr_free) { + CHARDEV_GET_CLASS(chr)->chr_free(chr); } - g_free(chr->filename); - g_free(chr->label); - if (chr->logfd != -1) { - close(chr->logfd); - } - qemu_mutex_destroy(&chr->chr_write_lock); - g_free(chr); -} - -void qemu_chr_free(CharDriverState *chr) -{ - if (chr->chr_free) { - chr->chr_free(chr); - } - qemu_chr_free_common(chr); + object_unref(OBJECT(chr)); } -void qemu_chr_delete(CharDriverState *chr) +void qemu_chr_delete(Chardev *chr) { QTAILQ_REMOVE(&chardevs, chr, next); qemu_chr_free(chr); @@ -4352,7 +4399,7 @@ void qemu_chr_delete(CharDriverState *chr) ChardevInfoList *qmp_query_chardev(Error **errp) { ChardevInfoList *chr_list = NULL; - CharDriverState *chr; + Chardev *chr; QTAILQ_FOREACH(chr, &chardevs, next) { ChardevInfoList *info = g_malloc0(sizeof(*info)); @@ -4368,28 +4415,41 @@ ChardevInfoList *qmp_query_chardev(Error **errp) return chr_list; } +static ChardevBackendInfoList * +qmp_prepend_backend(ChardevBackendInfoList *list, const char *name) +{ + ChardevBackendInfoList *info = g_malloc0(sizeof(*info)); + info->value = g_malloc0(sizeof(*info->value)); + info->value->name = g_strdup(name); + info->next = list; + return info; +} + ChardevBackendInfoList *qmp_query_chardev_backends(Error **errp) { ChardevBackendInfoList *backend_list = NULL; - CharDriver *c = NULL; - GSList *i = NULL; + const CharDriver *c; + int i; - for (i = backends; i; i = i->next) { - ChardevBackendInfoList *info = g_malloc0(sizeof(*info)); - c = i->data; - info->value = g_malloc0(sizeof(*info->value)); - info->value->name = g_strdup(c->name); + for (i = 0; i < ARRAY_SIZE(backends); i++) { + c = backends[i]; + if (!c) { + continue; + } - info->next = backend_list; - backend_list = info; + backend_list = qmp_prepend_backend(backend_list, + ChardevBackendKind_lookup[c->kind]); + if (c->alias) { + backend_list = qmp_prepend_backend(backend_list, c->alias); + } } return backend_list; } -CharDriverState *qemu_chr_find(const char *name) +Chardev *qemu_chr_find(const char *name) { - CharDriverState *chr; + Chardev *chr; QTAILQ_FOREACH(chr, &chardevs, next) { if (strcmp(chr->label, name) != 0) @@ -4495,21 +4555,19 @@ QemuOptsList qemu_chardev_opts = { #ifdef _WIN32 -static CharDriverState *qmp_chardev_open_file(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, - bool *be_opened, - Error **errp) +static void qmp_chardev_open_file(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { ChardevFile *file = backend->u.file.data; - ChardevCommon *common = qapi_ChardevFile_base(file); HANDLE out; DWORD accessmode; DWORD flags; if (file->has_in) { error_setg(errp, "input file not supported"); - return NULL; + return; } if (file->has_append && file->append) { @@ -4526,20 +4584,20 @@ static CharDriverState *qmp_chardev_open_file(const char *id, FILE_ATTRIBUTE_NORMAL, NULL); if (out == INVALID_HANDLE_VALUE) { error_setg(errp, "open %s failed", file->out); - return NULL; + return; } - return qemu_chr_open_win_file(out, common, errp); + + qemu_chr_open_win_file(chr, out); } -static CharDriverState *qmp_chardev_open_serial(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, - bool *be_opened, - Error **errp) +static void qmp_chardev_open_serial(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { ChardevHostdev *serial = backend->u.serial.data; - ChardevCommon *common = qapi_ChardevHostdev_base(serial); - return qemu_chr_open_win_path(serial->device, common, errp); + + win_chr_init(chr, serial->device, errp); } #else /* WIN32 */ @@ -4556,14 +4614,12 @@ static int qmp_chardev_open_file_source(char *src, int flags, return fd; } -static CharDriverState *qmp_chardev_open_file(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, - bool *be_opened, - Error **errp) +static void qmp_chardev_open_file(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { ChardevFile *file = backend->u.file.data; - ChardevCommon *common = qapi_ChardevFile_base(file); int flags, in = -1, out; flags = O_WRONLY | O_CREAT | O_BINARY; @@ -4575,7 +4631,7 @@ static CharDriverState *qmp_chardev_open_file(const char *id, out = qmp_chardev_open_file_source(file->out, flags, errp); if (out < 0) { - return NULL; + return; } if (file->has_in) { @@ -4583,58 +4639,141 @@ static CharDriverState *qmp_chardev_open_file(const char *id, in = qmp_chardev_open_file_source(file->in, flags, errp); if (in < 0) { qemu_close(out); - return NULL; + return; } } - return qemu_chr_open_fd(in, out, common, errp); + qemu_chr_open_fd(chr, in, out); } #ifdef HAVE_CHARDEV_SERIAL -static CharDriverState *qmp_chardev_open_serial(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, - bool *be_opened, - Error **errp) +static void qmp_chardev_open_serial(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { ChardevHostdev *serial = backend->u.serial.data; - ChardevCommon *common = qapi_ChardevHostdev_base(serial); int fd; fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp); if (fd < 0) { - return NULL; + return; } qemu_set_nonblock(fd); - return qemu_chr_open_tty_fd(fd, common, be_opened, errp); + tty_serial_init(fd, 115200, 'N', 8, 1); + + qemu_chr_open_fd(chr, fd, fd); } #endif #ifdef HAVE_CHARDEV_PARPORT -static CharDriverState *qmp_chardev_open_parallel(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, - bool *be_opened, - Error **errp) +static void qmp_chardev_open_parallel(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { ChardevHostdev *parallel = backend->u.parallel.data; - ChardevCommon *common = qapi_ChardevHostdev_base(parallel); int fd; fd = qmp_chardev_open_file_source(parallel->device, O_RDWR, errp); if (fd < 0) { - return NULL; + return; } - return qemu_chr_open_pp_fd(fd, common, be_opened, errp); + qemu_chr_open_pp_fd(chr, fd, be_opened, errp); } + +static const CharDriver parallel_driver = { + .kind = CHARDEV_BACKEND_KIND_PARALLEL, + .alias = "parport", + .parse = qemu_chr_parse_parallel, +}; + +static void char_parallel_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->open = qmp_chardev_open_parallel; +#if defined(__linux__) + cc->chr_write = null_chr_write; + cc->chr_ioctl = pp_ioctl; + cc->chr_free = pp_free; +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) + /* FIXME: no chr_free */ + cc->chr_write = null_chr_write; + cc->chr_ioctl = pp_ioctl; +#endif +} + +static const TypeInfo char_parallel_type_info = { + .name = TYPE_CHARDEV_PARALLEL, + .parent = TYPE_CHARDEV, + .instance_size = sizeof(ParallelChardev), + .class_init = char_parallel_class_init, +}; #endif #endif /* WIN32 */ +static const CharDriver file_driver = { + .kind = CHARDEV_BACKEND_KIND_FILE, + .parse = qemu_chr_parse_file_out, +}; + +static void char_file_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->open = qmp_chardev_open_file; +#ifdef _WIN32 + /* FIXME: no chr_free */ + cc->chr_free = NULL; +#endif +} + +static const TypeInfo char_file_type_info = { + .name = TYPE_CHARDEV_FILE, +#ifdef _WIN32 + .parent = TYPE_CHARDEV_WIN, +#else + .parent = TYPE_CHARDEV_FD, +#endif + .class_init = char_file_class_init, +}; + +#ifdef HAVE_CHARDEV_SERIAL + +static const CharDriver serial_driver = { + .kind = CHARDEV_BACKEND_KIND_SERIAL, + .alias = "tty", + .parse = qemu_chr_parse_serial, +}; + +static void char_serial_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->open = qmp_chardev_open_serial; +#ifndef _WIN32 + cc->chr_ioctl = tty_serial_ioctl; + cc->chr_free = qemu_chr_free_tty; +#endif +} + +static const TypeInfo char_serial_type_info = { + .name = TYPE_CHARDEV_SERIAL, +#ifdef _WIN32 + .parent = TYPE_CHARDEV_WIN, +#else + .parent = TYPE_CHARDEV_FD, +#endif + .class_init = char_serial_class_init, +}; +#endif + static gboolean socket_reconnect_timeout(gpointer opaque) { - CharDriverState *chr = opaque; - TCPCharDriver *s = chr->opaque; + Chardev *chr = CHARDEV(opaque); + SocketChardev *s = SOCKET_CHARDEV(opaque); QIOChannelSocket *sioc; s->reconnect_timer = 0; @@ -4652,14 +4791,12 @@ static gboolean socket_reconnect_timeout(gpointer opaque) return false; } -static CharDriverState *qmp_chardev_open_socket(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, - bool *be_opened, - Error **errp) +static void qmp_chardev_open_socket(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { - CharDriverState *chr; - TCPCharDriver *s; + SocketChardev *s = SOCKET_CHARDEV(chr); ChardevSocket *sock = backend->u.socket.data; SocketAddress *addr = sock->addr; bool do_nodelay = sock->has_nodelay ? sock->nodelay : false; @@ -4667,15 +4804,8 @@ static CharDriverState *qmp_chardev_open_socket(const char *id, bool is_telnet = sock->has_telnet ? sock->telnet : false; bool is_waitconnect = sock->has_wait ? sock->wait : false; int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0; - ChardevCommon *common = qapi_ChardevSocket_base(sock); QIOChannelSocket *sioc = NULL; - chr = qemu_chr_alloc(common, errp); - if (!chr) { - return NULL; - } - s = g_new0(TCPCharDriver, 1); - s->is_unix = addr->type == SOCKET_ADDRESS_KIND_UNIX; s->is_listen = is_listen; s->is_telnet = is_telnet; @@ -4720,17 +4850,6 @@ static CharDriverState *qmp_chardev_open_socket(const char *id, qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_FD_PASS); } - chr->opaque = s; - chr->chr_wait_connected = tcp_chr_wait_connected; - chr->chr_write = tcp_chr_write; - chr->chr_sync_read = tcp_chr_sync_read; - chr->chr_free = tcp_chr_free; - chr->chr_disconnect = tcp_chr_disconnect; - chr->get_msgfds = tcp_get_msgfds; - chr->set_msgfds = tcp_set_msgfds; - chr->chr_add_client = tcp_chr_add_client; - chr->chr_add_watch = tcp_chr_add_watch; - chr->chr_update_read_handler = tcp_chr_update_read_handler; /* be isn't opened until we get a connection */ *be_opened = false; @@ -4778,113 +4897,201 @@ static CharDriverState *qmp_chardev_open_socket(const char *id, } } - return chr; + return; - error: +error: if (sioc) { object_unref(OBJECT(sioc)); } if (s->tls_creds) { object_unref(OBJECT(s->tls_creds)); } - g_free(s); - qemu_chr_free_common(chr); - return NULL; } -static CharDriverState *qmp_chardev_open_udp(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, - bool *be_opened, - Error **errp) +static const CharDriver socket_driver = { + .kind = CHARDEV_BACKEND_KIND_SOCKET, + .parse = qemu_chr_parse_socket, +}; + +static void char_socket_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->open = qmp_chardev_open_socket; + cc->chr_wait_connected = tcp_chr_wait_connected; + cc->chr_write = tcp_chr_write; + cc->chr_sync_read = tcp_chr_sync_read; + cc->chr_disconnect = tcp_chr_disconnect; + cc->get_msgfds = tcp_get_msgfds; + cc->set_msgfds = tcp_set_msgfds; + cc->chr_add_client = tcp_chr_add_client; + cc->chr_add_watch = tcp_chr_add_watch; + cc->chr_update_read_handler = tcp_chr_update_read_handler; + cc->chr_free = tcp_chr_free; +} + +static const TypeInfo char_socket_type_info = { + .name = TYPE_CHARDEV_SOCKET, + .parent = TYPE_CHARDEV, + .instance_size = sizeof(SocketChardev), + .class_init = char_socket_class_init, +}; + +static void qmp_chardev_open_udp(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { ChardevUdp *udp = backend->u.udp.data; - ChardevCommon *common = qapi_ChardevUdp_base(udp); QIOChannelSocket *sioc = qio_channel_socket_new(); char *name; - CharDriverState *chr; + UdpChardev *s = UDP_CHARDEV(chr); if (qio_channel_socket_dgram_sync(sioc, udp->local, udp->remote, errp) < 0) { object_unref(OBJECT(sioc)); - return NULL; + return; } - chr = qemu_chr_open_udp(sioc, common, be_opened, errp); name = g_strdup_printf("chardev-udp-%s", chr->label); qio_channel_set_name(QIO_CHANNEL(sioc), name); g_free(name); - return chr; + s->ioc = QIO_CHANNEL(sioc); + /* be isn't opened until we get a connection */ + *be_opened = false; } +static const CharDriver udp_driver = { + .kind = CHARDEV_BACKEND_KIND_UDP, + .parse = qemu_chr_parse_udp, +}; + +static void char_udp_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); -bool qemu_chr_has_feature(CharDriverState *chr, + cc->open = qmp_chardev_open_udp; + cc->chr_write = udp_chr_write; + cc->chr_update_read_handler = udp_chr_update_read_handler; + cc->chr_free = udp_chr_free; +} + +static const TypeInfo char_udp_type_info = { + .name = TYPE_CHARDEV_UDP, + .parent = TYPE_CHARDEV, + .instance_size = sizeof(UdpChardev), + .class_init = char_udp_class_init, +}; + +bool qemu_chr_has_feature(Chardev *chr, CharDriverFeature feature) { return test_bit(feature, chr->features); } -void qemu_chr_set_feature(CharDriverState *chr, +void qemu_chr_set_feature(Chardev *chr, CharDriverFeature feature) { return set_bit(feature, chr->features); } -ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, - Error **errp) +static const ChardevClass *char_get_class(const char *driver, Error **errp) { - ChardevReturn *ret = g_new0(ChardevReturn, 1); - CharDriverState *chr = NULL; - Error *local_err = NULL; - GSList *i; - CharDriver *cd; - bool be_opened = true; + ObjectClass *oc; + const ChardevClass *cc; + char *typename = g_strdup_printf("chardev-%s", driver); - chr = qemu_chr_find(id); - if (chr) { - error_setg(errp, "Chardev '%s' already exists", id); - goto out_error; - } + oc = object_class_by_name(typename); + g_free(typename); - for (i = backends; i; i = i->next) { - cd = i->data; + if (!object_class_dynamic_cast(oc, TYPE_CHARDEV)) { + error_setg(errp, "'%s' is not a valid char driver name", driver); + return NULL; + } - if (cd->kind == backend->type) { - chr = cd->create(id, backend, ret, &be_opened, &local_err); - if (local_err) { - error_propagate(errp, local_err); - goto out_error; - } - break; - } + if (object_class_is_abstract(oc)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "driver", + "abstract device type"); + return NULL; } - if (chr == NULL) { - assert(!i); - error_setg(errp, "chardev backend not available"); - goto out_error; + cc = CHARDEV_CLASS(oc); + if (cc->internal) { + error_setg(errp, "'%s' is not a valid char driver name", driver); + return NULL; } + return cc; +} + +Chardev *qemu_chardev_new(const char *id, const char *typename, + ChardevBackend *backend, Error **errp) +{ + Chardev *chr = NULL; + Error *local_err = NULL; + bool be_opened = true; + + assert(g_str_has_prefix(typename, "chardev-")); + + chr = CHARDEV(object_new(typename)); chr->label = g_strdup(id); + + qemu_char_open(chr, backend, &be_opened, &local_err); + if (local_err) { + error_propagate(errp, local_err); + object_unref(OBJECT(chr)); + return NULL; + } + if (!chr->filename) { - chr->filename = g_strdup(ChardevBackendKind_lookup[backend->type]); + chr->filename = g_strdup(typename + 8); } if (be_opened) { qemu_chr_be_event(chr, CHR_EVENT_OPENED); } + + return chr; +} + +ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, + Error **errp) +{ + const ChardevClass *cc; + ChardevReturn *ret; + Chardev *chr; + + chr = qemu_chr_find(id); + if (chr) { + error_setg(errp, "Chardev '%s' already exists", id); + return NULL; + } + + cc = char_get_class(ChardevBackendKind_lookup[backend->type], errp); + if (!cc) { + return NULL; + } + + chr = qemu_chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)), + backend, errp); + if (!chr) { + return NULL; + } + + ret = g_new0(ChardevReturn, 1); + if (CHARDEV_IS_PTY(chr)) { + ret->pty = g_strdup(chr->filename + 4); + ret->has_pty = true; + } + QTAILQ_INSERT_TAIL(&chardevs, chr, next); return ret; - -out_error: - g_free(ret); - return NULL; } void qmp_chardev_remove(const char *id, Error **errp) { - CharDriverState *chr; + Chardev *chr; chr = qemu_chr_find(id); if (chr == NULL) { @@ -4895,7 +5102,7 @@ void qmp_chardev_remove(const char *id, Error **errp) error_setg(errp, "Chardev '%s' is busy", id); return; } - if (chr->replay) { + if (qemu_chr_replay(chr)) { error_setg(errp, "Chardev '%s' cannot be unplugged in record/replay mode", id); return; @@ -4905,7 +5112,7 @@ void qmp_chardev_remove(const char *id, Error **errp) void qemu_chr_cleanup(void) { - CharDriverState *chr, *tmp; + Chardev *chr, *tmp; QTAILQ_FOREACH_SAFE(chr, &chardevs, next, tmp) { qemu_chr_delete(chr); @@ -4914,45 +5121,46 @@ void qemu_chr_cleanup(void) static void register_types(void) { - register_char_driver("null", CHARDEV_BACKEND_KIND_NULL, NULL, - qemu_chr_open_null); - register_char_driver("socket", CHARDEV_BACKEND_KIND_SOCKET, - qemu_chr_parse_socket, qmp_chardev_open_socket); - register_char_driver("udp", CHARDEV_BACKEND_KIND_UDP, qemu_chr_parse_udp, - qmp_chardev_open_udp); - register_char_driver("ringbuf", CHARDEV_BACKEND_KIND_RINGBUF, - qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf); - register_char_driver("file", CHARDEV_BACKEND_KIND_FILE, - qemu_chr_parse_file_out, qmp_chardev_open_file); - register_char_driver("stdio", CHARDEV_BACKEND_KIND_STDIO, - qemu_chr_parse_stdio, qemu_chr_open_stdio); -#if defined HAVE_CHARDEV_SERIAL - register_char_driver("serial", CHARDEV_BACKEND_KIND_SERIAL, - qemu_chr_parse_serial, qmp_chardev_open_serial); - register_char_driver("tty", CHARDEV_BACKEND_KIND_SERIAL, - qemu_chr_parse_serial, qmp_chardev_open_serial); + static const struct { + const CharDriver *driver; + const TypeInfo *type; + } chardevs[] = { + { &null_driver, &char_null_type_info }, + { &socket_driver, &char_socket_type_info }, + { &udp_driver, &char_udp_type_info }, + { &ringbuf_driver, &char_ringbuf_type_info }, + { &file_driver, &char_file_type_info }, + { &stdio_driver, &char_stdio_type_info }, +#ifdef HAVE_CHARDEV_SERIAL + { &serial_driver, &char_serial_type_info }, #endif #ifdef HAVE_CHARDEV_PARPORT - register_char_driver("parallel", CHARDEV_BACKEND_KIND_PARALLEL, - qemu_chr_parse_parallel, qmp_chardev_open_parallel); - register_char_driver("parport", CHARDEV_BACKEND_KIND_PARALLEL, - qemu_chr_parse_parallel, qmp_chardev_open_parallel); + { ¶llel_driver, &char_parallel_type_info }, #endif #ifdef HAVE_CHARDEV_PTY - register_char_driver("pty", CHARDEV_BACKEND_KIND_PTY, NULL, - qemu_chr_open_pty); + { &pty_driver, &char_pty_type_info }, #endif #ifdef _WIN32 - register_char_driver("console", CHARDEV_BACKEND_KIND_CONSOLE, NULL, - qemu_chr_open_win_con); + { &console_driver, &char_console_type_info }, #endif - register_char_driver("pipe", CHARDEV_BACKEND_KIND_PIPE, - qemu_chr_parse_pipe, qemu_chr_open_pipe); - register_char_driver("mux", CHARDEV_BACKEND_KIND_MUX, qemu_chr_parse_mux, - qemu_chr_open_mux); - /* Bug-compatibility: */ - register_char_driver("memory", CHARDEV_BACKEND_KIND_MEMORY, - qemu_chr_parse_ringbuf, qemu_chr_open_ringbuf); + { &pipe_driver, &char_pipe_type_info }, + { &mux_driver, &char_mux_type_info }, + { &memory_driver, &char_memory_type_info } + }; + int i; + + type_register_static(&char_type_info); +#ifndef _WIN32 + type_register_static(&char_fd_type_info); +#else + type_register_static(&char_win_type_info); + type_register_static(&char_win_stdio_type_info); +#endif + for (i = 0; i < ARRAY_SIZE(chardevs); i++) { + type_register_static(chardevs[i].type); + register_char_driver(chardevs[i].driver); + } + /* this must be done after machine init, since we register FEs with muxes * as part of realize functions like serial_isa_realizefn when -nographic * is specified diff --git a/qemu-options.hx b/qemu-options.hx index 588e5beab3..ad2f8fc873 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2430,8 +2430,6 @@ Connect to standard input and standard output of the QEMU process. exiting QEMU with the key sequence @key{Control-c}. This option is enabled by default, use @option{signal=off} to disable it. -@option{stdio} is not available on Windows hosts. - @item -chardev braille ,id=@var{id} Connect to a local BrlAPI server. @option{braille} does not take any options. @@ -3017,7 +3015,7 @@ udp::4555@@:4556} to QEMU. Another approach is to use a patched version of netcat which can listen to a TCP port and send and receive characters via udp. If you have a patched version of netcat which activates telnet remote echo and single char transfer, then you can -use the following options to step up a netcat redirector to allow +use the following options to set up a netcat redirector to allow telnet on port 5555 to access the QEMU port. @table @code @item QEMU Options: @@ -3400,12 +3398,12 @@ re-inject them. ETEXI DEF("icount", HAS_ARG, QEMU_OPTION_icount, \ - "-icount [shift=N|auto][,align=on|off][,sleep=on|off,rr=record|replay,rrfile=<filename>]\n" \ + "-icount [shift=N|auto][,align=on|off][,sleep=on|off,rr=record|replay,rrfile=<filename>,rrsnapshot=<snapshot>]\n" \ " enable virtual instruction counter with 2^N clock ticks per\n" \ " instruction, enable aligning the host and virtual clocks\n" \ " or disable real time cpu sleeping\n", QEMU_ARCH_ALL) STEXI -@item -icount [shift=@var{N}|auto][,rr=record|replay,rrfile=@var{filename}] +@item -icount [shift=@var{N}|auto][,rr=record|replay,rrfile=@var{filename},rrsnapshot=@var{snapshot}] @findex -icount Enable virtual instruction counter. The virtual cpu will execute one instruction every 2^@var{N} ns of virtual time. If @code{auto} is specified @@ -3438,6 +3436,10 @@ when the shift value is high (how high depends on the host machine). When @option{rr} option is specified deterministic record/replay is enabled. Replay log is written into @var{filename} file in record mode and read from this file in replay mode. + +Option rrsnapshot is used to create new vm snapshot named @var{snapshot} +at the start of execution recording. In replay mode this option is used +to load the initial VM state. ETEXI DEF("watchdog", HAS_ARG, QEMU_OPTION_watchdog, \ @@ -616,7 +616,7 @@ void qmp_add_client(const char *protocol, const char *fdname, bool has_skipauth, bool skipauth, bool has_tls, bool tls, Error **errp) { - CharDriverState *s; + Chardev *s; int fd; fd = monitor_get_fd(cur_mon, fdname, errp); @@ -415,6 +415,7 @@ static void cpu_class_init(ObjectClass *klass, void *data) k->cpu_exec_enter = cpu_common_noop; k->cpu_exec_exit = cpu_common_noop; k->cpu_exec_interrupt = cpu_common_exec_interrupt; + set_bit(DEVICE_CATEGORY_CPU, dc->categories); dc->realize = cpu_common_realizefn; dc->unrealize = cpu_common_unrealizefn; /* @@ -670,7 +670,7 @@ static int qtest_init_accel(MachineState *ms) void qtest_init(const char *qtest_chrdev, const char *qtest_log, Error **errp) { - CharDriverState *chr; + Chardev *chr; chr = qemu_chr_new("qtest", qtest_chrdev); diff --git a/replay/replay-char.c b/replay/replay-char.c index edf46ab9df..aa65955942 100755 --- a/replay/replay-char.c +++ b/replay/replay-char.c @@ -18,7 +18,7 @@ /* Char drivers that generate qemu_chr_be_write events that should be saved into the log. */ -static CharDriverState **char_drivers; +static Chardev **char_drivers; static int drivers_count; /* Char event attributes. */ @@ -28,7 +28,7 @@ typedef struct CharEvent { size_t len; } CharEvent; -static int find_char_driver(CharDriverState *chr) +static int find_char_driver(Chardev *chr) { int i = 0; for ( ; i < drivers_count ; ++i) { @@ -39,7 +39,7 @@ static int find_char_driver(CharDriverState *chr) return -1; } -void replay_register_char_driver(CharDriverState *chr) +void replay_register_char_driver(Chardev *chr) { if (replay_mode == REPLAY_MODE_NONE) { return; @@ -49,7 +49,7 @@ void replay_register_char_driver(CharDriverState *chr) char_drivers[drivers_count++] = chr; } -void replay_chr_be_write(CharDriverState *s, uint8_t *buf, int len) +void replay_chr_be_write(Chardev *s, uint8_t *buf, int len) { CharEvent *event = g_malloc0(sizeof(CharEvent)); diff --git a/replay/replay-snapshot.c b/replay/replay-snapshot.c index 498059734d..65e2d375c2 100644 --- a/replay/replay-snapshot.c +++ b/replay/replay-snapshot.c @@ -59,3 +59,20 @@ void replay_vmstate_register(void) { vmstate_register(NULL, 0, &vmstate_replay, &replay_state); } + +void replay_vmstate_init(void) +{ + if (replay_snapshot) { + if (replay_mode == REPLAY_MODE_RECORD) { + if (save_vmstate(cur_mon, replay_snapshot) != 0) { + error_report("Could not create snapshot for icount record"); + exit(1); + } + } else if (replay_mode == REPLAY_MODE_PLAY) { + if (load_vmstate(replay_snapshot) != 0) { + error_report("Could not load snapshot for icount replay"); + exit(1); + } + } + } +} diff --git a/replay/replay.c b/replay/replay.c index 7f27cf17b0..1835b9902e 100644 --- a/replay/replay.c +++ b/replay/replay.c @@ -26,6 +26,7 @@ #define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t)) ReplayMode replay_mode = REPLAY_MODE_NONE; +char *replay_snapshot; /* Name of replay file */ static char *replay_filename; @@ -292,6 +293,7 @@ void replay_configure(QemuOpts *opts) exit(1); } + replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot")); replay_vmstate_register(); replay_enable(fname, mode); @@ -346,6 +348,9 @@ void replay_finish(void) replay_filename = NULL; } + g_free(replay_snapshot); + replay_snapshot = NULL; + replay_finish_events(); replay_mutex_destroy(); } diff --git a/spice-qemu-char.c b/spice-qemu-char.c index 276c4aef68..dd97c17fca 100644 --- a/spice-qemu-char.c +++ b/spice-qemu-char.c @@ -2,42 +2,51 @@ #include "trace.h" #include "ui/qemu-spice.h" #include "sysemu/char.h" +#include "qemu/error-report.h" #include <spice.h> #include <spice/protocol.h> -typedef struct SpiceCharDriver { - CharDriverState* chr; - SpiceCharDeviceInstance sin; +typedef struct SpiceChardev { + Chardev parent; + + SpiceCharDeviceInstance sin; bool active; bool blocked; const uint8_t *datapos; int datalen; - QLIST_ENTRY(SpiceCharDriver) next; -} SpiceCharDriver; + QLIST_ENTRY(SpiceChardev) next; +} SpiceChardev; + +#define TYPE_CHARDEV_SPICE "chardev-spice" +#define TYPE_CHARDEV_SPICEVMC "chardev-spicevmc" +#define TYPE_CHARDEV_SPICEPORT "chardev-spiceport" + +#define SPICE_CHARDEV(obj) OBJECT_CHECK(SpiceChardev, (obj), TYPE_CHARDEV_SPICE) typedef struct SpiceCharSource { GSource source; - SpiceCharDriver *scd; + SpiceChardev *scd; } SpiceCharSource; -static QLIST_HEAD(, SpiceCharDriver) spice_chars = +static QLIST_HEAD(, SpiceChardev) spice_chars = QLIST_HEAD_INITIALIZER(spice_chars); static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len) { - SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin); + SpiceChardev *scd = container_of(sin, SpiceChardev, sin); + Chardev *chr = CHARDEV(scd); ssize_t out = 0; ssize_t last_out; uint8_t* p = (uint8_t*)buf; while (len > 0) { - int can_write = qemu_chr_be_can_write(scd->chr); + int can_write = qemu_chr_be_can_write(chr); last_out = MIN(len, can_write); if (last_out <= 0) { break; } - qemu_chr_be_write(scd->chr, p, last_out); + qemu_chr_be_write(chr, p, last_out); out += last_out; len -= last_out; p += last_out; @@ -49,7 +58,7 @@ static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len) static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len) { - SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin); + SpiceChardev *scd = container_of(sin, SpiceChardev, sin); int bytes = MIN(len, scd->datalen); if (bytes > 0) { @@ -69,7 +78,8 @@ static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len) #if SPICE_SERVER_VERSION >= 0x000c02 static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event) { - SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin); + SpiceChardev *scd = container_of(sin, SpiceChardev, sin); + Chardev *chr = CHARDEV(scd); int chr_event; switch (event) { @@ -81,20 +91,21 @@ static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event) } trace_spice_vmc_event(chr_event); - qemu_chr_be_event(scd->chr, chr_event); + qemu_chr_be_event(chr, chr_event); } #endif static void vmc_state(SpiceCharDeviceInstance *sin, int connected) { - SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin); + SpiceChardev *scd = container_of(sin, SpiceChardev, sin); + Chardev *chr = CHARDEV(scd); - if ((scd->chr->be_open && connected) || - (!scd->chr->be_open && !connected)) { + if ((chr->be_open && connected) || + (!chr->be_open && !connected)) { return; } - qemu_chr_be_event(scd->chr, + qemu_chr_be_event(chr, connected ? CHR_EVENT_OPENED : CHR_EVENT_CLOSED); } @@ -115,7 +126,7 @@ static SpiceCharDeviceInterface vmc_interface = { }; -static void vmc_register_interface(SpiceCharDriver *scd) +static void vmc_register_interface(SpiceChardev *scd) { if (scd->active) { return; @@ -126,7 +137,7 @@ static void vmc_register_interface(SpiceCharDriver *scd) trace_spice_vmc_register_interface(scd); } -static void vmc_unregister_interface(SpiceCharDriver *scd) +static void vmc_unregister_interface(SpiceChardev *scd) { if (!scd->active) { return; @@ -166,9 +177,9 @@ static GSourceFuncs SpiceCharSourceFuncs = { .dispatch = spice_char_source_dispatch, }; -static GSource *spice_chr_add_watch(CharDriverState *chr, GIOCondition cond) +static GSource *spice_chr_add_watch(Chardev *chr, GIOCondition cond) { - SpiceCharDriver *scd = chr->opaque; + SpiceChardev *scd = SPICE_CHARDEV(chr); SpiceCharSource *src; assert(cond & G_IO_OUT); @@ -180,9 +191,9 @@ static GSource *spice_chr_add_watch(CharDriverState *chr, GIOCondition cond) return (GSource *)src; } -static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +static int spice_chr_write(Chardev *chr, const uint8_t *buf, int len) { - SpiceCharDriver *s = chr->opaque; + SpiceChardev *s = SPICE_CHARDEV(chr); int read_bytes; assert(s->datalen == 0); @@ -199,9 +210,9 @@ static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len) return read_bytes; } -static void spice_chr_free(struct CharDriverState *chr) +static void spice_chr_free(struct Chardev *chr) { - SpiceCharDriver *s = chr->opaque; + SpiceChardev *s = SPICE_CHARDEV(chr); vmc_unregister_interface(s); QLIST_REMOVE(s, next); @@ -210,12 +221,11 @@ static void spice_chr_free(struct CharDriverState *chr) #if SPICE_SERVER_VERSION >= 0x000c02 g_free((char *)s->sin.portname); #endif - g_free(s); } -static void spice_vmc_set_fe_open(struct CharDriverState *chr, int fe_open) +static void spice_vmc_set_fe_open(struct Chardev *chr, int fe_open) { - SpiceCharDriver *s = chr->opaque; + SpiceChardev *s = SPICE_CHARDEV(chr); if (fe_open) { vmc_register_interface(s); } else { @@ -223,10 +233,10 @@ static void spice_vmc_set_fe_open(struct CharDriverState *chr, int fe_open) } } -static void spice_port_set_fe_open(struct CharDriverState *chr, int fe_open) +static void spice_port_set_fe_open(struct Chardev *chr, int fe_open) { #if SPICE_SERVER_VERSION >= 0x000c02 - SpiceCharDriver *s = chr->opaque; + SpiceChardev *s = SPICE_CHARDEV(chr); if (fe_open) { spice_server_port_event(&s->sin, SPICE_PORT_EVENT_OPENED); @@ -236,69 +246,31 @@ static void spice_port_set_fe_open(struct CharDriverState *chr, int fe_open) #endif } -static void print_allowed_subtypes(void) +static void spice_chr_accept_input(struct Chardev *chr) { - const char** psubtype; - int i; - - fprintf(stderr, "allowed names: "); - for(i=0, psubtype = spice_server_char_device_recognized_subtypes(); - *psubtype != NULL; ++psubtype, ++i) { - if (i == 0) { - fprintf(stderr, "%s", *psubtype); - } else { - fprintf(stderr, ", %s", *psubtype); - } - } - fprintf(stderr, "\n"); -} - -static void spice_chr_accept_input(struct CharDriverState *chr) -{ - SpiceCharDriver *s = chr->opaque; + SpiceChardev *s = SPICE_CHARDEV(chr); spice_server_char_device_wakeup(&s->sin); } -static CharDriverState *chr_open(const char *subtype, - void (*set_fe_open)(struct CharDriverState *, - int), - ChardevCommon *backend, - Error **errp) +static void chr_open(Chardev *chr, const char *subtype) { - CharDriverState *chr; - SpiceCharDriver *s; + SpiceChardev *s = SPICE_CHARDEV(chr); - chr = qemu_chr_alloc(backend, errp); - if (!chr) { - return NULL; - } - s = g_malloc0(sizeof(SpiceCharDriver)); - s->chr = chr; s->active = false; s->sin.subtype = g_strdup(subtype); - chr->opaque = s; - chr->chr_write = spice_chr_write; - chr->chr_add_watch = spice_chr_add_watch; - chr->chr_free = spice_chr_free; - chr->chr_set_fe_open = set_fe_open; - chr->chr_accept_input = spice_chr_accept_input; QLIST_INSERT_HEAD(&spice_chars, s, next); - - return chr; } -static CharDriverState *qemu_chr_open_spice_vmc(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, - bool *be_opened, - Error **errp) +static void qemu_chr_open_spice_vmc(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { ChardevSpiceChannel *spicevmc = backend->u.spicevmc.data; const char *type = spicevmc->type; const char **psubtype = spice_server_char_device_recognized_subtypes(); - ChardevCommon *common = qapi_ChardevSpiceChannel_base(spicevmc); for (; *psubtype != NULL; ++psubtype) { if (strcmp(type, *psubtype) == 0) { @@ -306,47 +278,46 @@ static CharDriverState *qemu_chr_open_spice_vmc(const char *id, } } if (*psubtype == NULL) { - fprintf(stderr, "spice-qemu-char: unsupported type: %s\n", type); - print_allowed_subtypes(); - return NULL; + char *subtypes = g_strjoinv(", ", + (gchar **)spice_server_char_device_recognized_subtypes()); + + error_setg(errp, "unsupported type name: %s", type); + error_append_hint(errp, "allowed spice char type names: %s\n", + subtypes); + + g_free(subtypes); + return; } *be_opened = false; - return chr_open(type, spice_vmc_set_fe_open, common, errp); + chr_open(chr, type); } #if SPICE_SERVER_VERSION >= 0x000c02 -static CharDriverState *qemu_chr_open_spice_port(const char *id, - ChardevBackend *backend, - ChardevReturn *ret, - bool *be_opened, - Error **errp) +static void qemu_chr_open_spice_port(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { ChardevSpicePort *spiceport = backend->u.spiceport.data; const char *name = spiceport->fqdn; - ChardevCommon *common = qapi_ChardevSpicePort_base(spiceport); - CharDriverState *chr; - SpiceCharDriver *s; + SpiceChardev *s; if (name == NULL) { - fprintf(stderr, "spice-qemu-char: missing name parameter\n"); - return NULL; + error_setg(errp, "missing name parameter"); + return; } - chr = chr_open("port", spice_port_set_fe_open, common, errp); - if (!chr) { - return NULL; - } + chr_open(chr, "port"); + *be_opened = false; - s = chr->opaque; + s = SPICE_CHARDEV(chr); s->sin.portname = g_strdup(name); - - return chr; } void qemu_spice_register_ports(void) { - SpiceCharDriver *s; + SpiceChardev *s; QLIST_FOREACH(s, &spice_chars, next) { if (s->sin.portname == NULL) { @@ -387,12 +358,68 @@ static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend, spiceport->fqdn = g_strdup(name); } +static void char_spice_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->chr_write = spice_chr_write; + cc->chr_add_watch = spice_chr_add_watch; + cc->chr_accept_input = spice_chr_accept_input; + cc->chr_free = spice_chr_free; +} + +static const TypeInfo char_spice_type_info = { + .name = TYPE_CHARDEV_SPICE, + .parent = TYPE_CHARDEV, + .instance_size = sizeof(SpiceChardev), + .class_init = char_spice_class_init, + .abstract = true, +}; + +static void char_spicevmc_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->open = qemu_chr_open_spice_vmc; + cc->chr_set_fe_open = spice_vmc_set_fe_open; +} + +static const TypeInfo char_spicevmc_type_info = { + .name = TYPE_CHARDEV_SPICEVMC, + .parent = TYPE_CHARDEV_SPICE, + .class_init = char_spicevmc_class_init, +}; + +static void char_spiceport_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->open = qemu_chr_open_spice_port; + cc->chr_set_fe_open = spice_port_set_fe_open; +} + +static const TypeInfo char_spiceport_type_info = { + .name = TYPE_CHARDEV_SPICEPORT, + .parent = TYPE_CHARDEV_SPICE, + .class_init = char_spiceport_class_init, +}; + static void register_types(void) { - register_char_driver("spicevmc", CHARDEV_BACKEND_KIND_SPICEVMC, - qemu_chr_parse_spice_vmc, qemu_chr_open_spice_vmc); - register_char_driver("spiceport", CHARDEV_BACKEND_KIND_SPICEPORT, - qemu_chr_parse_spice_port, qemu_chr_open_spice_port); + static const CharDriver vmc_driver = { + .kind = CHARDEV_BACKEND_KIND_SPICEVMC, + .parse = qemu_chr_parse_spice_vmc, + }; + static const CharDriver port_driver = { + .kind = CHARDEV_BACKEND_KIND_SPICEPORT, + .parse = qemu_chr_parse_spice_port, + }; + register_char_driver(&vmc_driver); + register_char_driver(&port_driver); + + type_register_static(&char_spice_type_info); + type_register_static(&char_spicevmc_type_info); + type_register_static(&char_spiceport_type_info); } type_init(register_types); diff --git a/stubs/monitor.c b/stubs/monitor.c index 1d574b1c6f..e018c8f594 100644 --- a/stubs/monitor.c +++ b/stubs/monitor.c @@ -11,6 +11,6 @@ int monitor_get_fd(Monitor *mon, const char *name, Error **errp) return -1; } -void monitor_init(CharDriverState *chr, int flags) +void monitor_init(Chardev *chr, int flags) { } diff --git a/stubs/replay.c b/stubs/replay.c index d9a6da99d2..9c8aa48c9c 100644 --- a/stubs/replay.c +++ b/stubs/replay.c @@ -30,11 +30,11 @@ void replay_finish(void) { } -void replay_register_char_driver(CharDriverState *chr) +void replay_register_char_driver(Chardev *chr) { } -void replay_chr_be_write(CharDriverState *s, uint8_t *buf, int len) +void replay_chr_be_write(Chardev *s, uint8_t *buf, int len) { abort(); } diff --git a/target/i386/cpu.c b/target/i386/cpu.c index cff23e129d..eb49980ef1 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -3658,6 +3658,7 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_BOOL("cpuid-0xb", X86CPU, enable_cpuid_0xb, true), DEFINE_PROP_BOOL("lmce", X86CPU, enable_lmce, false), DEFINE_PROP_BOOL("l3-cache", X86CPU, enable_l3_cache, true), + DEFINE_PROP_BOOL("vmware-cpuid-freq", X86CPU, vmware_cpuid_freq, true), DEFINE_PROP_END_OF_LIST() }; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 10c5a3538d..4d788d56fc 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1214,6 +1214,10 @@ struct X86CPU { bool host_features; uint32_t apic_id; + /* Enables publishing of TSC increment and Local APIC bus frequencies to + * the guest OS in CPUID page 0x40000010, the same way that VMWare does. */ + bool vmware_cpuid_freq; + /* if true the CPUID code directly forward host cache leaves to the guest */ bool cache_info_passthrough; diff --git a/target/i386/kvm.c b/target/i386/kvm.c index 8e130ccf9c..27fd0505df 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -982,12 +982,6 @@ int kvm_arch_init_vcpu(CPUState *cs) } } - cpuid_data.cpuid.padding = 0; - r = kvm_vcpu_ioctl(cs, KVM_SET_CPUID2, &cpuid_data); - if (r) { - goto fail; - } - r = kvm_arch_set_tsc_khz(cs); if (r < 0) { goto fail; @@ -1007,6 +1001,36 @@ int kvm_arch_init_vcpu(CPUState *cs) } } + if (cpu->vmware_cpuid_freq + /* Guests depend on 0x40000000 to detect this feature, so only expose + * it if KVM exposes leaf 0x40000000. (Conflicts with Hyper-V) */ + && cpu->expose_kvm + && kvm_base == KVM_CPUID_SIGNATURE + /* TSC clock must be stable and known for this feature. */ + && ((env->features[FEAT_8000_0007_EDX] & CPUID_APM_INVTSC) + || env->user_tsc_khz != 0) + && env->tsc_khz != 0) { + + c = &cpuid_data.entries[cpuid_i++]; + c->function = KVM_CPUID_SIGNATURE | 0x10; + c->eax = env->tsc_khz; + /* LAPIC resolution of 1ns (freq: 1GHz) is hardcoded in KVM's + * APIC_BUS_CYCLE_NS */ + c->ebx = 1000000; + c->ecx = c->edx = 0; + + c = cpuid_find_entry(&cpuid_data.cpuid, kvm_base, 0); + c->eax = MAX(c->eax, KVM_CPUID_SIGNATURE | 0x10); + } + + cpuid_data.cpuid.nent = cpuid_i; + + cpuid_data.cpuid.padding = 0; + r = kvm_vcpu_ioctl(cs, KVM_SET_CPUID2, &cpuid_data); + if (r) { + goto fail; + } + if (has_xsave) { env->kvm_xsave_buf = qemu_memalign(4096, sizeof(struct kvm_xsave)); } diff --git a/target/i386/seg_helper.c b/target/i386/seg_helper.c index fb79f3180d..d24574da7f 100644 --- a/target/i386/seg_helper.c +++ b/target/i386/seg_helper.c @@ -1331,6 +1331,7 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) #endif if (interrupt_request & CPU_INTERRUPT_SIPI) { do_cpu_sipi(cpu); + ret = true; } else if (env->hflags2 & HF2_GIF_MASK) { if ((interrupt_request & CPU_INTERRUPT_SMI) && !(env->hflags & HF_SMM_MASK)) { diff --git a/tests/Makefile.include b/tests/Makefile.include index 22ea256e94..33b4f88746 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -510,7 +510,7 @@ tests/check-qjson$(EXESUF): tests/check-qjson.o $(test-util-obj-y) tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(test-qom-obj-y) tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(test-qom-obj-y) -tests/test-char$(EXESUF): tests/test-char.o qemu-char.o qemu-timer.o $(test-util-obj-y) $(qtest-obj-y) $(test-io-obj-y) +tests/test-char$(EXESUF): tests/test-char.o qemu-char.o qemu-timer.o $(test-util-obj-y) $(qtest-obj-y) $(test-block-obj-y) tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y) tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y) tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y) diff --git a/tests/test-char.c b/tests/test-char.c index 241685afbb..da69f110e4 100644 --- a/tests/test-char.c +++ b/tests/test-char.c @@ -40,7 +40,7 @@ static void fe_event(void *opaque, int event) #ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS static void char_stdio_test_subprocess(void) { - CharDriverState *chr; + Chardev *chr; CharBackend be; int ret; @@ -68,7 +68,7 @@ static void char_stdio_test(void) static void char_ringbuf_test(void) { QemuOpts *opts; - CharDriverState *chr; + Chardev *chr; CharBackend be; char *data; int ret; @@ -109,7 +109,7 @@ static void char_ringbuf_test(void) static void char_mux_test(void) { QemuOpts *opts; - CharDriverState *chr, *base; + Chardev *chr, *base; char *data; FeHandler h1 = { 0, }, h2 = { 0, }; CharBackend chr_be1, chr_be2; @@ -185,7 +185,7 @@ static void char_mux_test(void) static void char_null_test(void) { Error *err = NULL; - CharDriverState *chr; + Chardev *chr; CharBackend be; int ret; @@ -227,7 +227,7 @@ static void char_null_test(void) static void char_invalid_test(void) { - CharDriverState *chr; + Chardev *chr; chr = qemu_chr_new("label-invalid", "invalid"); g_assert_null(chr); diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index 96bf00eefa..f3ac6ea21a 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -454,7 +454,7 @@ static void chr_event(void *opaque, int event) static void test_server_create_chr(TestServer *server, const gchar *opt) { gchar *chr_path; - CharDriverState *chr; + Chardev *chr; chr_path = g_strdup_printf("unix:%s%s", server->socket_path, opt); chr = qemu_chr_new(server->chr_name, chr_path); @@ -486,7 +486,7 @@ static inline void test_server_connect(TestServer *server) static gboolean _test_server_free(TestServer *server) { int i; - CharDriverState *chr = qemu_chr_fe_get_driver(&server->chr); + Chardev *chr = qemu_chr_fe_get_driver(&server->chr); qemu_chr_fe_deinit(&server->chr); qemu_chr_delete(chr); diff --git a/translate-all.c b/translate-all.c index 20262938bb..6d2fcabca7 100644 --- a/translate-all.c +++ b/translate-all.c @@ -1290,6 +1290,8 @@ TranslationBlock *tb_gen_code(CPUState *cpu, /* flush must be done */ tb_flush(cpu); mmap_unlock(); + /* Make the execution loop process the flush as soon as possible. */ + cpu->exception_index = EXCP_INTERRUPT; cpu_loop_exit(cpu); } diff --git a/ui/console.c b/ui/console.c index b9575f2ee5..fe03a666f7 100644 --- a/ui/console.c +++ b/ui/console.c @@ -158,7 +158,7 @@ struct QemuConsole { int esc_params[MAX_ESC_PARAMS]; int nb_esc_params; - CharDriverState *chr; + Chardev *chr; /* fifo for key pressed */ QEMUFIFO out_fifo; uint8_t out_fifo_buf[16]; @@ -183,7 +183,7 @@ static int nb_consoles = 0; static bool cursor_visible_phase; static QEMUTimer *cursor_timer; -static void text_console_do_init(CharDriverState *chr, DisplayState *ds); +static void text_console_do_init(Chardev *chr, DisplayState *ds); static void dpy_refresh(DisplayState *s); static DisplayState *get_alloc_displaystate(void); static void text_console_update_cursor_timer(void); @@ -1046,11 +1046,24 @@ void console_select(unsigned int index) } } -static int console_puts(CharDriverState *chr, const uint8_t *buf, int len) +typedef struct VCChardev { + Chardev parent; + QemuConsole *console; +} VCChardev; + +#define TYPE_CHARDEV_VC "chardev-vc" +#define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC) + +static int vc_chr_write(Chardev *chr, const uint8_t *buf, int len) { - QemuConsole *s = chr->opaque; + VCChardev *drv = VC_CHARDEV(chr); + QemuConsole *s = drv->console; int i; + if (!s->ds) { + return 0; + } + s->update_x0 = s->width * FONT_WIDTH; s->update_y0 = s->height * FONT_HEIGHT; s->update_x1 = 0; @@ -1129,13 +1142,13 @@ void kbd_put_keysym_console(QemuConsole *s, int keysym) *q++ = '['; *q++ = keysym & 0xff; } else if (s->echo && (keysym == '\r' || keysym == '\n')) { - console_puts(s->chr, (const uint8_t *) "\r", 1); + vc_chr_write(s->chr, (const uint8_t *) "\r", 1); *q++ = '\n'; } else { *q++ = keysym; } if (s->echo) { - console_puts(s->chr, buf, q - buf); + vc_chr_write(s->chr, buf, q - buf); } be = s->chr->be; if (be && be->chr_read) { @@ -1952,9 +1965,10 @@ int qemu_console_get_height(QemuConsole *con, int fallback) return con ? surface_height(con->surface) : fallback; } -static void text_console_set_echo(CharDriverState *chr, bool echo) +static void vc_chr_set_echo(Chardev *chr, bool echo) { - QemuConsole *s = chr->opaque; + VCChardev *drv = VC_CHARDEV(chr); + QemuConsole *s = drv->console; s->echo = echo; } @@ -1992,16 +2006,13 @@ static const GraphicHwOps text_console_ops = { .text_update = text_console_update, }; -static void text_console_do_init(CharDriverState *chr, DisplayState *ds) +static void text_console_do_init(Chardev *chr, DisplayState *ds) { - QemuConsole *s; + VCChardev *drv = VC_CHARDEV(chr); + QemuConsole *s = drv->console; int g_width = 80 * FONT_WIDTH; int g_height = 24 * FONT_HEIGHT; - s = chr->opaque; - - chr->chr_write = console_puts; - s->out_fifo.buf = s->out_fifo_buf; s->out_fifo.buf_size = sizeof(s->out_fifo_buf); s->kbd_timer = timer_new_ms(QEMU_CLOCK_REALTIME, kbd_send_chars, s); @@ -2041,26 +2052,26 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds) s->t_attrib.bgcol = QEMU_COLOR_BLUE; len = snprintf(msg, sizeof(msg), "%s console\r\n", chr->label); - console_puts(chr, (uint8_t*)msg, len); + vc_chr_write(chr, (uint8_t *)msg, len); s->t_attrib = s->t_attrib_default; } qemu_chr_be_generic_open(chr); } -static CharDriverState *text_console_init(ChardevVC *vc, Error **errp) +static const CharDriver vc_driver; + +static void vc_chr_open(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { - ChardevCommon *common = qapi_ChardevVC_base(vc); - CharDriverState *chr; + ChardevVC *vc = backend->u.vc.data; + VCChardev *drv = VC_CHARDEV(chr); QemuConsole *s; unsigned width = 0; unsigned height = 0; - chr = qemu_chr_alloc(common, errp); - if (!chr) { - return NULL; - } - if (vc->has_width) { width = vc->width; } else if (vc->has_cols) { @@ -2082,37 +2093,21 @@ static CharDriverState *text_console_init(ChardevVC *vc, Error **errp) } if (!s) { - g_free(chr); error_setg(errp, "cannot create text console"); - return NULL; + return; } s->chr = chr; - chr->opaque = s; - chr->chr_set_echo = text_console_set_echo; + drv->console = s; if (display_state) { text_console_do_init(chr, display_state); } - return chr; -} - -static VcHandler *vc_handler = text_console_init; -static CharDriverState *vc_init(const char *id, ChardevBackend *backend, - ChardevReturn *ret, bool *be_opened, - Error **errp) -{ /* console/chardev init sometimes completes elsewhere in a 2nd * stage, so defer OPENED events until they are fully initialized */ *be_opened = false; - return vc_handler(backend->u.vc.data, errp); -} - -void register_vc_handler(VcHandler *handler) -{ - vc_handler = handler; } void qemu_console_resize(QemuConsole *s, int width, int height) @@ -2150,8 +2145,7 @@ PixelFormat qemu_default_pixelformat(int bpp) return pf; } -static void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, - Error **errp) +void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp) { int val; ChardevVC *vc; @@ -2191,12 +2185,39 @@ static const TypeInfo qemu_console_info = { .class_size = sizeof(QemuConsoleClass), }; +static void char_vc_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->open = vc_chr_open; + cc->chr_write = vc_chr_write; + cc->chr_set_echo = vc_chr_set_echo; +} + +static const TypeInfo char_vc_type_info = { + .name = TYPE_CHARDEV_VC, + .parent = TYPE_CHARDEV, + .instance_size = sizeof(VCChardev), + .class_init = char_vc_class_init, +}; + +void qemu_console_early_init(void) +{ + /* set the default vc driver */ + if (!object_class_by_name(TYPE_CHARDEV_VC)) { + type_register(&char_vc_type_info); + register_char_driver(&vc_driver); + } +} + +static const CharDriver vc_driver = { + .kind = CHARDEV_BACKEND_KIND_VC, + .parse = qemu_chr_parse_vc, +}; static void register_types(void) { type_register_static(&qemu_console_info); - register_char_driver("vc", CHARDEV_BACKEND_KIND_VC, qemu_chr_parse_vc, - vc_init); } type_init(register_types); @@ -181,6 +181,15 @@ struct GtkDisplayState { bool ignore_keys; }; +typedef struct VCChardev { + Chardev parent; + VirtualConsole *console; + bool echo; +} VCChardev; + +#define TYPE_CHARDEV_VC "chardev-vc" +#define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC) + static void gd_grab_pointer(VirtualConsole *vc, const char *reason); static void gd_ungrab_pointer(GtkDisplayState *s); static void gd_grab_keyboard(VirtualConsole *vc, const char *reason); @@ -1683,50 +1692,70 @@ static void gd_vc_adjustment_changed(GtkAdjustment *adjustment, void *opaque) } } -static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len) +static int gd_vc_chr_write(Chardev *chr, const uint8_t *buf, int len) { - VirtualConsole *vc = chr->opaque; + VCChardev *vcd = VC_CHARDEV(chr); + VirtualConsole *vc = vcd->console; vte_terminal_feed(VTE_TERMINAL(vc->vte.terminal), (const char *)buf, len); return len; } -static void gd_vc_chr_set_echo(CharDriverState *chr, bool echo) +static void gd_vc_chr_set_echo(Chardev *chr, bool echo) { - VirtualConsole *vc = chr->opaque; + VCChardev *vcd = VC_CHARDEV(chr); + VirtualConsole *vc = vcd->console; - vc->vte.echo = echo; + if (vc) { + vc->vte.echo = echo; + } else { + vcd->echo = echo; + } } static int nb_vcs; -static CharDriverState *vcs[MAX_VCS]; +static Chardev *vcs[MAX_VCS]; +static const CharDriver gd_vc_driver; -static CharDriverState *gd_vc_handler(ChardevVC *vc, Error **errp) +static void gd_vc_open(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { - ChardevCommon *common = qapi_ChardevVC_base(vc); - CharDriverState *chr; - if (nb_vcs == MAX_VCS) { error_setg(errp, "Maximum number of consoles reached"); - return NULL; - } - - chr = qemu_chr_alloc(common, errp); - if (!chr) { - return NULL; + return; } - chr->chr_write = gd_vc_chr_write; - chr->chr_set_echo = gd_vc_chr_set_echo; + vcs[nb_vcs++] = chr; - /* Temporary, until gd_vc_vte_init runs. */ - chr->opaque = g_new0(VirtualConsole, 1); + /* console/chardev init sometimes completes elsewhere in a 2nd + * stage, so defer OPENED events until they are fully initialized + */ + *be_opened = false; +} - vcs[nb_vcs++] = chr; +static void char_gd_vc_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); - return chr; + cc->open = gd_vc_open; + cc->chr_write = gd_vc_chr_write; + cc->chr_set_echo = gd_vc_chr_set_echo; } +static const TypeInfo char_gd_vc_type_info = { + .name = TYPE_CHARDEV_VC, + .parent = TYPE_CHARDEV, + .instance_size = sizeof(VCChardev), + .class_init = char_gd_vc_class_init, +}; + +static const CharDriver gd_vc_driver = { + .kind = CHARDEV_BACKEND_KIND_VC, + .parse = qemu_chr_parse_vc, +}; + static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size, gpointer user_data) { @@ -1755,21 +1784,19 @@ static gboolean gd_vc_in(VteTerminal *terminal, gchar *text, guint size, } static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, - CharDriverState *chr, int idx, + Chardev *chr, int idx, GSList *group, GtkWidget *view_menu) { char buffer[32]; GtkWidget *box; GtkWidget *scrollbar; GtkAdjustment *vadjustment; - VirtualConsole *tmp_vc = chr->opaque; + VCChardev *vcd = VC_CHARDEV(chr); vc->s = s; - vc->vte.echo = tmp_vc->vte.echo; - + vc->vte.echo = vcd->echo; vc->vte.chr = chr; - chr->opaque = vc; - g_free(tmp_vc); + vcd->console = vc; snprintf(buffer, sizeof(buffer), "vc%d", idx); vc->label = g_strdup_printf("%s", vc->vte.chr->label @@ -2325,6 +2352,7 @@ void early_gtk_display_init(int opengl) } #if defined(CONFIG_VTE) - register_vc_handler(gd_vc_handler); + type_register(&char_gd_vc_type_info); + register_char_driver(&gd_vc_driver); #endif } @@ -151,10 +151,10 @@ static int full_screen = 0; static int no_frame = 0; int no_quit = 0; static bool grab_on_hover; -CharDriverState *serial_hds[MAX_SERIAL_PORTS]; -CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; -CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES]; -CharDriverState *sclp_hds[MAX_SCLP_CONSOLES]; +Chardev *serial_hds[MAX_SERIAL_PORTS]; +Chardev *parallel_hds[MAX_PARALLEL_PORTS]; +Chardev *virtcon_hds[MAX_VIRTIO_CONSOLES]; +Chardev *sclp_hds[MAX_SCLP_CONSOLES]; int win2k_install_hack = 0; int singlestep = 0; int smp_cpus = 1; @@ -465,6 +465,9 @@ static QemuOptsList qemu_icount_opts = { }, { .name = "rrfile", .type = QEMU_OPT_STRING, + }, { + .name = "rrsnapshot", + .type = QEMU_OPT_STRING, }, { /* end of list */ } }, @@ -512,43 +515,6 @@ static QemuOptsList qemu_fw_cfg_opts = { }, }; -#ifdef CONFIG_LIBISCSI -static QemuOptsList qemu_iscsi_opts = { - .name = "iscsi", - .head = QTAILQ_HEAD_INITIALIZER(qemu_iscsi_opts.head), - .desc = { - { - .name = "user", - .type = QEMU_OPT_STRING, - .help = "username for CHAP authentication to target", - },{ - .name = "password", - .type = QEMU_OPT_STRING, - .help = "password for CHAP authentication to target", - },{ - .name = "password-secret", - .type = QEMU_OPT_STRING, - .help = "ID of the secret providing password for CHAP " - "authentication to target", - },{ - .name = "header-digest", - .type = QEMU_OPT_STRING, - .help = "HeaderDigest setting. " - "{CRC32C|CRC32C-NONE|NONE-CRC32C|NONE}", - },{ - .name = "initiator-name", - .type = QEMU_OPT_STRING, - .help = "Initiator iqn name to use when connecting", - },{ - .name = "timeout", - .type = QEMU_OPT_NUMBER, - .help = "Request timeout in seconds (default 0 = no timeout)", - }, - { /* end of list */ } - }, -}; -#endif - /** * Get machine options * @@ -2356,7 +2322,7 @@ static int fsdev_init_func(void *opaque, QemuOpts *opts, Error **errp) static int mon_init_func(void *opaque, QemuOpts *opts, Error **errp) { - CharDriverState *chr; + Chardev *chr; const char *chardev; const char *mode; int flags; @@ -3042,9 +3008,6 @@ int main(int argc, char **argv, char **envp) qemu_add_opts(&qemu_icount_opts); qemu_add_opts(&qemu_semihosting_config_opts); qemu_add_opts(&qemu_fw_cfg_opts); -#ifdef CONFIG_LIBISCSI - qemu_add_opts(&qemu_iscsi_opts); -#endif module_call_init(MODULE_INIT_OPTS); runstate_init(); @@ -4289,6 +4252,8 @@ int main(int argc, char **argv, char **envp) sdl_display_early_init(request_opengl); } + qemu_console_early_init(); + if (request_opengl == 1 && display_opengl == 0) { #if defined(CONFIG_OPENGL) error_report("OpenGL is not supported by the display"); @@ -4634,7 +4599,9 @@ int main(int argc, char **argv, char **envp) replay_checkpoint(CHECKPOINT_RESET); qemu_system_reset(VMRESET_SILENT); register_global_state(); - if (loadvm) { + if (replay_mode != REPLAY_MODE_NONE) { + replay_vmstate_init(); + } else if (loadvm) { if (load_vmstate(loadvm) < 0) { autostart = 0; } diff --git a/xen-common-stub.c b/xen-common-stub.c index 699c3f1c64..09fce2dd36 100644 --- a/xen-common-stub.c +++ b/xen-common-stub.c @@ -9,6 +9,6 @@ #include "qemu-common.h" #include "hw/xen/xen.h" -void xenstore_store_pv_console_info(int i, CharDriverState *chr) +void xenstore_store_pv_console_info(int i, Chardev *chr) { } diff --git a/xen-common.c b/xen-common.c index 909976071c..fd2c92847e 100644 --- a/xen-common.c +++ b/xen-common.c @@ -25,7 +25,7 @@ do { } while (0) #endif -static int store_dev_info(int domid, CharDriverState *cs, const char *string) +static int store_dev_info(int domid, Chardev *cs, const char *string) { struct xs_handle *xs = NULL; char *path = NULL; @@ -74,7 +74,7 @@ out: return ret; } -void xenstore_store_pv_console_info(int i, CharDriverState *chr) +void xenstore_store_pv_console_info(int i, Chardev *chr) { if (i == 0) { store_dev_info(xen_domid, chr, "/console"); |