Allow multiple dom0less domains to use the console_io hypercalls to print to the console. Handle them in a similar way to vpl011: only the domain which has focus can read from the console. All domains can write to the console but the ones without focus have a prefix. In this case the prefix is applied by using guest_printk instead of printk or console_puts which is what the original code was already doing.
When switching focus using Ctrl-AAA, discard any unread data in the input buffer. Input is read quickly and the user would be aware of it being slow or stuck as they use Ctrl-AAA to switch focus domain. In that situation, it is to be expected that the unread input is lost. The domain writes are buffered when the domain is not in focus. Push out the buffer when the domain enters focus. Add the console_lock around serial_rx_prod and serial_rx_cons modifications to protect it against concurrent writes to it. Also protect against changes of domain with focus while reading from the serial. Signed-off-by: Stefano Stabellini <[email protected]> --- Changes in v4: - protect serial_rx_prod and serial_rx_cons with console_lock - protect CONSOLEIO_read against concurrent changes of the focus domain - if vpl011 is enabled, send characters to it (retains current behavior on ARM) --- xen/drivers/char/console.c | 52 ++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c index 7b176da71a..acfc49d558 100644 --- a/xen/drivers/char/console.c +++ b/xen/drivers/char/console.c @@ -574,8 +574,12 @@ static void console_switch_input(void) if ( !d->console.input_allowed ) continue; - console_rx = next_rx; printk("*** Serial input to DOM%u", domid); + nrspin_lock_irq(&console_lock); + console_rx = next_rx; + /* Don't let the next dom read the previous dom's unread data. */ + serial_rx_cons = serial_rx_prod; + nrspin_unlock_irq(&console_lock); break; } } @@ -594,10 +598,22 @@ static void __serial_rx(char c) if ( console_rx == 0 ) return handle_keypress(c, false); + nrspin_lock_irq(&console_lock); d = console_get_domain(); if ( !d ) + { + nrspin_unlock_irq(&console_lock); return; + } +#ifdef CONFIG_SBSA_VUART_CONSOLE + /* Prioritize vpl011 if enabled for this domain */ + if ( d->arch.vpl011.base_addr ) + { + /* Deliver input to the emulated UART. */ + rc = vpl011_rx_char_xen(d, c); + } else +#endif if ( is_hardware_domain(d) || IS_ENABLED(CONFIG_DOM0LESS_BOOT) ) { /* @@ -613,11 +629,6 @@ static void __serial_rx(char c) */ send_guest_domain_virq(d, VIRQ_CONSOLE); } -#ifdef CONFIG_SBSA_VUART_CONSOLE - else - /* Deliver input to the emulated UART. */ - rc = vpl011_rx_char_xen(d, c); -#endif if ( consoled_is_enabled() ) /* Deliver input to the PV shim console. */ @@ -629,6 +640,7 @@ static void __serial_rx(char c) rc); console_put_domain(d); + nrspin_unlock_irq(&console_lock); } static void cf_check serial_rx(char c) @@ -729,6 +741,7 @@ static long guest_console_write(XEN_GUEST_HANDLE_PARAM(char) buffer, unsigned int flags = opt_console_to_ring ? CONSOLE_ALL : CONSOLE_DEFAULT; struct domain *cd = current->domain; + struct domain *input; while ( count > 0 ) { @@ -741,17 +754,26 @@ static long guest_console_write(XEN_GUEST_HANDLE_PARAM(char) buffer, if ( copy_from_guest(kbuf, buffer, kcount) ) return -EFAULT; - if ( is_hardware_domain(cd) ) + nrspin_lock_irq(&console_lock); + input = console_get_domain(); + if ( input && cd == input ) { + if ( cd->pbuf_idx ) + { + console_send(cd->pbuf, cd->pbuf_idx, flags); + cd->pbuf_idx = 0; + } /* Use direct console output as it could be interactive */ - nrspin_lock_irq(&console_lock); console_send(kbuf, kcount, flags); + console_put_domain(input); nrspin_unlock_irq(&console_lock); } else { char *kin = kbuf, *kout = kbuf, c; + console_put_domain(input); + nrspin_unlock_irq(&console_lock); /* Strip non-printable characters */ do { @@ -793,6 +815,7 @@ long do_console_io( { long rc; unsigned int idx, len; + struct domain *d; rc = xsm_console_io(XSM_OTHER, current->domain, cmd); if ( rc ) @@ -813,6 +836,15 @@ long do_console_io( if ( count > INT_MAX ) break; + nrspin_lock_irq(&console_lock); + d = console_get_domain(); + if ( d != current->domain ) + { + console_put_domain(d); + nrspin_unlock_irq(&console_lock); + return 0; + } + rc = 0; while ( (serial_rx_cons != serial_rx_prod) && (rc < count) ) { @@ -825,11 +857,15 @@ long do_console_io( if ( copy_to_guest_offset(buffer, rc, &serial_rx_ring[idx], len) ) { rc = -EFAULT; + console_put_domain(d); + nrspin_unlock_irq(&console_lock); break; } rc += len; serial_rx_cons += len; } + console_put_domain(d); + nrspin_unlock_irq(&console_lock); break; default: rc = -ENOSYS; -- 2.25.1
