xen-devel.lists.xenproject.org archive mirror
 help / color / mirror / Atom feed
* [PATCH V5] ns16550: Add support for UART present in Broadcom TruManage capable NetXtreme chips
@ 2013-11-22 16:45 Aravind Gopalakrishnan
  2013-11-22 16:59 ` Jan Beulich
  0 siblings, 1 reply; 4+ messages in thread
From: Aravind Gopalakrishnan @ 2013-11-22 16:45 UTC (permalink / raw)
  To: xen-devel, jbeulich
  Cc: Thomas Lendacky, andrew.cooper3, Aravind Gopalakrishnan,
	Suravee Suthikulpanit, sherry.hurwitz, shurd

Since it is an MMIO device, the code has been modified to accept MMIO based
devices as well. MMIO device settings are populated in the 'uart_config' table.
It also advertises 64 bit BAR. Therefore, code is reworked to account for 64
bit BAR and 64 bit MMIO lengths.

Some more quirks are - the need to shift the register offset by a specific
value and we also need to verify (UART_LSR_THRE && UART_LSR_TEMT) bits before
transmitting data.

While testing, include com1=115200,8n1,pci,0 on the xen cmdline to observe
output on console using SOL.

Changes from V4:
  - per Jan's comments:
    - Use PFN_UP and PFN_DOWN
    - Use len & -len
  - Misc:
    - Defer assigning values to fields in ns16550 struct until all checks are done
    - mmio length should at least be 8 bytes times (1 << reg_shift). Fixed this

Signed-off-by: Aravind Gopalakrishnan <Aravind.Gopalakrishnan@amd.com>
Signed-off-by: Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>
Signed-off-by: Thomas Lendacky <Thomas.Lendacky@amd.com>
---
 xen/drivers/char/ns16550.c | 168 ++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 149 insertions(+), 19 deletions(-)

diff --git a/xen/drivers/char/ns16550.c b/xen/drivers/char/ns16550.c
index 9c2cded..e13bad2 100644
--- a/xen/drivers/char/ns16550.c
+++ b/xen/drivers/char/ns16550.c
@@ -46,13 +46,14 @@ string_param("com2", opt_com2);
 static struct ns16550 {
     int baud, clock_hz, data_bits, parity, stop_bits, fifo_size, irq;
     u64 io_base;   /* I/O port or memory-mapped I/O address. */
-    u32 io_size;
+    u64 io_size;
     int reg_shift; /* Bits to shift register offset by */
     int reg_width; /* Size of access to use, the registers
                     * themselves are still bytes */
     char __iomem *remapped_io_base;  /* Remapped virtual address of MMIO. */
     /* UART with IRQ line: interrupt-driven I/O. */
     struct irqaction irqaction;
+    u8 lsr_mask;
 #ifdef CONFIG_ARM
     struct vuart_info vuart;
 #endif
@@ -69,6 +70,7 @@ static struct ns16550 {
     bool_t pb_bdf_enable;   /* if =1, pb-bdf effective, port behind bridge */
     bool_t ps_bdf_enable;   /* if =1, ps_bdf effective, port on pci card */
     u32 bar;
+    u32 bar64;
     u16 cr;
     u8 bar_idx;
 #endif
@@ -77,6 +79,37 @@ static struct ns16550 {
 #endif
 } ns16550_com[2] = { { 0 } };
 
+/* Defining uart config options for MMIO devices */
+struct ns16550_config_mmio {
+    u16 vendor_id;
+    u16 dev_id;
+    unsigned int reg_shift;
+    unsigned int reg_width; 
+    unsigned int fifo_size;
+    u8 lsr_mask;
+    unsigned int max_bars;
+};
+
+/*
+ * Create lookup tables for specific MMIO devices..
+ * It is assumed that if the device found is MMIO,
+ * then you have indexed it here. Else, the driver
+ * does nothing.
+ */
+static struct ns16550_config_mmio __initdata uart_config[] =
+{
+    /* Broadcom TruManage device */
+    { 
+        .vendor_id = 0x14e4,
+        .dev_id = 0x160a,
+        .reg_shift = 2,
+        .reg_width = 1,
+        .fifo_size = 16,
+        .lsr_mask = (UART_LSR_THRE | UART_LSR_TEMT),
+        .max_bars = 1,
+    },
+};
+
 static void ns16550_delayed_resume(void *data);
 
 static char ns_read_reg(struct ns16550 *uart, int reg)
@@ -134,7 +167,7 @@ static void ns16550_interrupt(
     while ( !(ns_read_reg(uart, UART_IIR) & UART_IIR_NOINT) )
     {
         char lsr = ns_read_reg(uart, UART_LSR);
-        if ( lsr & UART_LSR_THRE )
+        if ( (lsr & uart->lsr_mask) == uart->lsr_mask ) 
             serial_tx_interrupt(port, regs);
         if ( lsr & UART_LSR_DR )
             serial_rx_interrupt(port, regs);
@@ -160,7 +193,7 @@ static void __ns16550_poll(struct cpu_user_regs *regs)
         serial_rx_interrupt(port, regs);
     }
 
-    if ( ns_read_reg(uart, UART_LSR) & UART_LSR_THRE )
+    if ( ( ns_read_reg(uart, UART_LSR) & uart->lsr_mask ) == uart->lsr_mask )
         serial_tx_interrupt(port, regs);
 
 out:
@@ -183,7 +216,9 @@ static int ns16550_tx_ready(struct serial_port *port)
 
     if ( ns16550_ioport_invalid(uart) )
         return -EIO;
-    return ns_read_reg(uart, UART_LSR) & UART_LSR_THRE ? uart->fifo_size : 0;
+	
+    return ( (ns_read_reg(uart, UART_LSR) & 
+              uart->lsr_mask ) == uart->lsr_mask ) ? uart->fifo_size : 0;
 }
 
 static void ns16550_putc(struct serial_port *port, char c)
@@ -381,6 +416,13 @@ static void _ns16550_resume(struct serial_port *port)
     {
        pci_conf_write32(0, uart->ps_bdf[0], uart->ps_bdf[1], uart->ps_bdf[2],
                         PCI_BASE_ADDRESS_0 + uart->bar_idx*4, uart->bar);
+
+        /* If 64 bit BAR, write higher 32 bits to BAR+4 */
+        if ( uart->bar & PCI_BASE_ADDRESS_MEM_TYPE_64 )
+            pci_conf_write32(0, uart->ps_bdf[0], 
+                        uart->ps_bdf[1], uart->ps_bdf[2],
+                        PCI_BASE_ADDRESS_0 + (uart->bar_idx+1)*4, uart->bar64);
+
        pci_conf_write16(0, uart->ps_bdf[0], uart->ps_bdf[1], uart->ps_bdf[2],
                         PCI_COMMAND, uart->cr);
     }
@@ -432,9 +474,20 @@ static void __init ns16550_endboot(struct serial_port *port)
 {
 #ifdef HAS_IOPORTS
     struct ns16550 *uart = port->uart;
+    unsigned long sfn, efn;
 
     if ( uart->remapped_io_base )
+    {
+        sfn = PFN_UP((unsigned long) uart->io_base + (0x8 * (1 << uart->reg_shift)));
+        efn = PFN_DOWN((unsigned long) uart->io_base + uart->io_size - 1);
+        if ( sfn > efn )
+            BUG();
+
+        if ( iomem_deny_access(dom0, sfn, efn) != 0 )
+            BUG();
+
         return;
+    }
     if ( ioports_deny_access(dom0, uart->io_base, uart->io_base + 7) != 0 )
         BUG();
 #endif
@@ -546,11 +599,13 @@ static int __init check_existence(struct ns16550 *uart)
 }
 
 #ifdef HAS_PCI
-static int
+static int __init
 pci_uart_config (struct ns16550 *uart, int skip_amt, int bar_idx)
 {
-    uint32_t bar, len;
-    int b, d, f, nextf;
+    uint32_t bar, bar_64 = 0, len, len_64;
+    u64 size, mask;
+    unsigned int b, d, f, nextf, i;
+    u16 vendor, device;
 
     /* NB. Start at bus 1 to avoid AMT: a plug-in card cannot be on bus 0. */
     for ( b = skip_amt ? 1 : 0; b < 0x100; b++ )
@@ -579,24 +634,96 @@ pci_uart_config (struct ns16550 *uart, int skip_amt, int bar_idx)
                 bar = pci_conf_read32(0, b, d, f,
                                       PCI_BASE_ADDRESS_0 + bar_idx*4);
 
-                /* Not IO */
+                /* MMIO based */
                 if ( !(bar & PCI_BASE_ADDRESS_SPACE_IO) )
-                    continue;
-
-                pci_conf_write32(0, b, d, f, PCI_BASE_ADDRESS_0, ~0u);
-                len = pci_conf_read32(0, b, d, f, PCI_BASE_ADDRESS_0);
-                pci_conf_write32(0, b, d, f, PCI_BASE_ADDRESS_0 + bar_idx*4, bar);
-
-                /* Not 8 bytes */
-                if ( (len & 0xffff) != 0xfff9 )
-                    continue;
+                {
+                    vendor = pci_conf_read16(0, b, d, f, PCI_VENDOR_ID);
+                    device = pci_conf_read16(0, b, d, f, PCI_DEVICE_ID);
+
+                    pci_conf_write32(0, b, d, f, 
+                                     PCI_BASE_ADDRESS_0 + bar_idx*4, ~0u);
+                    len = pci_conf_read32(0, b, d, f, PCI_BASE_ADDRESS_0 + bar_idx*4);
+                    pci_conf_write32(0, b, d, f, 
+                                     PCI_BASE_ADDRESS_0 + bar_idx*4, bar);
+
+                    /* Handle 64 bit BAR if found */ 
+                    if ( bar & PCI_BASE_ADDRESS_MEM_TYPE_64 )
+                    {
+                        bar_64 = pci_conf_read32(0, b, d, f,
+                                      PCI_BASE_ADDRESS_0 + (bar_idx+1)*4);
+                        pci_conf_write32(0, b, d, f,
+                                    PCI_BASE_ADDRESS_0 + (bar_idx+1)*4, ~0u);
+                        len_64 = pci_conf_read32(0, b, d, f,
+                                    PCI_BASE_ADDRESS_0 + (bar_idx+1)*4);
+                        pci_conf_write32(0, b, d, f,
+                                    PCI_BASE_ADDRESS_0 + (bar_idx+1)*4, bar_64);
+                        mask = ((u64)~0 << 32) | PCI_BASE_ADDRESS_MEM_MASK;
+                        size = (((u64)len_64 << 32) | len) & mask;
+                    }
+                    else
+                        size = len & PCI_BASE_ADDRESS_MEM_MASK;
+
+                    size &= -(size);
+
+                    /* Check for quirks in uart_config lookup table */
+                    for ( i = 0; i < ARRAY_SIZE(uart_config); i++)
+                    {
+                        if ( uart_config[i].vendor_id != vendor )
+                            continue;
+
+                        if ( uart_config[i].dev_id != device )
+                            continue;
+
+                        /* 
+                         * Force length of mmio region to be at least 
+                         * 8 bytes times (1 << reg_shift)
+                         */
+                        if ( size < (0x8 * (1 << uart_config[i].reg_shift)) )
+                            continue;
+
+                        if ( bar_idx >= uart_config[i].max_bars )
+                            continue;
+
+                        if ( uart_config[i].fifo_size )
+                            uart->fifo_size = uart_config[i].fifo_size;
+
+                        uart->reg_shift = uart_config[i].reg_shift;
+                        uart->reg_width = uart_config[i].reg_width;
+                        uart->lsr_mask = uart_config[i].lsr_mask;
+                        uart->io_base = ((u64)bar_64 << 32) |
+                                        (bar & PCI_BASE_ADDRESS_MEM_MASK);
+                        break;
+                    }
+
+                    /* If we have an io_base, then we succeeded in the lookup */
+                    if ( !uart->io_base )
+                        continue;
+                }
+                /* IO based */
+                else
+                {
+                    pci_conf_write32(0, b, d, f, 
+                                     PCI_BASE_ADDRESS_0 + bar_idx*4, ~0u);
+                    len = pci_conf_read32(0, b, d, f, PCI_BASE_ADDRESS_0);
+                    pci_conf_write32(0, b, d, f, 
+                                     PCI_BASE_ADDRESS_0 + bar_idx*4, bar);
+                    size = len & PCI_BASE_ADDRESS_IO_MASK;
+                    size &= -(size);
+
+                    /* Not 8 bytes */
+                    if ( size != 0x8 )
+                        continue;
+
+                    uart->io_base = bar & ~PCI_BASE_ADDRESS_SPACE_IO;
+                }
 
                 uart->ps_bdf[0] = b;
                 uart->ps_bdf[1] = d;
                 uart->ps_bdf[2] = f;
-                uart->bar = bar;
                 uart->bar_idx = bar_idx;
-                uart->io_base = bar & ~PCI_BASE_ADDRESS_SPACE_IO;
+                uart->bar = bar;
+                uart->bar64 = bar_64;
+                uart->io_size = size;
                 uart->irq = pci_conf_read8(0, b, d, f, PCI_INTERRUPT_PIN) ?
                     pci_conf_read8(0, b, d, f, PCI_INTERRUPT_LINE) : 0;
 
@@ -746,6 +873,9 @@ void __init ns16550_init(int index, struct ns16550_defaults *defaults)
     /* Default is no transmit FIFO. */
     uart->fifo_size = 1;
 
+    /* Default lsr_mask = UART_LSR_THRE */
+    uart->lsr_mask = UART_LSR_THRE;
+
     ns16550_parse_port_config(uart, (index == 0) ? opt_com1 : opt_com2);
 }
 
-- 
1.8.1.2

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH V5] ns16550: Add support for UART present in Broadcom TruManage capable NetXtreme chips
  2013-11-22 16:45 [PATCH V5] ns16550: Add support for UART present in Broadcom TruManage capable NetXtreme chips Aravind Gopalakrishnan
@ 2013-11-22 16:59 ` Jan Beulich
  2013-11-22 18:52   ` Tom Lendacky
  0 siblings, 1 reply; 4+ messages in thread
From: Jan Beulich @ 2013-11-22 16:59 UTC (permalink / raw)
  To: Aravind Gopalakrishnan, xen-devel
  Cc: Thomas Lendacky, andrew.cooper3, shurd, Suravee Suthikulpanit,
	sherry.hurwitz

>>> On 22.11.13 at 17:45, Aravind Gopalakrishnan <Aravind.Gopalakrishnan@amd.com> wrote:
> @@ -432,9 +474,20 @@ static void __init ns16550_endboot(struct serial_port *port)
>  {
>  #ifdef HAS_IOPORTS
>      struct ns16550 *uart = port->uart;
> +    unsigned long sfn, efn;
>  
>      if ( uart->remapped_io_base )
> +    {
> +        sfn = PFN_UP((unsigned long) uart->io_base + (0x8 * (1 << uart->reg_shift)));
> +        efn = PFN_DOWN((unsigned long) uart->io_base + uart->io_size - 1);

So I told you on the previous round that these casts are wrong.
Why did you keep them.

Also, I don't see why you adjust io_base above - you want to cover
the full BAR, no matter what the register shift.

> +        if ( sfn > efn )
> +            BUG();

Ehm - what? Without taking the specifics of BARs into account, this
can validly happen (which is why I commented on it on the previous
version). But now that I remembered that the value comes from a
BAR, it can't happen, and hence you either don't check it at all, or
ASSERT() rather than BUG().

> +                    else
> +                        size = len & PCI_BASE_ADDRESS_MEM_MASK;
> +
> +                    size &= -(size);

Stray parentheses.

> +                        /* 
> +                         * Force length of mmio region to be at least 
> +                         * 8 bytes times (1 << reg_shift)
> +                         */
> +                        if ( size < (0x8 * (1 << uart_config[i].reg_shift)) )

                        if ( size < (8 << uart_config[i].reg_shift) )

Jan

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH V5] ns16550: Add support for UART present in Broadcom TruManage capable NetXtreme chips
  2013-11-22 16:59 ` Jan Beulich
@ 2013-11-22 18:52   ` Tom Lendacky
  2013-11-25  9:25     ` Jan Beulich
  0 siblings, 1 reply; 4+ messages in thread
From: Tom Lendacky @ 2013-11-22 18:52 UTC (permalink / raw)
  To: Jan Beulich
  Cc: andrew.cooper3, xen-devel, Aravind Gopalakrishnan,
	Suravee Suthikulpanit, sherry.hurwitz, shurd


On Friday, November 22, 2013 04:59:21 PM Jan Beulich wrote:
> >>> On 22.11.13 at 17:45, Aravind Gopalakrishnan 
<Aravind.Gopalakrishnan@amd.com> wrote:
> > @@ -432,9 +474,20 @@ static void __init ns16550_endboot(struct serial_port
> > *port)> 
> >  {
> >  #ifdef HAS_IOPORTS
> >  
> >      struct ns16550 *uart = port->uart;
> > 
> > +    unsigned long sfn, efn;
> > 
> >      if ( uart->remapped_io_base )
> > 
> > +    {
> > +        sfn = PFN_UP((unsigned long) uart->io_base + (0x8 * (1 <<
> > uart->reg_shift))); +        efn = PFN_DOWN((unsigned long) uart->io_base
> > + uart->io_size - 1);
> So I told you on the previous round that these casts are wrong.
> Why did you keep them.
> 
> Also, I don't see why you adjust io_base above - you want to cover
> the full BAR, no matter what the register shift.

This is one of the situations that Aravind mentioned previously. If the
full BAR is covered then Dom0 fails to boot/come up since it sees the device
(02:00.5 below), tries to access/configure it and fails.

02:00.0 Ethernet controller [0200]: Broadcom Corporation NetXtreme
BCM5725 Gigabit Ethernet PCIe [14e4:1643] (rev 10)
02:00.5 Communication controller [0780]: Broadcom Corporation Device 
[14e4:160a] (rev 01)
02:00.6 IPMI SMIC interface [0c07]: Broadcom Corporation Device [14e4:16b9] 
(rev 10)

I'm not sure what the options are here since we can't hide the device
from Dom0 to prevent it from trying to access/configure the device and
restricting the full BAR causes Dom0 to fail.

Thoughts?

Tom

> 
> > +        if ( sfn > efn )
> > +            BUG();
> 
> Ehm - what? Without taking the specifics of BARs into account, this
> can validly happen (which is why I commented on it on the previous
> version). But now that I remembered that the value comes from a
> BAR, it can't happen, and hence you either don't check it at all, or
> ASSERT() rather than BUG().
> 
> > +                    else
> > +                        size = len & PCI_BASE_ADDRESS_MEM_MASK;
> > +
> > +                    size &= -(size);
> 
> Stray parentheses.
> 
> > +                        /*
> > +                         * Force length of mmio region to be at least
> > +                         * 8 bytes times (1 << reg_shift)
> > +                         */
> > +                        if ( size < (0x8 * (1 <<
> > uart_config[i].reg_shift)) )
>                         if ( size < (8 << uart_config[i].reg_shift) )
> 
> Jan
-- 
Tom

Thomas Lendacky
Advanced Micro Devices, Inc.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH V5] ns16550: Add support for UART present in Broadcom TruManage capable NetXtreme chips
  2013-11-22 18:52   ` Tom Lendacky
@ 2013-11-25  9:25     ` Jan Beulich
  0 siblings, 0 replies; 4+ messages in thread
From: Jan Beulich @ 2013-11-25  9:25 UTC (permalink / raw)
  To: Tom Lendacky
  Cc: andrew.cooper3, xen-devel, Aravind Gopalakrishnan,
	Suravee Suthikulpanit, sherry.hurwitz, shurd

>>> On 22.11.13 at 19:52, Tom Lendacky <thomas.lendacky@amd.com> wrote:

> On Friday, November 22, 2013 04:59:21 PM Jan Beulich wrote:
>> >>> On 22.11.13 at 17:45, Aravind Gopalakrishnan 
> <Aravind.Gopalakrishnan@amd.com> wrote:
>> > @@ -432,9 +474,20 @@ static void __init ns16550_endboot(struct serial_port
>> > *port)> 
>> >  {
>> >  #ifdef HAS_IOPORTS
>> >  
>> >      struct ns16550 *uart = port->uart;
>> > 
>> > +    unsigned long sfn, efn;
>> > 
>> >      if ( uart->remapped_io_base )
>> > 
>> > +    {
>> > +        sfn = PFN_UP((unsigned long) uart->io_base + (0x8 * (1 <<
>> > uart->reg_shift))); +        efn = PFN_DOWN((unsigned long) uart->io_base
>> > + uart->io_size - 1);
>> So I told you on the previous round that these casts are wrong.
>> Why did you keep them.
>> 
>> Also, I don't see why you adjust io_base above - you want to cover
>> the full BAR, no matter what the register shift.
> 
> This is one of the situations that Aravind mentioned previously. If the
> full BAR is covered then Dom0 fails to boot/come up since it sees the device
> (02:00.5 below), tries to access/configure it and fails.
> 
> 02:00.0 Ethernet controller [0200]: Broadcom Corporation NetXtreme
> BCM5725 Gigabit Ethernet PCIe [14e4:1643] (rev 10)
> 02:00.5 Communication controller [0780]: Broadcom Corporation Device 
> [14e4:160a] (rev 01)
> 02:00.6 IPMI SMIC interface [0c07]: Broadcom Corporation Device [14e4:16b9] 
> (rev 10)
> 
> I'm not sure what the options are here since we can't hide the device
> from Dom0 to prevent it from trying to access/configure the device and
> restricting the full BAR causes Dom0 to fail.

Once again - if Dom0 gets as far as accessing the MMIO region, it
would already conflict with Xen's use of it (perhaps reads would be
okay, provided they have no side effects, but writes certainly aren't).
So a writable mapping must be avoided by all means. Which ought to
be doable by not only making the device a r/o one, but also adding
the MMIO range to mmio_ro_ranges (instead of outright denying
Dom0 access).

Jan

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2013-11-25  9:25 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-11-22 16:45 [PATCH V5] ns16550: Add support for UART present in Broadcom TruManage capable NetXtreme chips Aravind Gopalakrishnan
2013-11-22 16:59 ` Jan Beulich
2013-11-22 18:52   ` Tom Lendacky
2013-11-25  9:25     ` Jan Beulich

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).