#include <linux/acpi_pmtmr.h>
 #include <linux/kernel.h>
 #include <linux/reboot.h>
+#include <linux/serial_8250.h>
 #include <asm/apic.h>
 #include <asm/cpu.h>
 #include <asm/hypervisor.h>
 #include <asm/setup.h>
 #include <asm/jailhouse_para.h>
 
-static __initdata struct jailhouse_setup_data setup_data;
+static struct jailhouse_setup_data setup_data;
 #define SETUP_DATA_V1_LEN      (sizeof(setup_data.hdr) + sizeof(setup_data.v1))
+#define SETUP_DATA_V2_LEN      (SETUP_DATA_V1_LEN + sizeof(setup_data.v2))
 
 static unsigned int precalibrated_tsc_khz;
 
+static void jailhouse_setup_irq(unsigned int irq)
+{
+       struct mpc_intsrc mp_irq = {
+               .type           = MP_INTSRC,
+               .irqtype        = mp_INT,
+               .irqflag        = MP_IRQPOL_ACTIVE_HIGH | MP_IRQTRIG_EDGE,
+               .srcbusirq      = irq,
+               .dstirq         = irq,
+       };
+       mp_save_irq(&mp_irq);
+}
+
 static uint32_t jailhouse_cpuid_base(void)
 {
        if (boot_cpu_data.cpuid_level < 0 ||
                .type = IOAPIC_DOMAIN_STRICT,
                .ops = &mp_ioapic_irqdomain_ops,
        };
-       struct mpc_intsrc mp_irq = {
-               .type = MP_INTSRC,
-               .irqtype = mp_INT,
-               .irqflag = MP_IRQPOL_ACTIVE_HIGH | MP_IRQTRIG_EDGE,
-       };
        unsigned int cpu;
 
        jailhouse_x2apic_init();
        if (setup_data.v1.standard_ioapic) {
                mp_register_ioapic(0, 0xfec00000, gsi_top, &ioapic_cfg);
 
-               /* Register 1:1 mapping for legacy UART IRQs 3 and 4 */
-               mp_irq.srcbusirq = mp_irq.dstirq = 3;
-               mp_save_irq(&mp_irq);
-
-               mp_irq.srcbusirq = mp_irq.dstirq = 4;
-               mp_save_irq(&mp_irq);
+               if (IS_ENABLED(CONFIG_SERIAL_8250) &&
+                   setup_data.hdr.version < 2) {
+                       /* Register 1:1 mapping for legacy UART IRQs 3 and 4 */
+                       jailhouse_setup_irq(3);
+                       jailhouse_setup_irq(4);
+               }
        }
 }
 
        return 0;
 }
 
+#ifdef CONFIG_SERIAL_8250
+static inline bool jailhouse_uart_enabled(unsigned int uart_nr)
+{
+       return setup_data.v2.flags & BIT(uart_nr);
+}
+
+static void jailhouse_serial_fixup(int port, struct uart_port *up,
+                                  u32 *capabilities)
+{
+       static const u16 pcuart_base[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8};
+       unsigned int n;
+
+       for (n = 0; n < ARRAY_SIZE(pcuart_base); n++) {
+               if (pcuart_base[n] != up->iobase)
+                       continue;
+
+               if (jailhouse_uart_enabled(n)) {
+                       pr_info("Enabling UART%u (port 0x%lx)\n", n,
+                               up->iobase);
+                       jailhouse_setup_irq(up->irq);
+               } else {
+                       /* Deactivate UART if access isn't allowed */
+                       up->iobase = 0;
+               }
+               break;
+       }
+}
+
+static void __init jailhouse_serial_workaround(void)
+{
+       /*
+        * There are flags inside setup_data that indicate availability of
+        * platform UARTs since setup data version 2.
+        *
+        * In case of version 1, we don't know which UARTs belong Linux. In
+        * this case, unconditionally register 1:1 mapping for legacy UART IRQs
+        * 3 and 4.
+        */
+       if (setup_data.hdr.version > 1)
+               serial8250_set_isa_configurator(jailhouse_serial_fixup);
+}
+#else /* !CONFIG_SERIAL_8250 */
+static inline void jailhouse_serial_workaround(void)
+{
+}
+#endif /* CONFIG_SERIAL_8250 */
+
 static void __init jailhouse_init_platform(void)
 {
        u64 pa_data = boot_params.hdr.setup_data;
        if (setup_data.hdr.version == 0 ||
            setup_data.hdr.compatible_version !=
                JAILHOUSE_SETUP_REQUIRED_VERSION ||
-           (setup_data.hdr.version >= 1 && header.len < SETUP_DATA_V1_LEN))
+           (setup_data.hdr.version == 1 && header.len < SETUP_DATA_V1_LEN) ||
+           (setup_data.hdr.version >= 2 && header.len < SETUP_DATA_V2_LEN))
                goto unsupported;
 
        pmtmr_ioport = setup_data.v1.pm_timer_address;
         * are none in a non-root cell.
         */
        disable_acpi();
+
+       jailhouse_serial_workaround();
        return;
 
 unsupported: