diff --git a/xen/arch/x86/include/asm/setup.h b/xen/arch/x86/include/asm/setup.h
index 51fce66607..5242dfcf93 100644
--- a/xen/arch/x86/include/asm/setup.h
+++ b/xen/arch/x86/include/asm/setup.h
@@ -64,6 +64,4 @@ extern bool opt_dom0_verbose;
 extern bool opt_dom0_cpuid_faulting;
 extern bool opt_dom0_msr_relaxed;
 
-#define max_init_domid (0)
-
 #endif
diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
index 0e410fa086..f5b759898e 100644
--- a/xen/drivers/char/console.c
+++ b/xen/drivers/char/console.c
@@ -473,45 +473,102 @@ static void cf_check dump_console_ring_key(unsigned char key)
  */
 static unsigned int __read_mostly console_rx = 0;
 
-#define max_console_rx (max_init_domid + 1)
+#define CON_RX_DOMID (console_rx - 1)
 
 /* Make sure to rcu_unlock_domain after use */
 struct domain *console_input_domain(void)
 {
     if ( console_rx == 0 )
             return NULL;
-    return rcu_lock_domain_by_id(console_rx - 1);
+    return rcu_lock_domain_by_id(CON_RX_DOMID);
 }
 
 static void switch_serial_input(void)
 {
-    unsigned int next_rx = console_rx;
+    struct domain *next, *d = console_input_domain();
 
-    /*
-     * Rotate among Xen, dom0 and boot-time created domUs while skipping
-     * switching serial input to non existing domains.
-     */
-    for ( ; ; )
+    if ( d == NULL )
     {
-        struct domain *d;
+        if ( hardware_domain )
+        {
+            console_rx = hardware_domain->domain_id + 1;
+            printk("*** Serial input to DOM%d", CON_RX_DOMID);
+            goto out; //print switch_code statement & newline
+        }
+        else
+        {
+            for_each_domain(next)
+            {
+                if ( xsm_console_io(XSM_OTHER, next, CONSOLEIO_read) == 0 )
+                {
+                    console_rx = next->domain_id + 1;
+                    printk("*** Serial input to DOM%d", CON_RX_DOMID);
+                    goto out; //print switch_code statement & newline
+                }
+            }
 
-        if ( next_rx++ >= max_console_rx )
+            console_rx = 0;
+            printk("*** Serial input to Xen");
+            goto out;
+        }
+    }
+
+    for ( next = rcu_dereference(d->next_in_list); next != NULL;
+          next = rcu_dereference(next->next_in_list) )
+    {
+        if ( hardware_domain && next == hardware_domain )
         {
             console_rx = 0;
             printk("*** Serial input to Xen");
-            break;
+            goto out;
         }
 
-        d = rcu_lock_domain_by_id(next_rx - 1);
-        if ( d )
+        if ( xsm_console_io(XSM_OTHER, next, CONSOLEIO_read) == 0 )
         {
-            rcu_unlock_domain(d);
-            console_rx = next_rx;
-            printk("*** Serial input to DOM%u", next_rx - 1);
-            break;
+            console_rx = next->domain_id + 1;
+            printk("*** Serial input to DOM%d", CON_RX_DOMID);
+            goto out;
+        }
+    }
+
+    /*
+     * Hit the end of the domain list and instead of assuming that the
+     * hardware domain is the first in the list, get the first domain
+     * in the domain list and then if it is not the hardware domain or
+     * does not have console privilege, iterate the list until we find
+     * the hardware domain or a domain with console privilege.
+     */
+    if ( next == NULL )
+    {
+        for_each_domain(next)
+        {
+            if ( hardware_domain && next == hardware_domain )
+            {
+                console_rx = 0;
+                printk("*** Serial input to Xen");
+                goto out;
+            }
+
+            if ( xsm_console_io(XSM_OTHER, next, CONSOLEIO_read) == 0 )
+            {
+                console_rx = next->domain_id + 1;
+                printk("*** Serial input to DOM%d", CON_RX_DOMID);
+                goto out;
+            }
         }
     }
 
+    /*
+     * If we got here, could not find a domain with console io privilege.
+     * Default to Xen.
+     */
+    console_rx = 0;
+    printk("*** Serial input to Xen");
+
+out:
+    if ( d != NULL)
+        rcu_unlock_domain(d);
+
     if ( switch_code )
         printk(" (type 'CTRL-%c' three times to switch input)",
                opt_conswitch[0]);
@@ -520,12 +577,11 @@ static void switch_serial_input(void)
 
 static void __serial_rx(char c, struct cpu_user_regs *regs)
 {
-    switch ( console_rx )
-    {
-    case 0:
+    if ( console_rx == 0 )
         return handle_keypress(c, regs);
 
-    case 1:
+    if ( hardware_domain->domain_id == CON_RX_DOMID )
+    {
         /*
          * Deliver input to the hardware domain buffer, unless it is
          * already full.
@@ -538,31 +594,37 @@ static void __serial_rx(char c, struct cpu_user_regs *regs)
          * getting stuck.
          */
         send_global_virq(VIRQ_CONSOLE);
-        break;
-
-#ifdef CONFIG_SBSA_VUART_CONSOLE
-    default:
+    }
+    else
     {
-        struct domain *d = rcu_lock_domain_by_id(console_rx - 1);
+        struct domain *d = rcu_lock_domain_by_any_id(CON_RX_DOMID);
 
+        if ( d == NULL )
+            goto unlock_out;
+
+#ifdef CONFIG_SBSA_VUART_CONSOLE
         /*
          * If we have a properly initialized vpl011 console for the
          * domain, without a full PV ring to Dom0 (in that case input
          * comes from the PV ring), then send the character to it.
          */
-        if ( d != NULL &&
-             !d->arch.vpl011.backend_in_domain &&
+        if ( !d->arch.vpl011.backend_in_domain &&
              d->arch.vpl011.backend.xen != NULL )
+        {
             vpl011_rx_char_xen(d, c);
-        else
-            printk("Cannot send chars to Dom%d: no UART available\n",
-                   console_rx - 1);
+            goto unlock_out;
+        }
+#endif
+
+        if ( (serial_rx_prod - serial_rx_cons) != SERIAL_RX_SIZE )
+            serial_rx_ring[SERIAL_RX_MASK(serial_rx_prod++)] = c;
 
+        send_guest_global_virq(d, VIRQ_CONSOLE);
+
+unlock_out:
         if ( d != NULL )
             rcu_unlock_domain(d);
     }
-#endif
-    }
 
 #ifdef CONFIG_X86
     if ( pv_shim && pv_console )
@@ -627,7 +689,7 @@ 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) )
+        if ( cd->domain_id == CON_RX_DOMID )
         {
             /* Use direct console output as it could be interactive */
             spin_lock_irq(&console_lock);
@@ -717,6 +779,8 @@ long do_console_io(
         rc = -E2BIG;
         if ( count > INT_MAX )
             break;
+        if ( CON_RX_DOMID != current->domain->domain_id )
+            return 0;
 
         rc = 0;
         while ( (serial_rx_cons != serial_rx_prod) && (rc < count) )
@@ -1107,7 +1171,7 @@ void __init console_endboot(void)
      * a useful 'how to switch' message.
      */
     if ( opt_conswitch[1] == 'x' )
-        console_rx = max_console_rx;
+        console_rx = 0;
 
     register_keyhandler('w', dump_console_ring_key,
                         "synchronously dump console ring buffer (dmesg)", 0);
