[PATCH] Revert "chardev: fix backend events regression with mux chardev"

2020-06-30 Thread Szymon Lukasz
This reverts commit d09c4a47874f30820b08c39ad39bcca9b8cde084.

Seems like the problem described in the above commit is also
fixed by eeaa6715050.

Basically, the current code has the following invariant:
(d->focus == -1 && chr->be == NULL) || (d->focus >= 0 && chr->be == 
d->backends[d->focus])

So there's no need to add the code for sending events
to the focused frontend because qemu_chr_be_event()
will do that for us.

Signed-off-by: Szymon Lukasz 
---
 chardev/char-mux.c | 10 --
 chardev/char.c | 18 ++
 include/chardev/char.h |  1 -
 3 files changed, 6 insertions(+), 23 deletions(-)

diff --git a/chardev/char-mux.c b/chardev/char-mux.c
index 46c44af67c..30196d6e91 100644
--- a/chardev/char-mux.c
+++ b/chardev/char-mux.c
@@ -126,15 +126,6 @@ static void mux_chr_send_event(MuxChardev *d, int mux_nr, 
QEMUChrEvent event)
 }
 }
 
-static void mux_chr_be_event(Chardev *chr, QEMUChrEvent event)
-{
-MuxChardev *d = MUX_CHARDEV(chr);
-
-if (d->focus != -1) {
-mux_chr_send_event(d, d->focus, event);
-}
-}
-
 static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch)
 {
 if (d->term_got_escape) {
@@ -382,7 +373,6 @@ static void char_mux_class_init(ObjectClass *oc, void *data)
 cc->chr_write = mux_chr_write;
 cc->chr_accept_input = mux_chr_accept_input;
 cc->chr_add_watch = mux_chr_add_watch;
-cc->chr_be_event = mux_chr_be_event;
 cc->chr_machine_done = open_muxes;
 cc->chr_update_read_handler = mux_chr_update_read_handlers;
 }
diff --git a/chardev/char.c b/chardev/char.c
index e3051295ac..1041bcc368 100644
--- a/chardev/char.c
+++ b/chardev/char.c
@@ -50,19 +50,10 @@ static Object *get_chardevs_root(void)
 return container_get(object_get_root(), "/chardevs");
 }
 
-static void chr_be_event(Chardev *s, QEMUChrEvent event)
+void qemu_chr_be_event(Chardev *s, QEMUChrEvent event)
 {
 CharBackend *be = s->be;
 
-if (!be || !be->chr_event) {
-return;
-}
-
-be->chr_event(be->opaque, event);
-}
-
-void qemu_chr_be_event(Chardev *s, QEMUChrEvent event)
-{
 /* Keep track if the char device is open */
 switch (event) {
 case CHR_EVENT_OPENED:
@@ -78,7 +69,11 @@ void qemu_chr_be_event(Chardev *s, QEMUChrEvent event)
 break;
 }
 
-CHARDEV_GET_CLASS(s)->chr_be_event(s, event);
+if (!be || !be->chr_event) {
+return;
+}
+
+be->chr_event(be->opaque, event);
 }
 
 /* Not reporting errors from writing to logfile, as logs are
@@ -276,7 +271,6 @@ static void char_class_init(ObjectClass *oc, void *data)
 ChardevClass *cc = CHARDEV_CLASS(oc);
 
 cc->chr_write = null_chr_write;
-cc->chr_be_event = chr_be_event;
 }
 
 static void char_finalize(Object *obj)
diff --git a/include/chardev/char.h b/include/chardev/char.h
index 00589a6025..d77341605f 100644
--- a/include/chardev/char.h
+++ b/include/chardev/char.h
@@ -273,7 +273,6 @@ typedef struct ChardevClass {
 void (*chr_accept_input)(Chardev *chr);
 void (*chr_set_echo)(Chardev *chr, bool echo);
 void (*chr_set_fe_open)(Chardev *chr, int fe_open);
-void (*chr_be_event)(Chardev *s, QEMUChrEvent event);
 /* Return 0 if succeeded, 1 if failed */
 int (*chr_machine_done)(Chardev *chr);
 } ChardevClass;
-- 
2.27.0




[PATCH v3 8/9] virtio-serial-bus: add terminal resize messages

2020-06-29 Thread Szymon Lukasz
Implement the part of the virtio spec that allows to notify the virtio
driver about terminal resizes. The virtio spec contains two methods to
achieve that:

For legacy drivers, we have only one port and we put the terminal size
in the config space and inject the config changed interrupt.

For multiport devices, we use the control virtqueue to send a packet
containing the terminal size. Note that the Linux kernel expects
the fields indicating the number of rows and columns in a packet to be
in a different order than the one specified in the current version of
the virtio spec. We follow the Linux implementation, so hopefully there
is no implementation of this functionality conforming to the spec.

Signed-off-by: Szymon Lukasz 
Reviewed-by: Michael S. Tsirkin 
---
 hw/char/trace-events  |  1 +
 hw/char/virtio-serial-bus.c   | 42 +--
 hw/core/machine.c |  1 +
 include/hw/virtio/virtio-serial.h |  5 
 4 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/hw/char/trace-events b/hw/char/trace-events
index d20eafd56f..be40df47ea 100644
--- a/hw/char/trace-events
+++ b/hw/char/trace-events
@@ -10,6 +10,7 @@ serial_ioport_write(uint16_t addr, uint8_t value) "write addr 
0x%02x val 0x%02x"
 
 # virtio-serial-bus.c
 virtio_serial_send_control_event(unsigned int port, uint16_t event, uint16_t 
value) "port %u, event %u, value %u"
+virtio_serial_send_console_resize(unsigned int port, uint16_t cols, uint16_t 
rows) "port %u, cols %u, rows %u"
 virtio_serial_throttle_port(unsigned int port, bool throttle) "port %u, 
throttle %d"
 virtio_serial_handle_control_message(uint16_t event, uint16_t value) "event 
%u, value %u"
 virtio_serial_handle_control_message_port(unsigned int port) "port %u"
diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c
index 262089c0c9..6d9e94a64e 100644
--- a/hw/char/virtio-serial-bus.c
+++ b/hw/char/virtio-serial-bus.c
@@ -261,6 +261,42 @@ static size_t send_control_event(VirtIOSerial *vser, 
uint32_t port_id,
 return send_control_msg(vser, , sizeof(cpkt));
 }
 
+/*
+ * This struct should be added to the Linux kernel uapi headers
+ * and later imported to standard-headers/linux/virtio_console.h
+ */
+struct virtio_console_resize {
+__virtio16 rows;
+__virtio16 cols;
+};
+
+void virtio_serial_send_console_resize(VirtIOSerialPort *port,
+   uint16_t cols, uint16_t rows)
+{
+VirtIOSerial *vser = port->vser;
+VirtIODevice *vdev = VIRTIO_DEVICE(vser);
+
+if (virtio_vdev_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) {
+struct {
+struct virtio_console_control control;
+struct virtio_console_resize resize;
+} buffer;
+
+virtio_stl_p(vdev, , port->id);
+virtio_stw_p(vdev, , VIRTIO_CONSOLE_RESIZE);
+virtio_stw_p(vdev, , cols);
+virtio_stw_p(vdev, , rows);
+
+trace_virtio_serial_send_console_resize(port->id, cols, rows);
+send_control_msg(vser, , sizeof(buffer));
+
+} else if (virtio_vdev_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) {
+vser->port0_cols = cols;
+vser->port0_rows = rows;
+virtio_notify_config(vdev);
+}
+}
+
 /* Functions for use inside qemu to open and read from/write to ports */
 int virtio_serial_open(VirtIOSerialPort *port)
 {
@@ -572,8 +608,8 @@ static void get_config(VirtIODevice *vdev, uint8_t 
*config_data)
 struct virtio_console_config *config =
 (struct virtio_console_config *)config_data;
 
-config->cols = 0;
-config->rows = 0;
+config->cols = virtio_tswap16(vdev, vser->port0_cols);
+config->rows = virtio_tswap16(vdev, vser->port0_rows);
 config->max_nr_ports = virtio_tswap32(vdev,
   vser->serial.max_virtserial_ports);
 }
@@ -1168,6 +1204,8 @@ static Property virtio_serial_properties[] = {
   31),
 DEFINE_PROP_BIT64("emergency-write", VirtIOSerial, host_features,
   VIRTIO_CONSOLE_F_EMERG_WRITE, true),
+DEFINE_PROP_BIT64("console-size", VirtIOSerial, host_features,
+  VIRTIO_CONSOLE_F_SIZE, true),
 DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 1d80ab0e1d..c370c220f0 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -30,6 +30,7 @@
 
 GlobalProperty hw_compat_5_0[] = {
 { "virtio-balloon-device", "page-poison", "false" },
+{ "virtio-serial-device", "console-size", "off" },
 };
 const size_t hw_compat_5_0_len = G_N_ELEMENTS(hw_compat_5_0);
 
diff --git a/include/hw/virtio/virtio-serial.h 
b/include/hw/virtio/virtio-serial.h
index ed3e916b68..1d6436c0b1 100644
--- a/include/hw/virtio/virtio-serial.h
+++ 

[PATCH v3 7/9] qmp: add chardev-resize command

2020-06-29 Thread Szymon Lukasz
The managment software can use this command to notify QEMU about the
size of the terminal connected to a chardev, QEMU can then forward this
information to the guest if the chardev is connected to a virtio console
device.

Signed-off-by: Szymon Lukasz 
Suggested-by: Daniel P. Berrangé 
---
 chardev/char.c | 14 ++
 qapi/char.json | 25 +
 2 files changed, 39 insertions(+)

diff --git a/chardev/char.c b/chardev/char.c
index 1dc22aca95..c1bdfc8b5f 100644
--- a/chardev/char.c
+++ b/chardev/char.c
@@ -1182,6 +1182,20 @@ void qmp_chardev_send_break(const char *id, Error **errp)
 qemu_chr_be_event(chr, CHR_EVENT_BREAK);
 }
 
+void qmp_chardev_resize(const char *id, uint16_t cols, uint16_t rows,
+Error **errp)
+{
+Chardev *chr;
+
+chr = qemu_chr_find(id);
+if (chr == NULL) {
+error_setg(errp, "Chardev '%s' not found", id);
+return;
+}
+
+qemu_chr_resize(chr, cols, rows);
+}
+
 /*
  * Add a timeout callback for the chardev (in milliseconds), return
  * the GSource object created. Please use this to add timeout hook for
diff --git a/qapi/char.json b/qapi/char.json
index daceb20f84..dc2d6cab22 100644
--- a/qapi/char.json
+++ b/qapi/char.json
@@ -573,3 +573,28 @@
 { 'event': 'VSERPORT_CHANGE',
   'data': { 'id': 'str',
 'open': 'bool' } }
+
+##
+# @chardev-resize:
+#
+# Notifies a chardev about the current size of the terminal connected
+# to this chardev.
+#
+# @id: the chardev's ID, must exist
+# @cols: the number of columns
+# @rows: the number of rows
+#
+# Returns: Nothing on success
+#
+# Since: 5.1
+#
+# Example:
+#
+# -> { "execute": "chardev-resize", "arguments": { "id": "foo", "cols": 80, 
"rows": 24 } }
+# <- { "return": {} }
+#
+##
+{ 'command': 'chardev-resize',
+  'data': { 'id': 'str',
+'cols': 'uint16',
+'rows': 'uint16' } }
-- 
2.27.0




[PATCH v3 2/9] chardev: add CHR_EVENT_RESIZE

2020-06-29 Thread Szymon Lukasz
Add a new chardev event, CHR_EVENT_RESIZE, which a backend should
trigger if detects the size of the connected terminal changed.

Signed-off-by: Szymon Lukasz 
---
 backends/cryptodev-vhost-user.c | 1 +
 chardev/char.c  | 1 +
 hw/block/vhost-user-blk.c   | 1 +
 hw/char/terminal3270.c  | 1 +
 hw/char/virtio-console.c| 1 +
 hw/ipmi/ipmi_bmc_extern.c   | 1 +
 hw/usb/ccid-card-passthru.c | 1 +
 hw/usb/dev-serial.c | 1 +
 hw/usb/redirect.c   | 1 +
 include/chardev/char.h  | 4 
 monitor/hmp.c   | 1 +
 monitor/qmp.c   | 1 +
 net/vhost-user.c| 1 +
 13 files changed, 16 insertions(+)

diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c
index 8b8cbc4223..bbf8ad426a 100644
--- a/backends/cryptodev-vhost-user.c
+++ b/backends/cryptodev-vhost-user.c
@@ -174,6 +174,7 @@ static void cryptodev_vhost_user_event(void *opaque, 
QEMUChrEvent event)
 case CHR_EVENT_BREAK:
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/chardev/char.c b/chardev/char.c
index e3051295ac..904f8bf6e3 100644
--- a/chardev/char.c
+++ b/chardev/char.c
@@ -74,6 +74,7 @@ void qemu_chr_be_event(Chardev *s, QEMUChrEvent event)
 case CHR_EVENT_BREAK:
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
index a00b854736..1a656a27c3 100644
--- a/hw/block/vhost-user-blk.c
+++ b/hw/block/vhost-user-blk.c
@@ -403,6 +403,7 @@ static void vhost_user_blk_event(void *opaque, QEMUChrEvent 
event)
 case CHR_EVENT_BREAK:
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/hw/char/terminal3270.c b/hw/char/terminal3270.c
index 2c47ebf007..eadccbb617 100644
--- a/hw/char/terminal3270.c
+++ b/hw/char/terminal3270.c
@@ -169,6 +169,7 @@ static void chr_event(void *opaque, QEMUChrEvent event)
 case CHR_EVENT_BREAK:
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c
index 4f46753ea3..97b9240ef5 100644
--- a/hw/char/virtio-console.c
+++ b/hw/char/virtio-console.c
@@ -165,6 +165,7 @@ static void chr_event(void *opaque, QEMUChrEvent event)
 case CHR_EVENT_BREAK:
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c
index f9a13e0a44..9562584309 100644
--- a/hw/ipmi/ipmi_bmc_extern.c
+++ b/hw/ipmi/ipmi_bmc_extern.c
@@ -439,6 +439,7 @@ static void chr_event(void *opaque, QEMUChrEvent event)
 case CHR_EVENT_BREAK:
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c
index bb325dbc4a..3c26b16ed0 100644
--- a/hw/usb/ccid-card-passthru.c
+++ b/hw/usb/ccid-card-passthru.c
@@ -321,6 +321,7 @@ static void ccid_card_vscard_event(void *opaque, 
QEMUChrEvent event)
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
 case CHR_EVENT_CLOSED:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c
index 7e50e3ba47..e8e960d0e6 100644
--- a/hw/usb/dev-serial.c
+++ b/hw/usb/dev-serial.c
@@ -507,6 +507,7 @@ static void usb_serial_event(void *opaque, QEMUChrEvent 
event)
 break;
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 417a60a2e6..b716c4fdd7 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -1383,6 +1383,7 @@ static void usbredir_chardev_event(void *opaque, 
QEMUChrEvent event)
 case CHR_EVENT_BREAK:
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/include/chardev/char.h b/include/chardev/char.h
index 0cea33cb9a..42203e9fa4 100644
--- a/include/chardev/char.h
+++ b/include/chardev/char.h
@@ -22,6 +22,10 @@ typedef enum {
 CHR_EVENT_OPENED, /* new connection established */
 CHR_EVENT_MUX_IN, /* mux-focus was set to this terminal */
 CHR_EVENT_MUX_OUT, /* mux-focus will move on */
+CHR_EVENT_RESIZE, /*
+   * the size of the terminal connected to
+   * the chardev changed
+   */
 CHR_EVENT_CLOSED /* connection closed.  NOTE: currently this event
   * is only bound to the read port of the chardev

[PATCH v3 5/9] main-loop: change the handling of SIGWINCH

2020-06-29 Thread Szymon Lukasz
Block SIGWINCH, so it is delivered only via signalfd.
Install a handler that uses NotifierList to tell
interested parties about SIGWINCH delivery.

Signed-off-by: Szymon Lukasz 
---
 include/qemu/main-loop.h |  4 
 ui/curses.c  | 11 ++-
 util/main-loop.c | 21 +
 3 files changed, 31 insertions(+), 5 deletions(-)

diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h
index a6d20b0719..f27dba1fd8 100644
--- a/include/qemu/main-loop.h
+++ b/include/qemu/main-loop.h
@@ -325,4 +325,8 @@ typedef struct MainLoopPoll {
 void main_loop_poll_add_notifier(Notifier *notify);
 void main_loop_poll_remove_notifier(Notifier *notify);
 
+#ifndef _WIN32
+void sigwinch_add_notifier(Notifier *n);
+#endif
+
 #endif
diff --git a/ui/curses.c b/ui/curses.c
index a59b23a9cf..e5895d506f 100644
--- a/ui/curses.c
+++ b/ui/curses.c
@@ -34,6 +34,7 @@
 #include 
 
 #include "qapi/error.h"
+#include "qemu/main-loop.h"
 #include "qemu/module.h"
 #include "ui/console.h"
 #include "ui/input.h"
@@ -146,7 +147,7 @@ static void curses_resize(DisplayChangeListener *dcl,
 }
 
 #if !defined(_WIN32) && defined(SIGWINCH) && defined(KEY_RESIZE)
-static volatile sig_atomic_t got_sigwinch;
+static bool got_sigwinch;
 static void curses_winch_check(void)
 {
 struct winsize {
@@ -169,17 +170,17 @@ static void curses_winch_check(void)
 invalidate = 1;
 }
 
-static void curses_winch_handler(int signum)
+static void curses_winch_handler(Notifier *n, void *data)
 {
 got_sigwinch = true;
 }
 
 static void curses_winch_init(void)
 {
-struct sigaction old, winch = {
-.sa_handler  = curses_winch_handler,
+static Notifier n = {
+.notify = curses_winch_handler
 };
-sigaction(SIGWINCH, , );
+sigwinch_add_notifier();
 }
 #else
 static void curses_winch_check(void) {}
diff --git a/util/main-loop.c b/util/main-loop.c
index eda63fe4e0..0f5c8f3af1 100644
--- a/util/main-loop.c
+++ b/util/main-loop.c
@@ -90,6 +90,7 @@ static int qemu_signal_init(Error **errp)
 sigaddset(, SIGIO);
 sigaddset(, SIGALRM);
 sigaddset(, SIGBUS);
+sigaddset(, SIGWINCH);
 /* SIGINT cannot be handled via signalfd, so that ^C can be used
  * to interrupt QEMU when it is being run under gdb.  SIGHUP and
  * SIGTERM are also handled asynchronously, even though it is not
@@ -111,6 +112,26 @@ static int qemu_signal_init(Error **errp)
 return 0;
 }
 
+static NotifierList sigwinch_notifiers =
+NOTIFIER_LIST_INITIALIZER(sigwinch_notifiers);
+
+static void sigwinch_handler(int signum)
+{
+notifier_list_notify(_notifiers, NULL);
+}
+
+void sigwinch_add_notifier(Notifier *n)
+{
+if (notifier_list_empty(_notifiers)) {
+struct sigaction action = {
+.sa_handler = sigwinch_handler,
+};
+sigaction(SIGWINCH, , NULL);
+}
+
+notifier_list_add(_notifiers, n);
+}
+
 #else /* _WIN32 */
 
 static int qemu_signal_init(Error **errp)
-- 
2.27.0




[PATCH v3 9/9] virtio-console: notify the guest about terminal resizes

2020-06-29 Thread Szymon Lukasz
If a virtio serial port is a console port forward terminal resize
messages from the chardev backend to the guest.

Signed-off-by: Szymon Lukasz 
---
 hw/char/virtio-console.c | 62 +---
 1 file changed, 58 insertions(+), 4 deletions(-)

diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c
index 97b9240ef5..c4c2c7a844 100644
--- a/hw/char/virtio-console.c
+++ b/hw/char/virtio-console.c
@@ -29,6 +29,7 @@ typedef struct VirtConsole {
 
 CharBackend chr;
 guint watch;
+uint16_t cols, rows;
 } VirtConsole;
 
 /*
@@ -104,6 +105,33 @@ static ssize_t flush_buf(VirtIOSerialPort *port,
 return ret;
 }
 
+static void virtconsole_send_resize(VirtIOSerialPort *port)
+{
+uint16_t cols, rows;
+VirtConsole *vcon = VIRTIO_CONSOLE(port);
+
+/*
+ * We probably shouldn't send these messages before
+ * we told the guest it is a console port (which we do
+ * by sending VIRTIO_CONSOLE_CONSOLE_PORT message).
+ * Instead of adding a new field to the device state
+ * lets just use the guest_connected field for that purpose
+ * since the guest should not care about the terminal size
+ * before opening the port.
+ */
+if (!port->guest_connected) {
+return;
+}
+
+qemu_chr_fe_get_winsize(>chr, , );
+
+if (cols != vcon->cols || rows != vcon->rows) {
+vcon->cols = cols;
+vcon->rows = rows;
+virtio_serial_send_console_resize(port, cols, rows);
+}
+}
+
 /* Callback function that's called when the guest opens/closes the port */
 static void set_guest_connected(VirtIOSerialPort *port, int guest_connected)
 {
@@ -111,7 +139,9 @@ static void set_guest_connected(VirtIOSerialPort *port, int 
guest_connected)
 DeviceState *dev = DEVICE(port);
 VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
 
-if (!k->is_console) {
+if (k->is_console) {
+virtconsole_send_resize(port);
+} else {
 qemu_chr_fe_set_open(>chr, guest_connected);
 }
 
@@ -171,6 +201,23 @@ static void chr_event(void *opaque, QEMUChrEvent event)
 }
 }
 
+static void chr_event_console(void *opaque, QEMUChrEvent event)
+{
+VirtConsole *vcon = opaque;
+VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(vcon);
+
+trace_virtio_console_chr_event(port->id, event);
+switch (event) {
+case CHR_EVENT_OPENED:
+case CHR_EVENT_MUX_IN:
+case CHR_EVENT_RESIZE:
+virtconsole_send_resize(port);
+break;
+default:
+break;
+}
+}
+
 static int chr_be_change(void *opaque)
 {
 VirtConsole *vcon = opaque;
@@ -179,7 +226,9 @@ static int chr_be_change(void *opaque)
 
 if (k->is_console) {
 qemu_chr_fe_set_handlers(>chr, chr_can_read, chr_read,
- NULL, chr_be_change, vcon, NULL, true);
+ chr_event_console, chr_be_change,
+ vcon, NULL, true);
+virtconsole_send_resize(port);
 } else {
 qemu_chr_fe_set_handlers(>chr, chr_can_read, chr_read,
  chr_event, chr_be_change, vcon, NULL, false);
@@ -207,7 +256,7 @@ static void virtconsole_enable_backend(VirtIOSerialPort 
*port, bool enable)
 VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
 
 qemu_chr_fe_set_handlers(>chr, chr_can_read, chr_read,
- k->is_console ? NULL : chr_event,
+ k->is_console ? chr_event_console : chr_event,
  chr_be_change, vcon, NULL, false);
 } else {
 qemu_chr_fe_set_handlers(>chr, NULL, NULL, NULL,
@@ -227,6 +276,11 @@ static void virtconsole_realize(DeviceState *dev, Error 
**errp)
 return;
 }
 
+if (k->is_console) {
+vcon->cols = (uint16_t) -1;
+vcon->rows = (uint16_t) -1;
+}
+
 if (qemu_chr_fe_backend_connected(>chr)) {
 /*
  * For consoles we don't block guest data transfer just
@@ -239,7 +293,7 @@ static void virtconsole_realize(DeviceState *dev, Error 
**errp)
  */
 if (k->is_console) {
 qemu_chr_fe_set_handlers(>chr, chr_can_read, chr_read,
- NULL, chr_be_change,
+ chr_event_console, chr_be_change,
  vcon, NULL, true);
 virtio_serial_open(port);
 } else {
-- 
2.27.0




[PATCH v3 6/9] char-stdio: add support for the terminal size

2020-06-29 Thread Szymon Lukasz
Update the terminal size upon SIGWINCH delivery.

Signed-off-by: Szymon Lukasz 
---
 chardev/char-stdio.c | 29 +
 1 file changed, 29 insertions(+)

diff --git a/chardev/char-stdio.c b/chardev/char-stdio.c
index 82eaebc1db..4c3c8a80b6 100644
--- a/chardev/char-stdio.c
+++ b/chardev/char-stdio.c
@@ -34,7 +34,9 @@
 #include "chardev/char-win-stdio.h"
 #else
 #include 
+#include 
 #include "chardev/char-fd.h"
+#include "qemu/main-loop.h"
 #endif
 
 #ifndef _WIN32
@@ -45,6 +47,13 @@ static bool stdio_in_use;
 static bool stdio_allow_signal;
 static bool stdio_echo_state;
 
+typedef struct {
+FDChardev parent;
+Notifier resize_notifier;
+} StdioChardev;
+
+#define STDIO_CHARDEV(obj) OBJECT_CHECK(StdioChardev, (obj), 
TYPE_CHARDEV_STDIO)
+
 static void term_exit(void)
 {
 if (stdio_in_use) {
@@ -82,11 +91,26 @@ static void term_stdio_handler(int sig)
 qemu_chr_set_echo_stdio(NULL, stdio_echo_state);
 }
 
+static void qemu_chr_resize_stdio(Chardev *chr)
+{
+struct winsize ws;
+if (ioctl(1, TIOCGWINSZ, ) != -1) {
+qemu_chr_resize(chr, ws.ws_col, ws.ws_row);
+}
+}
+
+static void term_resize_notify(Notifier *n, void *data)
+{
+StdioChardev *s = container_of(n, StdioChardev, resize_notifier);
+qemu_chr_resize_stdio(CHARDEV(s));
+}
+
 static void qemu_chr_open_stdio(Chardev *chr,
 ChardevBackend *backend,
 bool *be_opened,
 Error **errp)
 {
+StdioChardev *s = STDIO_CHARDEV(chr);
 ChardevStdio *opts = backend->u.stdio.data;
 struct sigaction act;
 
@@ -116,6 +140,10 @@ static void qemu_chr_open_stdio(Chardev *chr,
 stdio_allow_signal = opts->signal;
 }
 qemu_chr_set_echo_stdio(chr, false);
+
+qemu_chr_resize_stdio(chr);
+s->resize_notifier.notify = term_resize_notify;
+sigwinch_add_notifier(>resize_notifier);
 }
 #endif
 
@@ -155,6 +183,7 @@ static const TypeInfo char_stdio_type_info = {
 .parent = TYPE_CHARDEV_WIN_STDIO,
 #else
 .parent = TYPE_CHARDEV_FD,
+.instance_size = sizeof(StdioChardev),
 #endif
 .instance_finalize = char_stdio_finalize,
 .class_init = char_stdio_class_init,
-- 
2.27.0




[PATCH v3 3/9] chardev: add qemu_chr_resize()

2020-06-29 Thread Szymon Lukasz
This function should be called whenever we learn about a new size of
the terminal connected to a chardev.

Signed-off-by: Szymon Lukasz 
---
 chardev/char.c | 11 +++
 include/chardev/char.h |  2 ++
 2 files changed, 13 insertions(+)

diff --git a/chardev/char.c b/chardev/char.c
index 904f8bf6e3..1dc22aca95 100644
--- a/chardev/char.c
+++ b/chardev/char.c
@@ -354,6 +354,17 @@ int qemu_chr_wait_connected(Chardev *chr, Error **errp)
 return 0;
 }
 
+void qemu_chr_resize(Chardev *chr, uint16_t cols, uint16_t rows)
+{
+if (cols != chr->cols || rows != chr->rows) {
+chr->cols = cols;
+chr->rows = rows;
+if (chr->be_open) {
+qemu_chr_be_event(chr, CHR_EVENT_RESIZE);
+}
+}
+}
+
 QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename,
 bool permit_mux_mon)
 {
diff --git a/include/chardev/char.h b/include/chardev/char.h
index 42203e9fa4..01099121f1 100644
--- a/include/chardev/char.h
+++ b/include/chardev/char.h
@@ -230,6 +230,8 @@ int qemu_chr_write(Chardev *s, const uint8_t *buf, int len, 
bool write_all);
 #define qemu_chr_write_all(s, buf, len) qemu_chr_write(s, buf, len, true)
 int qemu_chr_wait_connected(Chardev *chr, Error **errp);
 
+void qemu_chr_resize(Chardev *chr, uint16_t cols, uint16_t rows);
+
 #define TYPE_CHARDEV "chardev"
 #define CHARDEV(obj) OBJECT_CHECK(Chardev, (obj), TYPE_CHARDEV)
 #define CHARDEV_CLASS(klass) \
-- 
2.27.0




[PATCH v3 0/9] virtio-console: notify about the terminal size

2020-06-29 Thread Szymon Lukasz
The goal of this series is to have a resizable terminal into a guest
without having to set up networking and using, e.g. ssh.

The virtio spec allows a virtio-console device to notify the guest about
terminal resizes in the host. Linux Kernel implements the driver part of
the spec. This series implement the device part in QEMU.

This series adds support for a resizable terminal if a virtio console
device is connected to the stdio backend. 

This series also introduces resize messages that can be sent over QMP to
notify QEMU about the size of the terminal connented to some chardev.
In the libvirt setting, it will allow to implement a resizable terminal 
for virsh console and other libvirt clients.


v3:
add resize messages over QMP, as suggested by Daniel

v2:
fix adding a new virtio feature bit to the virtio console device

Szymon Lukasz (9):
  chardev: add cols, rows fields
  chardev: add CHR_EVENT_RESIZE
  chardev: add qemu_chr_resize()
  char-mux: add support for the terminal size
  main-loop: change the handling of SIGWINCH
  char-stdio: add support for the terminal size
  qmp: add chardev-resize command
  virtio-serial-bus: add terminal resize messages
  virtio-console: notify the guest about terminal resizes

 backends/cryptodev-vhost-user.c   |  1 +
 chardev/char-fe.c | 13 +++
 chardev/char-mux.c| 18 -
 chardev/char-stdio.c  | 29 ++
 chardev/char.c| 26 +
 hw/block/vhost-user-blk.c |  1 +
 hw/char/terminal3270.c|  1 +
 hw/char/trace-events  |  1 +
 hw/char/virtio-console.c  | 63 +--
 hw/char/virtio-serial-bus.c   | 42 -
 hw/core/machine.c |  1 +
 hw/ipmi/ipmi_bmc_extern.c |  1 +
 hw/usb/ccid-card-passthru.c   |  1 +
 hw/usb/dev-serial.c   |  1 +
 hw/usb/redirect.c |  1 +
 include/chardev/char-fe.h | 10 +
 include/chardev/char.h|  7 
 include/hw/virtio/virtio-serial.h |  5 +++
 include/qemu/main-loop.h  |  4 ++
 monitor/hmp.c |  1 +
 monitor/qmp.c |  1 +
 net/vhost-user.c  |  1 +
 qapi/char.json| 25 
 ui/curses.c   | 11 +++---
 util/main-loop.c  | 21 +++
 25 files changed, 274 insertions(+), 12 deletions(-)

-- 
2.27.0




[PATCH v3 4/9] char-mux: add support for the terminal size

2020-06-29 Thread Szymon Lukasz
The terminal size of a mux chardev should be the same as the real
chardev, so listen for CHR_EVENT_RESIZE to be up to date.

We forward CHR_EVENT_RESIZE only to the focused frontend. This means
frontends should probably update their view of the terminal size on
receiving CHR_EVENT_MUX_IN.

Signed-off-by: Szymon Lukasz 
---
 chardev/char-mux.c | 18 +-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/chardev/char-mux.c b/chardev/char-mux.c
index 46c44af67c..fa81f8341e 100644
--- a/chardev/char-mux.c
+++ b/chardev/char-mux.c
@@ -247,9 +247,24 @@ void mux_chr_send_all_event(Chardev *chr, QEMUChrEvent 
event)
 }
 }
 
+static void mux_update_winsize(Chardev *chr)
+{
+MuxChardev *d = MUX_CHARDEV(chr);
+uint16_t cols, rows;
+
+qemu_chr_fe_get_winsize(>chr, , );
+qemu_chr_resize(chr, cols, rows);
+}
+
 static void mux_chr_event(void *opaque, QEMUChrEvent event)
 {
-mux_chr_send_all_event(CHARDEV(opaque), event);
+Chardev *chr = CHARDEV(opaque);
+
+if (event == CHR_EVENT_RESIZE) {
+mux_update_winsize(chr);
+} else {
+mux_chr_send_all_event(chr, event);
+}
 }
 
 static GSource *mux_chr_add_watch(Chardev *s, GIOCondition cond)
@@ -330,6 +345,7 @@ static void qemu_chr_open_mux(Chardev *chr,
  */
 *be_opened = machine_init_done;
 qemu_chr_fe_init(>chr, drv, errp);
+mux_update_winsize(chr);
 }
 
 static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend,
-- 
2.27.0




[PATCH v3 1/9] chardev: add cols, rows fields

2020-06-29 Thread Szymon Lukasz
These fields should be interpreted as the size of the terminal connected
to a given chardev.

Signed-off-by: Szymon Lukasz 
---
 chardev/char-fe.c | 13 +
 include/chardev/char-fe.h | 10 ++
 include/chardev/char.h|  1 +
 3 files changed, 24 insertions(+)

diff --git a/chardev/char-fe.c b/chardev/char-fe.c
index f3530a90e6..27f95bfcdf 100644
--- a/chardev/char-fe.c
+++ b/chardev/char-fe.c
@@ -336,6 +336,19 @@ void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
 }
 }
 
+void qemu_chr_fe_get_winsize(CharBackend *be, uint16_t *cols, uint16_t *rows)
+{
+Chardev *chr = be->chr;
+
+if (chr) {
+*cols = chr->cols;
+*rows = chr->rows;
+} else {
+*cols = 0;
+*rows = 0;
+}
+}
+
 void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
 {
 Chardev *chr = be->chr;
diff --git a/include/chardev/char-fe.h b/include/chardev/char-fe.h
index a553843364..3672b0d97d 100644
--- a/include/chardev/char-fe.h
+++ b/include/chardev/char-fe.h
@@ -154,6 +154,16 @@ int qemu_chr_fe_wait_connected(CharBackend *be, Error 
**errp);
  */
 void qemu_chr_fe_set_echo(CharBackend *be, bool echo);
 
+/**
+ * qemu_chr_fe_get_winsize:
+ * @cols: the address for storing columns
+ * @rows: the address for storing rows
+ *
+ * Get the size of the terminal connected to the chardev backend.
+ * Returns *cols = *rows = 0, if no associated Chardev.
+ */
+void qemu_chr_fe_get_winsize(CharBackend *be, uint16_t *cols, uint16_t *rows);
+
 /**
  * qemu_chr_fe_set_open:
  *
diff --git a/include/chardev/char.h b/include/chardev/char.h
index 00589a6025..0cea33cb9a 100644
--- a/include/chardev/char.h
+++ b/include/chardev/char.h
@@ -65,6 +65,7 @@ struct Chardev {
 char *filename;
 int logfd;
 int be_open;
+uint16_t cols, rows;
 GSource *gsource;
 GMainContext *gcontext;
 DECLARE_BITMAP(features, QEMU_CHAR_FEATURE_LAST);
-- 
2.27.0




Re: [PATCH v2 0/6] virtio-console: notify about the terminal size

2020-06-25 Thread Szymon Lukasz
On Wed, Jun 24, 2020 at 12:56:15PM +0100, Daniel P. Berrangé wrote:
> On Wed, Jun 24, 2020 at 01:26:34PM +0200, Szymon Lukasz wrote:
> > In this series resize notifications are only supported for the stdio
> > backend but I think it should be easy to add support for the vc backend.
> > Support for tty/serial backends is complicated by the fact that there is
> > no clean way to detect resizes of the underlying terminal.
> 
> In a libvirt managed scenario it is typical to have the virtio console
> connected to a UNIX socket. It would be desirable to have a way to
> deal with resizes there.
> 
> QEMU socket chardev (TCP & UNIX socket) supports a "telnet" protocol
> addition. Currently it doesn't almost nothing useful, but in theory
> we could wire up support for the telnet resize message:
> 
>https://tools.ietf.org/html/rfc1073
> 
> 
> Another option is to allow dealing with resizes out of band, via the
> QMP monitor. ie we can introduce a qmp_chardev_winsize command that
> a client app can use to trigger the resize message to the guest OS.
> From libvirt's POV, this would be quite easy to support & useful to
> have.

I will look into that.
> 
> 
> Regards,
> Daniel
> -- 
> |: https://berrange.com  -o-https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org -o-https://fstop138.berrange.com :|
> |: https://entangle-photo.org-o-https://www.instagram.com/dberrange :|
> 



Re: [PATCH v2 0/6] virtio-console: notify about the terminal size

2020-06-25 Thread Szymon Lukasz
On Wed, Jun 24, 2020 at 12:49:15PM +0100, Daniel P. Berrangé wrote:
> On Wed, Jun 24, 2020 at 01:26:34PM +0200, Szymon Lukasz wrote:
> > Also there is a problem with the virtio spec and Linux Kernel
> > implementation, the order of fields in virtio_console_resize struct
> > differs between the kernel and the spec. I do not know if there is any
> > implementation of the virtio-console driver that handles resize messages
> > and uses a different order than Linux.
> 
> Well this is a bit of a mess :-(
> 
> The main virtio_console_config struct has cols, then rows.
> 
> The Linux impl of resizing appears to have arrived in 2010, and created
> a new struct with rows, then cols.
> 
> commit 8345adbf96fc1bde7d9846aadbe5af9b2ae90882
> Author: Amit Shah 
> Date:   Thu May 6 02:05:09 2010 +0530
> 
> virtio: console: Accept console size along with resize control message
> 
> The VIRTIO_CONSOLE_RESIZE control message sent to us by the host now
> contains the new {rows, cols} values for the console. This ensures each
> console port gets its own size, and we don't depend on the config-space
> rows and cols values at all now.
> 
> Signed-off-by: Amit Shah 
> CC: Christian Borntraeger 
> CC: linuxppc-...@ozlabs.org
> CC: Kusanagi Kouichi 
> Signed-off-by: Rusty Russell 
> 
> 
> The virtio spec documenting this came 4 years later in 2014 and documented
> the resize struct with cols, then rows, which differs from Linux impl,
> but matches ordering of the main virtio_console_config:
> 
> commit 908cfaa782e950d6656d947599d7a6c9fb16cad1
> Author: rusty 
> Date:   Wed Feb 12 03:15:57 2014 +
> 
> Feedback #6: Applied
> 
> As per minutes:
> https://lists.oasis-open.org/archives/virtio/201402/msg00121.html
> 
> Signed-off-by: Rusty Russell 
> 
> git-svn-id: https://tools.oasis-open.org/version-control/svn/virtio@237 
> 0c8fb4dd-22a2-4bb5-bc14-6c75a5f43652
> 
> I can understand why it is desirable for the resize struct to match
> the order of the initial config struct.  I'm guessing it just wasn't
> realized that the Linux impl was inverted for resize
> 
> The FreeBSD impl of virtio-console doesn't do resize:
> 
>   
> https://github.com/freebsd/freebsd/blob/master/sys/dev/virtio/console/virtio_console.c#L874
> 
> Not sure what other impls are going to be around, but I feel like
> Linux is going to be the most commonly deployed by orders of magnitude.
> 
> So I'd say QEMU should match Linux, and the spec should be fixed.

I had the same thoughts. I will ask for a change in the spec.
> 
> 
> Have you reported this bug to the virtio spec people directly yet ?
> 
> I don't see an issue open at
> 
>   https://github.com/oasis-tcs/virtio-spec/issues/
> 
> so I think one should be filed there
> 
> Regards,
> Daniel
> -- 
> |: https://berrange.com  -o-https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org -o-https://fstop138.berrange.com :|
> |: https://entangle-photo.org-o-https://www.instagram.com/dberrange :|
> 



[PATCH v2 5/6] virtio-serial-bus: add terminal resize messages

2020-06-24 Thread Szymon Lukasz
Implement the part of the virtio spec that allows to notify the virtio
driver about terminal resizes. The virtio spec contains two methods to
achieve that:

For legacy drivers, we have only one port and we put the terminal size
in the config space and inject the config changed interrupt.

For multiport devices, we use the control virtqueue to send a packet
containing the terminal size. Note that the Linux kernel expects
the fields indicating the number of rows and columns in a packet to be
in a different order than the one specified in the current version of
the virtio spec. We follow the Linux implementation, so hopefully there
is no implementation of this functionality conforming to the spec.

Signed-off-by: Szymon Lukasz 
---
 hw/char/trace-events  |  1 +
 hw/char/virtio-serial-bus.c   | 42 +--
 hw/core/machine.c |  1 +
 include/hw/virtio/virtio-serial.h |  5 
 4 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/hw/char/trace-events b/hw/char/trace-events
index d20eafd56f..be40df47ea 100644
--- a/hw/char/trace-events
+++ b/hw/char/trace-events
@@ -10,6 +10,7 @@ serial_ioport_write(uint16_t addr, uint8_t value) "write addr 
0x%02x val 0x%02x"
 
 # virtio-serial-bus.c
 virtio_serial_send_control_event(unsigned int port, uint16_t event, uint16_t 
value) "port %u, event %u, value %u"
+virtio_serial_send_console_resize(unsigned int port, uint16_t cols, uint16_t 
rows) "port %u, cols %u, rows %u"
 virtio_serial_throttle_port(unsigned int port, bool throttle) "port %u, 
throttle %d"
 virtio_serial_handle_control_message(uint16_t event, uint16_t value) "event 
%u, value %u"
 virtio_serial_handle_control_message_port(unsigned int port) "port %u"
diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c
index 262089c0c9..6d9e94a64e 100644
--- a/hw/char/virtio-serial-bus.c
+++ b/hw/char/virtio-serial-bus.c
@@ -261,6 +261,42 @@ static size_t send_control_event(VirtIOSerial *vser, 
uint32_t port_id,
 return send_control_msg(vser, , sizeof(cpkt));
 }
 
+/*
+ * This struct should be added to the Linux kernel uapi headers
+ * and later imported to standard-headers/linux/virtio_console.h
+ */
+struct virtio_console_resize {
+__virtio16 rows;
+__virtio16 cols;
+};
+
+void virtio_serial_send_console_resize(VirtIOSerialPort *port,
+   uint16_t cols, uint16_t rows)
+{
+VirtIOSerial *vser = port->vser;
+VirtIODevice *vdev = VIRTIO_DEVICE(vser);
+
+if (virtio_vdev_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) {
+struct {
+struct virtio_console_control control;
+struct virtio_console_resize resize;
+} buffer;
+
+virtio_stl_p(vdev, , port->id);
+virtio_stw_p(vdev, , VIRTIO_CONSOLE_RESIZE);
+virtio_stw_p(vdev, , cols);
+virtio_stw_p(vdev, , rows);
+
+trace_virtio_serial_send_console_resize(port->id, cols, rows);
+send_control_msg(vser, , sizeof(buffer));
+
+} else if (virtio_vdev_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) {
+vser->port0_cols = cols;
+vser->port0_rows = rows;
+virtio_notify_config(vdev);
+}
+}
+
 /* Functions for use inside qemu to open and read from/write to ports */
 int virtio_serial_open(VirtIOSerialPort *port)
 {
@@ -572,8 +608,8 @@ static void get_config(VirtIODevice *vdev, uint8_t 
*config_data)
 struct virtio_console_config *config =
 (struct virtio_console_config *)config_data;
 
-config->cols = 0;
-config->rows = 0;
+config->cols = virtio_tswap16(vdev, vser->port0_cols);
+config->rows = virtio_tswap16(vdev, vser->port0_rows);
 config->max_nr_ports = virtio_tswap32(vdev,
   vser->serial.max_virtserial_ports);
 }
@@ -1168,6 +1204,8 @@ static Property virtio_serial_properties[] = {
   31),
 DEFINE_PROP_BIT64("emergency-write", VirtIOSerial, host_features,
   VIRTIO_CONSOLE_F_EMERG_WRITE, true),
+DEFINE_PROP_BIT64("console-size", VirtIOSerial, host_features,
+  VIRTIO_CONSOLE_F_SIZE, true),
 DEFINE_PROP_END_OF_LIST(),
 };
 
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 1d80ab0e1d..c370c220f0 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -30,6 +30,7 @@
 
 GlobalProperty hw_compat_5_0[] = {
 { "virtio-balloon-device", "page-poison", "false" },
+{ "virtio-serial-device", "console-size", "off" },
 };
 const size_t hw_compat_5_0_len = G_N_ELEMENTS(hw_compat_5_0);
 
diff --git a/include/hw/virtio/virtio-serial.h 
b/include/hw/virtio/virtio-serial.h
index ed3e916b68..1d6436c0b1 100644
--- a/include/hw/virtio/virtio-serial.h
+++ b/include/hw/virtio/virtio-serial.h
@@ -188,6 

[PATCH v2 6/6] virtio-console: notify the guest about terminal resizes

2020-06-24 Thread Szymon Lukasz
If a virtio serial port is a console port forward terminal resize
messages from the chardev backend to the guest.

Signed-off-by: Szymon Lukasz 
---
 hw/char/virtio-console.c | 64 +---
 1 file changed, 60 insertions(+), 4 deletions(-)

diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c
index 97b9240ef5..1ea06aad08 100644
--- a/hw/char/virtio-console.c
+++ b/hw/char/virtio-console.c
@@ -29,6 +29,7 @@ typedef struct VirtConsole {
 
 CharBackend chr;
 guint watch;
+uint16_t cols, rows;
 } VirtConsole;
 
 /*
@@ -104,6 +105,36 @@ static ssize_t flush_buf(VirtIOSerialPort *port,
 return ret;
 }
 
+static void virtconsole_send_resize(VirtIOSerialPort *port)
+{
+uint16_t cols, rows;
+VirtConsole *vcon = VIRTIO_CONSOLE(port);
+
+/*
+ * We probably shouldn't send these messages before
+ * we told the guest it is a console port (which we do
+ * by sending VIRTIO_CONSOLE_CONSOLE_PORT message).
+ * Instead of adding a new field to the device state
+ * lets just use the guest_connected field for that purpose
+ * since the guest should not care about the terminal size
+ * before opening the port.
+ */
+if (!port->guest_connected) {
+return;
+}
+
+if (qemu_chr_fe_get_winsize(>chr, , ) < 0) {
+cols = 0;
+rows = 0;
+}
+
+if (cols != vcon->cols || rows != vcon->rows) {
+vcon->cols = cols;
+vcon->rows = rows;
+virtio_serial_send_console_resize(port, cols, rows);
+}
+}
+
 /* Callback function that's called when the guest opens/closes the port */
 static void set_guest_connected(VirtIOSerialPort *port, int guest_connected)
 {
@@ -111,7 +142,9 @@ static void set_guest_connected(VirtIOSerialPort *port, int 
guest_connected)
 DeviceState *dev = DEVICE(port);
 VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
 
-if (!k->is_console) {
+if (k->is_console) {
+virtconsole_send_resize(port);
+} else {
 qemu_chr_fe_set_open(>chr, guest_connected);
 }
 
@@ -171,6 +204,22 @@ static void chr_event(void *opaque, QEMUChrEvent event)
 }
 }
 
+static void chr_event_console(void *opaque, QEMUChrEvent event)
+{
+VirtConsole *vcon = opaque;
+VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(vcon);
+
+switch (event) {
+case CHR_EVENT_OPENED:
+case CHR_EVENT_RESIZE:
+trace_virtio_console_chr_event(port->id, event);
+virtconsole_send_resize(port);
+break;
+default:
+break;
+}
+}
+
 static int chr_be_change(void *opaque)
 {
 VirtConsole *vcon = opaque;
@@ -179,7 +228,9 @@ static int chr_be_change(void *opaque)
 
 if (k->is_console) {
 qemu_chr_fe_set_handlers(>chr, chr_can_read, chr_read,
- NULL, chr_be_change, vcon, NULL, true);
+ chr_event_console, chr_be_change,
+ vcon, NULL, true);
+virtconsole_send_resize(port);
 } else {
 qemu_chr_fe_set_handlers(>chr, chr_can_read, chr_read,
  chr_event, chr_be_change, vcon, NULL, false);
@@ -207,7 +258,7 @@ static void virtconsole_enable_backend(VirtIOSerialPort 
*port, bool enable)
 VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
 
 qemu_chr_fe_set_handlers(>chr, chr_can_read, chr_read,
- k->is_console ? NULL : chr_event,
+ k->is_console ? chr_event_console : chr_event,
  chr_be_change, vcon, NULL, false);
 } else {
 qemu_chr_fe_set_handlers(>chr, NULL, NULL, NULL,
@@ -227,6 +278,11 @@ static void virtconsole_realize(DeviceState *dev, Error 
**errp)
 return;
 }
 
+if (k->is_console) {
+vcon->cols = (uint16_t) -1;
+vcon->rows = (uint16_t) -1;
+}
+
 if (qemu_chr_fe_backend_connected(>chr)) {
 /*
  * For consoles we don't block guest data transfer just
@@ -239,7 +295,7 @@ static void virtconsole_realize(DeviceState *dev, Error 
**errp)
  */
 if (k->is_console) {
 qemu_chr_fe_set_handlers(>chr, chr_can_read, chr_read,
- NULL, chr_be_change,
+ chr_event_console, chr_be_change,
  vcon, NULL, true);
 virtio_serial_open(port);
 } else {
-- 
2.27.0




[PATCH v2 3/6] chardev: add support for notifying about terminal resizes

2020-06-24 Thread Szymon Lukasz
Add a new chardev event, CHR_EVENT_RESIZE, which a backend should
trigger if detects the size of the connected terminal changed.

Signed-off-by: Szymon Lukasz 
---
 backends/cryptodev-vhost-user.c | 1 +
 chardev/char.c  | 1 +
 hw/block/vhost-user-blk.c   | 1 +
 hw/char/terminal3270.c  | 1 +
 hw/char/virtio-console.c| 1 +
 hw/ipmi/ipmi_bmc_extern.c   | 1 +
 hw/usb/ccid-card-passthru.c | 1 +
 hw/usb/dev-serial.c | 1 +
 hw/usb/redirect.c   | 1 +
 include/chardev/char.h  | 1 +
 monitor/hmp.c   | 1 +
 monitor/qmp.c   | 1 +
 net/vhost-user.c| 1 +
 13 files changed, 13 insertions(+)

diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c
index 8b8cbc4223..bbf8ad426a 100644
--- a/backends/cryptodev-vhost-user.c
+++ b/backends/cryptodev-vhost-user.c
@@ -174,6 +174,7 @@ static void cryptodev_vhost_user_event(void *opaque, 
QEMUChrEvent event)
 case CHR_EVENT_BREAK:
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/chardev/char.c b/chardev/char.c
index e3051295ac..904f8bf6e3 100644
--- a/chardev/char.c
+++ b/chardev/char.c
@@ -74,6 +74,7 @@ void qemu_chr_be_event(Chardev *s, QEMUChrEvent event)
 case CHR_EVENT_BREAK:
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
index a00b854736..1a656a27c3 100644
--- a/hw/block/vhost-user-blk.c
+++ b/hw/block/vhost-user-blk.c
@@ -403,6 +403,7 @@ static void vhost_user_blk_event(void *opaque, QEMUChrEvent 
event)
 case CHR_EVENT_BREAK:
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/hw/char/terminal3270.c b/hw/char/terminal3270.c
index 2c47ebf007..eadccbb617 100644
--- a/hw/char/terminal3270.c
+++ b/hw/char/terminal3270.c
@@ -169,6 +169,7 @@ static void chr_event(void *opaque, QEMUChrEvent event)
 case CHR_EVENT_BREAK:
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c
index 4f46753ea3..97b9240ef5 100644
--- a/hw/char/virtio-console.c
+++ b/hw/char/virtio-console.c
@@ -165,6 +165,7 @@ static void chr_event(void *opaque, QEMUChrEvent event)
 case CHR_EVENT_BREAK:
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c
index f9a13e0a44..9562584309 100644
--- a/hw/ipmi/ipmi_bmc_extern.c
+++ b/hw/ipmi/ipmi_bmc_extern.c
@@ -439,6 +439,7 @@ static void chr_event(void *opaque, QEMUChrEvent event)
 case CHR_EVENT_BREAK:
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c
index bb325dbc4a..3c26b16ed0 100644
--- a/hw/usb/ccid-card-passthru.c
+++ b/hw/usb/ccid-card-passthru.c
@@ -321,6 +321,7 @@ static void ccid_card_vscard_event(void *opaque, 
QEMUChrEvent event)
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
 case CHR_EVENT_CLOSED:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c
index 7e50e3ba47..e8e960d0e6 100644
--- a/hw/usb/dev-serial.c
+++ b/hw/usb/dev-serial.c
@@ -507,6 +507,7 @@ static void usb_serial_event(void *opaque, QEMUChrEvent 
event)
 break;
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 417a60a2e6..b716c4fdd7 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -1383,6 +1383,7 @@ static void usbredir_chardev_event(void *opaque, 
QEMUChrEvent event)
 case CHR_EVENT_BREAK:
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/include/chardev/char.h b/include/chardev/char.h
index fb20707917..c3d108ce82 100644
--- a/include/chardev/char.h
+++ b/include/chardev/char.h
@@ -22,6 +22,7 @@ typedef enum {
 CHR_EVENT_OPENED, /* new connection established */
 CHR_EVENT_MUX_IN, /* mux-focus was set to this terminal */
 CHR_EVENT_MUX_OUT, /* mux-focus will move on */
+CHR_EVENT_RESIZE, /* the terminal size of the chardev changed */
 CHR_EVENT_CLOSED /* connection closed.  NOTE: currently this event
   * is only bound to the read port of the chardev.
   * Normally the read port and write port of a
diff --git a/monitor/hmp.c b/monitor/hmp.c
index

[PATCH v2 1/6] main-loop: change the handling of SIGWINCH

2020-06-24 Thread Szymon Lukasz
Block SIGWINCH, so it is delivered only via signalfd.
Install a handler that uses NotifierList to tell
interested parties about SIGWINCH delivery.

Signed-off-by: Szymon Lukasz 
---
 include/qemu/main-loop.h |  4 
 ui/curses.c  | 11 ++-
 util/main-loop.c | 21 +
 3 files changed, 31 insertions(+), 5 deletions(-)

diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h
index a6d20b0719..f27dba1fd8 100644
--- a/include/qemu/main-loop.h
+++ b/include/qemu/main-loop.h
@@ -325,4 +325,8 @@ typedef struct MainLoopPoll {
 void main_loop_poll_add_notifier(Notifier *notify);
 void main_loop_poll_remove_notifier(Notifier *notify);
 
+#ifndef _WIN32
+void sigwinch_add_notifier(Notifier *n);
+#endif
+
 #endif
diff --git a/ui/curses.c b/ui/curses.c
index a59b23a9cf..e5895d506f 100644
--- a/ui/curses.c
+++ b/ui/curses.c
@@ -34,6 +34,7 @@
 #include 
 
 #include "qapi/error.h"
+#include "qemu/main-loop.h"
 #include "qemu/module.h"
 #include "ui/console.h"
 #include "ui/input.h"
@@ -146,7 +147,7 @@ static void curses_resize(DisplayChangeListener *dcl,
 }
 
 #if !defined(_WIN32) && defined(SIGWINCH) && defined(KEY_RESIZE)
-static volatile sig_atomic_t got_sigwinch;
+static bool got_sigwinch;
 static void curses_winch_check(void)
 {
 struct winsize {
@@ -169,17 +170,17 @@ static void curses_winch_check(void)
 invalidate = 1;
 }
 
-static void curses_winch_handler(int signum)
+static void curses_winch_handler(Notifier *n, void *data)
 {
 got_sigwinch = true;
 }
 
 static void curses_winch_init(void)
 {
-struct sigaction old, winch = {
-.sa_handler  = curses_winch_handler,
+static Notifier n = {
+.notify = curses_winch_handler
 };
-sigaction(SIGWINCH, , );
+sigwinch_add_notifier();
 }
 #else
 static void curses_winch_check(void) {}
diff --git a/util/main-loop.c b/util/main-loop.c
index eda63fe4e0..0f5c8f3af1 100644
--- a/util/main-loop.c
+++ b/util/main-loop.c
@@ -90,6 +90,7 @@ static int qemu_signal_init(Error **errp)
 sigaddset(, SIGIO);
 sigaddset(, SIGALRM);
 sigaddset(, SIGBUS);
+sigaddset(, SIGWINCH);
 /* SIGINT cannot be handled via signalfd, so that ^C can be used
  * to interrupt QEMU when it is being run under gdb.  SIGHUP and
  * SIGTERM are also handled asynchronously, even though it is not
@@ -111,6 +112,26 @@ static int qemu_signal_init(Error **errp)
 return 0;
 }
 
+static NotifierList sigwinch_notifiers =
+NOTIFIER_LIST_INITIALIZER(sigwinch_notifiers);
+
+static void sigwinch_handler(int signum)
+{
+notifier_list_notify(_notifiers, NULL);
+}
+
+void sigwinch_add_notifier(Notifier *n)
+{
+if (notifier_list_empty(_notifiers)) {
+struct sigaction action = {
+.sa_handler = sigwinch_handler,
+};
+sigaction(SIGWINCH, , NULL);
+}
+
+notifier_list_add(_notifiers, n);
+}
+
 #else /* _WIN32 */
 
 static int qemu_signal_init(Error **errp)
-- 
2.27.0




[PATCH v2 4/6] char-stdio: add support for the terminal size

2020-06-24 Thread Szymon Lukasz
Implement chr_get_winsize for the stdio backend
and trigger CHR_EVENT_RESIZE upon SIGWINCH delivery.

Signed-off-by: Szymon Lukasz 
---
 chardev/char-stdio.c | 35 +++
 1 file changed, 35 insertions(+)

diff --git a/chardev/char-stdio.c b/chardev/char-stdio.c
index 82eaebc1db..1edc82fc6e 100644
--- a/chardev/char-stdio.c
+++ b/chardev/char-stdio.c
@@ -34,7 +34,9 @@
 #include "chardev/char-win-stdio.h"
 #else
 #include 
+#include 
 #include "chardev/char-fd.h"
+#include "qemu/main-loop.h"
 #endif
 
 #ifndef _WIN32
@@ -45,6 +47,13 @@ static bool stdio_in_use;
 static bool stdio_allow_signal;
 static bool stdio_echo_state;
 
+typedef struct {
+FDChardev parent;
+Notifier resize_notifier;
+} StdioChardev;
+
+#define STDIO_CHARDEV(obj) OBJECT_CHECK(StdioChardev, (obj), 
TYPE_CHARDEV_STDIO)
+
 static void term_exit(void)
 {
 if (stdio_in_use) {
@@ -82,11 +91,32 @@ static void term_stdio_handler(int sig)
 qemu_chr_set_echo_stdio(NULL, stdio_echo_state);
 }
 
+static int qemu_chr_get_winsize_stdio(Chardev *chr, uint16_t *cols,
+  uint16_t *rows)
+{
+struct winsize ws;
+
+if (ioctl(1, TIOCGWINSZ, ) < 0) {
+return -1;
+}
+
+*cols = ws.ws_col;
+*rows = ws.ws_row;
+return 0;
+}
+
+static void term_resize_notify(Notifier *n, void *data)
+{
+StdioChardev *s = container_of(n, StdioChardev, resize_notifier);
+qemu_chr_be_event(CHARDEV(s), CHR_EVENT_RESIZE);
+}
+
 static void qemu_chr_open_stdio(Chardev *chr,
 ChardevBackend *backend,
 bool *be_opened,
 Error **errp)
 {
+StdioChardev *s = STDIO_CHARDEV(chr);
 ChardevStdio *opts = backend->u.stdio.data;
 struct sigaction act;
 
@@ -116,6 +146,9 @@ static void qemu_chr_open_stdio(Chardev *chr,
 stdio_allow_signal = opts->signal;
 }
 qemu_chr_set_echo_stdio(chr, false);
+
+s->resize_notifier.notify = term_resize_notify;
+sigwinch_add_notifier(>resize_notifier);
 }
 #endif
 
@@ -139,6 +172,7 @@ static void char_stdio_class_init(ObjectClass *oc, void 
*data)
 #ifndef _WIN32
 cc->open = qemu_chr_open_stdio;
 cc->chr_set_echo = qemu_chr_set_echo_stdio;
+cc->chr_get_winsize = qemu_chr_get_winsize_stdio;
 #endif
 }
 
@@ -155,6 +189,7 @@ static const TypeInfo char_stdio_type_info = {
 .parent = TYPE_CHARDEV_WIN_STDIO,
 #else
 .parent = TYPE_CHARDEV_FD,
+.instance_size = sizeof(StdioChardev),
 #endif
 .instance_finalize = char_stdio_finalize,
 .class_init = char_stdio_class_init,
-- 
2.27.0




[PATCH v2 0/6] virtio-console: notify about the terminal size

2020-06-24 Thread Szymon Lukasz
The goal of this series is to have a resizable terminal into a guest
without having to set up networking and using, e.g. ssh.

The virtio spec allows a virtio-console device to notify the guest about
terminal resizes in the host. Linux Kernel implements the driver part of
the spec. This series implement the device part in QEMU.

In this series resize notifications are only supported for the stdio
backend but I think it should be easy to add support for the vc backend.
Support for tty/serial backends is complicated by the fact that there is
no clean way to detect resizes of the underlying terminal.

Also there is a problem with the virtio spec and Linux Kernel
implementation, the order of fields in virtio_console_resize struct
differs between the kernel and the spec. I do not know if there is any
implementation of the virtio-console driver that handles resize messages
and uses a different order than Linux.


v2:
fix adding a new virtio feature bit to the virtio console device


Szymon Lukasz (6):
  main-loop: change the handling of SIGWINCH
  chardev: add support for retrieving the terminal size
  chardev: add support for notifying about terminal resizes
  char-stdio: add support for the terminal size
  virtio-serial-bus: add terminal resize messages
  virtio-console: notify the guest about terminal resizes

 backends/cryptodev-vhost-user.c   |  1 +
 chardev/char-fe.c | 11 ++
 chardev/char-mux.c|  7 
 chardev/char-stdio.c  | 35 +
 chardev/char.c|  1 +
 hw/block/vhost-user-blk.c |  1 +
 hw/char/terminal3270.c|  1 +
 hw/char/trace-events  |  1 +
 hw/char/virtio-console.c  | 65 +--
 hw/char/virtio-serial-bus.c   | 42 +++-
 hw/core/machine.c |  1 +
 hw/ipmi/ipmi_bmc_extern.c |  1 +
 hw/usb/ccid-card-passthru.c   |  1 +
 hw/usb/dev-serial.c   |  1 +
 hw/usb/redirect.c |  1 +
 include/chardev/char-fe.h | 11 ++
 include/chardev/char.h|  2 +
 include/hw/virtio/virtio-serial.h |  5 +++
 include/qemu/main-loop.h  |  4 ++
 monitor/hmp.c |  1 +
 monitor/qmp.c |  1 +
 net/vhost-user.c  |  1 +
 ui/curses.c   | 11 +++---
 util/main-loop.c  | 21 ++
 24 files changed, 216 insertions(+), 11 deletions(-)

-- 
2.27.0




[PATCH v2 2/6] chardev: add support for retrieving the terminal size

2020-06-24 Thread Szymon Lukasz
Extend the class of chardevs with a new function - chr_get_winsize.
A chardev backend should implement if it is able to get the size of
the connected terminal and can detect changes in the terminal size,
i.e. if the backend cannot detect resizes it must not implement this
(e.g. if we have a tty backend connected to some (pseudo)terminal
there is no clean way to detect resizes since SIGWINCH is sent only
for the controlling terminal).

Signed-off-by: Szymon Lukasz 
---
 chardev/char-fe.c | 11 +++
 chardev/char-mux.c|  7 +++
 include/chardev/char-fe.h | 11 +++
 include/chardev/char.h|  1 +
 4 files changed, 30 insertions(+)

diff --git a/chardev/char-fe.c b/chardev/char-fe.c
index f3530a90e6..802d3096cd 100644
--- a/chardev/char-fe.c
+++ b/chardev/char-fe.c
@@ -336,6 +336,17 @@ void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
 }
 }
 
+int qemu_chr_fe_get_winsize(CharBackend *be, uint16_t *cols, uint16_t *rows)
+{
+Chardev *chr = be->chr;
+
+if (chr && CHARDEV_GET_CLASS(chr)->chr_get_winsize) {
+return CHARDEV_GET_CLASS(chr)->chr_get_winsize(chr, cols, rows);
+}
+
+return -1;
+}
+
 void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
 {
 Chardev *chr = be->chr;
diff --git a/chardev/char-mux.c b/chardev/char-mux.c
index 46c44af67c..368ce2334e 100644
--- a/chardev/char-mux.c
+++ b/chardev/char-mux.c
@@ -293,6 +293,12 @@ static void mux_chr_update_read_handlers(Chardev *chr)
   chr->gcontext, true, false);
 }
 
+static int mux_chr_get_winsize(Chardev *chr, uint16_t *cols, uint16_t *rows)
+{
+MuxChardev *d = MUX_CHARDEV(chr);
+return qemu_chr_fe_get_winsize(>chr, cols, rows);
+}
+
 void mux_set_focus(Chardev *chr, int focus)
 {
 MuxChardev *d = MUX_CHARDEV(chr);
@@ -385,6 +391,7 @@ static void char_mux_class_init(ObjectClass *oc, void *data)
 cc->chr_be_event = mux_chr_be_event;
 cc->chr_machine_done = open_muxes;
 cc->chr_update_read_handler = mux_chr_update_read_handlers;
+cc->chr_get_winsize = mux_chr_get_winsize;
 }
 
 static const TypeInfo char_mux_type_info = {
diff --git a/include/chardev/char-fe.h b/include/chardev/char-fe.h
index a553843364..b7943df93a 100644
--- a/include/chardev/char-fe.h
+++ b/include/chardev/char-fe.h
@@ -154,6 +154,17 @@ int qemu_chr_fe_wait_connected(CharBackend *be, Error 
**errp);
  */
 void qemu_chr_fe_set_echo(CharBackend *be, bool echo);
 
+/**
+ * qemu_chr_fe_get_winsize:
+ * @cols: the address for storing columns
+ * @rows: the address for storing rows
+ *
+ * Get the terminal size of the backend.
+ *
+ * Returns: 0 on success and < 0 on error
+ */
+int qemu_chr_fe_get_winsize(CharBackend *be, uint16_t *cols, uint16_t *rows);
+
 /**
  * qemu_chr_fe_set_open:
  *
diff --git a/include/chardev/char.h b/include/chardev/char.h
index 00589a6025..fb20707917 100644
--- a/include/chardev/char.h
+++ b/include/chardev/char.h
@@ -276,6 +276,7 @@ typedef struct ChardevClass {
 void (*chr_be_event)(Chardev *s, QEMUChrEvent event);
 /* Return 0 if succeeded, 1 if failed */
 int (*chr_machine_done)(Chardev *chr);
+int (*chr_get_winsize)(Chardev *chr, uint16_t *cols, uint16_t *rows);
 } ChardevClass;
 
 Chardev *qemu_chardev_new(const char *id, const char *typename,
-- 
2.27.0




[PATCH 6/6] virtio-console: notify the guest about terminal resizes

2020-06-21 Thread Szymon Lukasz
If a virtio serial port is a console port forward terminal resize
messages from the chardev backend to the guest.

Signed-off-by: Szymon Lukasz 
---
 hw/char/virtio-console.c | 64 +---
 1 file changed, 60 insertions(+), 4 deletions(-)

diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c
index 97b9240ef5..1ea06aad08 100644
--- a/hw/char/virtio-console.c
+++ b/hw/char/virtio-console.c
@@ -29,6 +29,7 @@ typedef struct VirtConsole {
 
 CharBackend chr;
 guint watch;
+uint16_t cols, rows;
 } VirtConsole;
 
 /*
@@ -104,6 +105,36 @@ static ssize_t flush_buf(VirtIOSerialPort *port,
 return ret;
 }
 
+static void virtconsole_send_resize(VirtIOSerialPort *port)
+{
+uint16_t cols, rows;
+VirtConsole *vcon = VIRTIO_CONSOLE(port);
+
+/*
+ * We probably shouldn't send these messages before
+ * we told the guest it is a console port (which we do
+ * by sending VIRTIO_CONSOLE_CONSOLE_PORT message).
+ * Instead of adding a new field to the device state
+ * lets just use the guest_connected field for that purpose
+ * since the guest should not care about the terminal size
+ * before opening the port.
+ */
+if (!port->guest_connected) {
+return;
+}
+
+if (qemu_chr_fe_get_winsize(>chr, , ) < 0) {
+cols = 0;
+rows = 0;
+}
+
+if (cols != vcon->cols || rows != vcon->rows) {
+vcon->cols = cols;
+vcon->rows = rows;
+virtio_serial_send_console_resize(port, cols, rows);
+}
+}
+
 /* Callback function that's called when the guest opens/closes the port */
 static void set_guest_connected(VirtIOSerialPort *port, int guest_connected)
 {
@@ -111,7 +142,9 @@ static void set_guest_connected(VirtIOSerialPort *port, int 
guest_connected)
 DeviceState *dev = DEVICE(port);
 VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
 
-if (!k->is_console) {
+if (k->is_console) {
+virtconsole_send_resize(port);
+} else {
 qemu_chr_fe_set_open(>chr, guest_connected);
 }
 
@@ -171,6 +204,22 @@ static void chr_event(void *opaque, QEMUChrEvent event)
 }
 }
 
+static void chr_event_console(void *opaque, QEMUChrEvent event)
+{
+VirtConsole *vcon = opaque;
+VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(vcon);
+
+switch (event) {
+case CHR_EVENT_OPENED:
+case CHR_EVENT_RESIZE:
+trace_virtio_console_chr_event(port->id, event);
+virtconsole_send_resize(port);
+break;
+default:
+break;
+}
+}
+
 static int chr_be_change(void *opaque)
 {
 VirtConsole *vcon = opaque;
@@ -179,7 +228,9 @@ static int chr_be_change(void *opaque)
 
 if (k->is_console) {
 qemu_chr_fe_set_handlers(>chr, chr_can_read, chr_read,
- NULL, chr_be_change, vcon, NULL, true);
+ chr_event_console, chr_be_change,
+ vcon, NULL, true);
+virtconsole_send_resize(port);
 } else {
 qemu_chr_fe_set_handlers(>chr, chr_can_read, chr_read,
  chr_event, chr_be_change, vcon, NULL, false);
@@ -207,7 +258,7 @@ static void virtconsole_enable_backend(VirtIOSerialPort 
*port, bool enable)
 VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_GET_CLASS(port);
 
 qemu_chr_fe_set_handlers(>chr, chr_can_read, chr_read,
- k->is_console ? NULL : chr_event,
+ k->is_console ? chr_event_console : chr_event,
  chr_be_change, vcon, NULL, false);
 } else {
 qemu_chr_fe_set_handlers(>chr, NULL, NULL, NULL,
@@ -227,6 +278,11 @@ static void virtconsole_realize(DeviceState *dev, Error 
**errp)
 return;
 }
 
+if (k->is_console) {
+vcon->cols = (uint16_t) -1;
+vcon->rows = (uint16_t) -1;
+}
+
 if (qemu_chr_fe_backend_connected(>chr)) {
 /*
  * For consoles we don't block guest data transfer just
@@ -239,7 +295,7 @@ static void virtconsole_realize(DeviceState *dev, Error 
**errp)
  */
 if (k->is_console) {
 qemu_chr_fe_set_handlers(>chr, chr_can_read, chr_read,
- NULL, chr_be_change,
+ chr_event_console, chr_be_change,
  vcon, NULL, true);
 virtio_serial_open(port);
 } else {
-- 
2.27.0




[PATCH 2/6] chardev: add support for retrieving the terminal size

2020-06-21 Thread Szymon Lukasz
Extend the class of chardevs with a new function - chr_get_winsize.
A chardev backend should implement if it is able to get the size of
the connected terminal and can detect changes in the terminal size,
i.e. if the backend cannot detect resizes it must not implement this
(e.g. if we have a tty backend connected to some (pseudo)terminal
there is no clean way to detect resizes since SIGWINCH is sent only
for the controlling terminal).

Signed-off-by: Szymon Lukasz 
---
 chardev/char-fe.c | 11 +++
 chardev/char-mux.c|  7 +++
 include/chardev/char-fe.h | 11 +++
 include/chardev/char.h|  1 +
 4 files changed, 30 insertions(+)

diff --git a/chardev/char-fe.c b/chardev/char-fe.c
index f3530a90e6..802d3096cd 100644
--- a/chardev/char-fe.c
+++ b/chardev/char-fe.c
@@ -336,6 +336,17 @@ void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
 }
 }
 
+int qemu_chr_fe_get_winsize(CharBackend *be, uint16_t *cols, uint16_t *rows)
+{
+Chardev *chr = be->chr;
+
+if (chr && CHARDEV_GET_CLASS(chr)->chr_get_winsize) {
+return CHARDEV_GET_CLASS(chr)->chr_get_winsize(chr, cols, rows);
+}
+
+return -1;
+}
+
 void qemu_chr_fe_set_open(CharBackend *be, int fe_open)
 {
 Chardev *chr = be->chr;
diff --git a/chardev/char-mux.c b/chardev/char-mux.c
index 46c44af67c..368ce2334e 100644
--- a/chardev/char-mux.c
+++ b/chardev/char-mux.c
@@ -293,6 +293,12 @@ static void mux_chr_update_read_handlers(Chardev *chr)
   chr->gcontext, true, false);
 }
 
+static int mux_chr_get_winsize(Chardev *chr, uint16_t *cols, uint16_t *rows)
+{
+MuxChardev *d = MUX_CHARDEV(chr);
+return qemu_chr_fe_get_winsize(>chr, cols, rows);
+}
+
 void mux_set_focus(Chardev *chr, int focus)
 {
 MuxChardev *d = MUX_CHARDEV(chr);
@@ -385,6 +391,7 @@ static void char_mux_class_init(ObjectClass *oc, void *data)
 cc->chr_be_event = mux_chr_be_event;
 cc->chr_machine_done = open_muxes;
 cc->chr_update_read_handler = mux_chr_update_read_handlers;
+cc->chr_get_winsize = mux_chr_get_winsize;
 }
 
 static const TypeInfo char_mux_type_info = {
diff --git a/include/chardev/char-fe.h b/include/chardev/char-fe.h
index a553843364..b7943df93a 100644
--- a/include/chardev/char-fe.h
+++ b/include/chardev/char-fe.h
@@ -154,6 +154,17 @@ int qemu_chr_fe_wait_connected(CharBackend *be, Error 
**errp);
  */
 void qemu_chr_fe_set_echo(CharBackend *be, bool echo);
 
+/**
+ * qemu_chr_fe_get_winsize:
+ * @cols: the address for storing columns
+ * @rows: the address for storing rows
+ *
+ * Get the terminal size of the backend.
+ *
+ * Returns: 0 on success and < 0 on error
+ */
+int qemu_chr_fe_get_winsize(CharBackend *be, uint16_t *cols, uint16_t *rows);
+
 /**
  * qemu_chr_fe_set_open:
  *
diff --git a/include/chardev/char.h b/include/chardev/char.h
index 00589a6025..fb20707917 100644
--- a/include/chardev/char.h
+++ b/include/chardev/char.h
@@ -276,6 +276,7 @@ typedef struct ChardevClass {
 void (*chr_be_event)(Chardev *s, QEMUChrEvent event);
 /* Return 0 if succeeded, 1 if failed */
 int (*chr_machine_done)(Chardev *chr);
+int (*chr_get_winsize)(Chardev *chr, uint16_t *cols, uint16_t *rows);
 } ChardevClass;
 
 Chardev *qemu_chardev_new(const char *id, const char *typename,
-- 
2.27.0




[PATCH 4/6] char-stdio: add support for the terminal size

2020-06-21 Thread Szymon Lukasz
Implement chr_get_winsize for the stdio backend
and trigger CHR_EVENT_RESIZE upon SIGWINCH delivery.

Signed-off-by: Szymon Lukasz 
---
 chardev/char-stdio.c | 34 ++
 1 file changed, 34 insertions(+)

diff --git a/chardev/char-stdio.c b/chardev/char-stdio.c
index 82eaebc1db..ab14edffc1 100644
--- a/chardev/char-stdio.c
+++ b/chardev/char-stdio.c
@@ -34,7 +34,9 @@
 #include "chardev/char-win-stdio.h"
 #else
 #include 
+#include 
 #include "chardev/char-fd.h"
+#include "qemu/main-loop.h"
 #endif
 
 #ifndef _WIN32
@@ -45,6 +47,13 @@ static bool stdio_in_use;
 static bool stdio_allow_signal;
 static bool stdio_echo_state;
 
+typedef struct {
+FDChardev parent;
+Notifier resize_notifier;
+} StdioChardev;
+
+#define STDIO_CHARDEV(obj) OBJECT_CHECK(StdioChardev, (obj), 
TYPE_CHARDEV_STDIO)
+
 static void term_exit(void)
 {
 if (stdio_in_use) {
@@ -82,11 +91,31 @@ static void term_stdio_handler(int sig)
 qemu_chr_set_echo_stdio(NULL, stdio_echo_state);
 }
 
+static int qemu_chr_get_winsize_stdio(Chardev *chr, uint16_t *cols, uint16_t 
*rows)
+{
+struct winsize ws;
+
+if (ioctl(1, TIOCGWINSZ, ) < 0) {
+return -1;
+}
+
+*cols = ws.ws_col;
+*rows = ws.ws_row;
+return 0;
+}
+
+static void term_resize_notify(Notifier *n, void *data)
+{
+StdioChardev *s = container_of(n, StdioChardev, resize_notifier);
+qemu_chr_be_event(CHARDEV(s), CHR_EVENT_RESIZE);
+}
+
 static void qemu_chr_open_stdio(Chardev *chr,
 ChardevBackend *backend,
 bool *be_opened,
 Error **errp)
 {
+StdioChardev *s = STDIO_CHARDEV(chr);
 ChardevStdio *opts = backend->u.stdio.data;
 struct sigaction act;
 
@@ -116,6 +145,9 @@ static void qemu_chr_open_stdio(Chardev *chr,
 stdio_allow_signal = opts->signal;
 }
 qemu_chr_set_echo_stdio(chr, false);
+
+s->resize_notifier.notify = term_resize_notify;
+sigwinch_add_notifier(>resize_notifier);
 }
 #endif
 
@@ -139,6 +171,7 @@ static void char_stdio_class_init(ObjectClass *oc, void 
*data)
 #ifndef _WIN32
 cc->open = qemu_chr_open_stdio;
 cc->chr_set_echo = qemu_chr_set_echo_stdio;
+cc->chr_get_winsize = qemu_chr_get_winsize_stdio;
 #endif
 }
 
@@ -155,6 +188,7 @@ static const TypeInfo char_stdio_type_info = {
 .parent = TYPE_CHARDEV_WIN_STDIO,
 #else
 .parent = TYPE_CHARDEV_FD,
+.instance_size = sizeof(StdioChardev),
 #endif
 .instance_finalize = char_stdio_finalize,
 .class_init = char_stdio_class_init,
-- 
2.27.0




[PATCH 3/6] chardev: add support for notifying about terminal resizes

2020-06-21 Thread Szymon Lukasz
Add a new chardev event, CHR_EVENT_RESIZE, which a backend should
trigger if detects the size of the connected terminal changed.

Signed-off-by: Szymon Lukasz 
---
 backends/cryptodev-vhost-user.c | 1 +
 chardev/char.c  | 1 +
 hw/block/vhost-user-blk.c   | 1 +
 hw/char/terminal3270.c  | 1 +
 hw/char/virtio-console.c| 1 +
 hw/ipmi/ipmi_bmc_extern.c   | 1 +
 hw/usb/ccid-card-passthru.c | 1 +
 hw/usb/dev-serial.c | 1 +
 hw/usb/redirect.c   | 1 +
 include/chardev/char.h  | 1 +
 monitor/hmp.c   | 1 +
 monitor/qmp.c   | 1 +
 net/vhost-user.c| 1 +
 13 files changed, 13 insertions(+)

diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c
index 8b8cbc4223..bbf8ad426a 100644
--- a/backends/cryptodev-vhost-user.c
+++ b/backends/cryptodev-vhost-user.c
@@ -174,6 +174,7 @@ static void cryptodev_vhost_user_event(void *opaque, 
QEMUChrEvent event)
 case CHR_EVENT_BREAK:
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/chardev/char.c b/chardev/char.c
index e3051295ac..904f8bf6e3 100644
--- a/chardev/char.c
+++ b/chardev/char.c
@@ -74,6 +74,7 @@ void qemu_chr_be_event(Chardev *s, QEMUChrEvent event)
 case CHR_EVENT_BREAK:
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c
index a00b854736..1a656a27c3 100644
--- a/hw/block/vhost-user-blk.c
+++ b/hw/block/vhost-user-blk.c
@@ -403,6 +403,7 @@ static void vhost_user_blk_event(void *opaque, QEMUChrEvent 
event)
 case CHR_EVENT_BREAK:
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/hw/char/terminal3270.c b/hw/char/terminal3270.c
index 2c47ebf007..eadccbb617 100644
--- a/hw/char/terminal3270.c
+++ b/hw/char/terminal3270.c
@@ -169,6 +169,7 @@ static void chr_event(void *opaque, QEMUChrEvent event)
 case CHR_EVENT_BREAK:
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c
index 4f46753ea3..97b9240ef5 100644
--- a/hw/char/virtio-console.c
+++ b/hw/char/virtio-console.c
@@ -165,6 +165,7 @@ static void chr_event(void *opaque, QEMUChrEvent event)
 case CHR_EVENT_BREAK:
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c
index f9a13e0a44..9562584309 100644
--- a/hw/ipmi/ipmi_bmc_extern.c
+++ b/hw/ipmi/ipmi_bmc_extern.c
@@ -439,6 +439,7 @@ static void chr_event(void *opaque, QEMUChrEvent event)
 case CHR_EVENT_BREAK:
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c
index bb325dbc4a..3c26b16ed0 100644
--- a/hw/usb/ccid-card-passthru.c
+++ b/hw/usb/ccid-card-passthru.c
@@ -321,6 +321,7 @@ static void ccid_card_vscard_event(void *opaque, 
QEMUChrEvent event)
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
 case CHR_EVENT_CLOSED:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c
index 7e50e3ba47..e8e960d0e6 100644
--- a/hw/usb/dev-serial.c
+++ b/hw/usb/dev-serial.c
@@ -507,6 +507,7 @@ static void usb_serial_event(void *opaque, QEMUChrEvent 
event)
 break;
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 417a60a2e6..b716c4fdd7 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -1383,6 +1383,7 @@ static void usbredir_chardev_event(void *opaque, 
QEMUChrEvent event)
 case CHR_EVENT_BREAK:
 case CHR_EVENT_MUX_IN:
 case CHR_EVENT_MUX_OUT:
+case CHR_EVENT_RESIZE:
 /* Ignore */
 break;
 }
diff --git a/include/chardev/char.h b/include/chardev/char.h
index fb20707917..c3d108ce82 100644
--- a/include/chardev/char.h
+++ b/include/chardev/char.h
@@ -22,6 +22,7 @@ typedef enum {
 CHR_EVENT_OPENED, /* new connection established */
 CHR_EVENT_MUX_IN, /* mux-focus was set to this terminal */
 CHR_EVENT_MUX_OUT, /* mux-focus will move on */
+CHR_EVENT_RESIZE, /* the terminal size of the chardev changed */
 CHR_EVENT_CLOSED /* connection closed.  NOTE: currently this event
   * is only bound to the read port of the chardev.
   * Normally the read port and write port of a
diff --git a/monitor/hmp.c b/monitor/hmp.c
index

[PATCH 0/6] virtio-console: notify about the terminal size

2020-06-21 Thread Szymon Lukasz
The goal of this series is to have a nice terminal into a Linux guest
without having to set up networking and using, e.g. ssh.

The virtio spec allows a virtio-console device to notify the guest about
terminal resizes in the host. Linux Kernel implements the driver part of
the spec. This series implement the device part in QEMU.

In this series resize notifications are only supported for the stdio
backend but I think it should be easy to add support for the vc backend.
Support for tty/serial backends is complicated by the fact that there is
no clean way to detect resizes of the underlying terminal.

Also there is a problem with the virtio spec and Linux Kernel
implementation, the order of fields in virtio_console_resize struct
differs between the kernel and the spec. I do not know if there is any
implementation of the virtio-console driver that handles resize messages
and uses a different order than Linux.



Szymon Lukasz (6):
  main-loop: change the handling of SIGWINCH
  chardev: add support for retrieving the terminal size
  chardev: add support for notifying about terminal resizes
  char-stdio: add support for the terminal size
  virtio-serial-bus: add terminal resize messages
  virtio-console: notify the guest about terminal resizes

 backends/cryptodev-vhost-user.c   |  1 +
 chardev/char-fe.c | 11 ++
 chardev/char-mux.c|  7 
 chardev/char-stdio.c  | 34 
 chardev/char.c|  1 +
 hw/block/vhost-user-blk.c |  1 +
 hw/char/terminal3270.c|  1 +
 hw/char/trace-events  |  1 +
 hw/char/virtio-console.c  | 65 +--
 hw/char/virtio-serial-bus.c   | 41 ++-
 hw/ipmi/ipmi_bmc_extern.c |  1 +
 hw/usb/ccid-card-passthru.c   |  1 +
 hw/usb/dev-serial.c   |  1 +
 hw/usb/redirect.c |  1 +
 include/chardev/char-fe.h | 11 ++
 include/chardev/char.h|  2 +
 include/hw/virtio/virtio-serial.h |  5 +++
 include/qemu/main-loop.h  |  4 ++
 monitor/hmp.c |  1 +
 monitor/qmp.c |  1 +
 net/vhost-user.c  |  1 +
 ui/curses.c   | 11 +++---
 util/main-loop.c  | 21 ++
 23 files changed, 213 insertions(+), 11 deletions(-)

-- 
2.27.0




[PATCH 1/6] main-loop: change the handling of SIGWINCH

2020-06-21 Thread Szymon Lukasz
Block SIGWINCH, so it is delivered only via signalfd.
Install a handler that uses NotifierList to tell
interested parties about SIGWINCH delivery.

Signed-off-by: Szymon Lukasz 
---
 include/qemu/main-loop.h |  4 
 ui/curses.c  | 11 ++-
 util/main-loop.c | 21 +
 3 files changed, 31 insertions(+), 5 deletions(-)

diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h
index a6d20b0719..f27dba1fd8 100644
--- a/include/qemu/main-loop.h
+++ b/include/qemu/main-loop.h
@@ -325,4 +325,8 @@ typedef struct MainLoopPoll {
 void main_loop_poll_add_notifier(Notifier *notify);
 void main_loop_poll_remove_notifier(Notifier *notify);
 
+#ifndef _WIN32
+void sigwinch_add_notifier(Notifier *n);
+#endif
+
 #endif
diff --git a/ui/curses.c b/ui/curses.c
index a59b23a9cf..e5895d506f 100644
--- a/ui/curses.c
+++ b/ui/curses.c
@@ -34,6 +34,7 @@
 #include 
 
 #include "qapi/error.h"
+#include "qemu/main-loop.h"
 #include "qemu/module.h"
 #include "ui/console.h"
 #include "ui/input.h"
@@ -146,7 +147,7 @@ static void curses_resize(DisplayChangeListener *dcl,
 }
 
 #if !defined(_WIN32) && defined(SIGWINCH) && defined(KEY_RESIZE)
-static volatile sig_atomic_t got_sigwinch;
+static bool got_sigwinch;
 static void curses_winch_check(void)
 {
 struct winsize {
@@ -169,17 +170,17 @@ static void curses_winch_check(void)
 invalidate = 1;
 }
 
-static void curses_winch_handler(int signum)
+static void curses_winch_handler(Notifier *n, void *data)
 {
 got_sigwinch = true;
 }
 
 static void curses_winch_init(void)
 {
-struct sigaction old, winch = {
-.sa_handler  = curses_winch_handler,
+static Notifier n = {
+.notify = curses_winch_handler
 };
-sigaction(SIGWINCH, , );
+sigwinch_add_notifier();
 }
 #else
 static void curses_winch_check(void) {}
diff --git a/util/main-loop.c b/util/main-loop.c
index eda63fe4e0..0f5c8f3af1 100644
--- a/util/main-loop.c
+++ b/util/main-loop.c
@@ -90,6 +90,7 @@ static int qemu_signal_init(Error **errp)
 sigaddset(, SIGIO);
 sigaddset(, SIGALRM);
 sigaddset(, SIGBUS);
+sigaddset(, SIGWINCH);
 /* SIGINT cannot be handled via signalfd, so that ^C can be used
  * to interrupt QEMU when it is being run under gdb.  SIGHUP and
  * SIGTERM are also handled asynchronously, even though it is not
@@ -111,6 +112,26 @@ static int qemu_signal_init(Error **errp)
 return 0;
 }
 
+static NotifierList sigwinch_notifiers =
+NOTIFIER_LIST_INITIALIZER(sigwinch_notifiers);
+
+static void sigwinch_handler(int signum)
+{
+notifier_list_notify(_notifiers, NULL);
+}
+
+void sigwinch_add_notifier(Notifier *n)
+{
+if (notifier_list_empty(_notifiers)) {
+struct sigaction action = {
+.sa_handler = sigwinch_handler,
+};
+sigaction(SIGWINCH, , NULL);
+}
+
+notifier_list_add(_notifiers, n);
+}
+
 #else /* _WIN32 */
 
 static int qemu_signal_init(Error **errp)
-- 
2.27.0




[PATCH 5/6] virtio-serial-bus: add terminal resize messages

2020-06-21 Thread Szymon Lukasz
Implement the part of the virtio spec that allows to notify the virtio
driver about terminal resizes. The virtio spec contains two methods to
achieve that:

For legacy drivers, we have only one port and we put the terminal size
in the config space and inject the config changed interrupt.

For multiport devices, we use the control virtqueue to send a packet
containing the terminal size. Note that the Linux kernel expects
the fields indicating the number of rows and columns in a packet to be
in a different order than the one specified in the current version of
the virtio spec. We follow the Linux implementation, so hopefully there
is no implementation of this functionality conforming to the spec.

Signed-off-by: Szymon Lukasz 
---
 hw/char/trace-events  |  1 +
 hw/char/virtio-serial-bus.c   | 41 +--
 include/hw/virtio/virtio-serial.h |  5 
 3 files changed, 45 insertions(+), 2 deletions(-)

diff --git a/hw/char/trace-events b/hw/char/trace-events
index d20eafd56f..be40df47ea 100644
--- a/hw/char/trace-events
+++ b/hw/char/trace-events
@@ -10,6 +10,7 @@ serial_ioport_write(uint16_t addr, uint8_t value) "write addr 
0x%02x val 0x%02x"
 
 # virtio-serial-bus.c
 virtio_serial_send_control_event(unsigned int port, uint16_t event, uint16_t 
value) "port %u, event %u, value %u"
+virtio_serial_send_console_resize(unsigned int port, uint16_t cols, uint16_t 
rows) "port %u, cols %u, rows %u"
 virtio_serial_throttle_port(unsigned int port, bool throttle) "port %u, 
throttle %d"
 virtio_serial_handle_control_message(uint16_t event, uint16_t value) "event 
%u, value %u"
 virtio_serial_handle_control_message_port(unsigned int port) "port %u"
diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c
index 262089c0c9..9d99161e13 100644
--- a/hw/char/virtio-serial-bus.c
+++ b/hw/char/virtio-serial-bus.c
@@ -261,6 +261,42 @@ static size_t send_control_event(VirtIOSerial *vser, 
uint32_t port_id,
 return send_control_msg(vser, , sizeof(cpkt));
 }
 
+/*
+ * This struct should be added to the Linux kernel uapi headers
+ * and later imported to standard-headers/linux/virtio_console.h
+ */
+struct virtio_console_resize {
+__virtio16 rows;
+__virtio16 cols;
+};
+
+void virtio_serial_send_console_resize(VirtIOSerialPort *port,
+   uint16_t cols, uint16_t rows)
+{
+VirtIOSerial *vser = port->vser;
+VirtIODevice *vdev = VIRTIO_DEVICE(vser);
+
+if (virtio_vdev_has_feature(vdev, VIRTIO_CONSOLE_F_MULTIPORT)) {
+struct {
+struct virtio_console_control control;
+struct virtio_console_resize resize;
+} buffer;
+
+virtio_stl_p(vdev, , port->id);
+virtio_stw_p(vdev, , VIRTIO_CONSOLE_RESIZE);
+virtio_stw_p(vdev, , cols);
+virtio_stw_p(vdev, , rows);
+
+trace_virtio_serial_send_console_resize(port->id, cols, rows);
+send_control_msg(vser, , sizeof(buffer));
+
+} else if (virtio_vdev_has_feature(vdev, VIRTIO_CONSOLE_F_SIZE)) {
+vser->port0_cols = cols;
+vser->port0_rows = rows;
+virtio_notify_config(vdev);
+}
+}
+
 /* Functions for use inside qemu to open and read from/write to ports */
 int virtio_serial_open(VirtIOSerialPort *port)
 {
@@ -559,6 +595,7 @@ static uint64_t get_features(VirtIODevice *vdev, uint64_t 
features,
 vser = VIRTIO_SERIAL(vdev);
 
 features |= vser->host_features;
+virtio_add_feature(, VIRTIO_CONSOLE_F_SIZE);
 if (vser->bus.max_nr_ports > 1) {
 virtio_add_feature(, VIRTIO_CONSOLE_F_MULTIPORT);
 }
@@ -572,8 +609,8 @@ static void get_config(VirtIODevice *vdev, uint8_t 
*config_data)
 struct virtio_console_config *config =
 (struct virtio_console_config *)config_data;
 
-config->cols = 0;
-config->rows = 0;
+config->cols = virtio_tswap16(vdev, vser->port0_cols);
+config->rows = virtio_tswap16(vdev, vser->port0_rows);
 config->max_nr_ports = virtio_tswap32(vdev,
   vser->serial.max_virtserial_ports);
 }
diff --git a/include/hw/virtio/virtio-serial.h 
b/include/hw/virtio/virtio-serial.h
index ed3e916b68..1d6436c0b1 100644
--- a/include/hw/virtio/virtio-serial.h
+++ b/include/hw/virtio/virtio-serial.h
@@ -188,6 +188,8 @@ struct VirtIOSerial {
 virtio_serial_conf serial;
 
 uint64_t host_features;
+
+uint16_t port0_cols, port0_rows;
 };
 
 /* Interface to the virtio-serial bus */
@@ -222,6 +224,9 @@ size_t virtio_serial_guest_ready(VirtIOSerialPort *port);
  */
 void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle);
 
+void virtio_serial_send_console_resize(VirtIOSerialPort *port,
+   uint16_t cols, uint16_t rows);
+
 #define TYPE_VIRTIO_SERIAL "virtio-serial-device"
 #define VIRTIO_SERIAL(obj) \
 OBJECT_CHECK(VirtIOSerial, (obj), TYPE_VIRTIO_SERIAL)
-- 
2.27.0