lkml.org 
[lkml]   [2022]   [Feb]   [26]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subject[PATCH] PCI: Avoid handing out address 0 to devices
We have numerous platforms that permit assigning addresses from 0 to PCI 
devices, both in the memory and the I/O bus space, and we happily do so
if there is no conflict, e.g.:

pci 0000:07:00.0: BAR 0: assigned [io 0x0000-0x0007]
pci 0000:07:00.1: BAR 0: assigned [io 0x0008-0x000f]
pci 0000:06:01.0: PCI bridge to [bus 07]
pci 0000:06:01.0: bridge window [io 0x0000-0x0fff]

(with the SiFive HiFive Unmatched RISC-V board and a dual serial port
option card based on the OxSemi OXPCIe952 device wired for the legacy
UART mode).

Address 0 is treated specially however in many places, for example in
`pci_iomap_range' and `pci_iomap_wc_range' we require that the start
address is non-zero, and even if we let such an address through, then
individual device drivers could reject a request to handle a device at
such an address, such as in `uart_configure_port'. Consequently given
devices configured as shown above only one is actually usable:

Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
serial 0000:07:00.0: enabling device (0000 -> 0001)
serial: probe of 0000:07:00.0 failed with error -12
serial 0000:07:00.1: enabling device (0000 -> 0001)
serial 0000:07:00.1: detected caps 00000700 should be 00000500
0000:07:00.1: ttyS0 at I/O 0x8 (irq = 39, base_baud = 15625000) is a 16C950/954

Especially I/O space ranges are particularly valuable, because bridges
only decode bits from 12 up and consequently where 16-bit addressing is
in effect, as few as 16 separate ranges can be assigned to individual
buses only.

Therefore avoid handing out address 0, however rather than bumping the
lowest address available to PCI via PCIBIOS_MIN_IO and PCIBIOS_MIN_MEM,
or doing an equivalent arrangement in `__pci_assign_resource', let the
whole range assigned to a bus start from that address and instead only
avoid it for actual devices. Do it in `pci_bus_alloc_from_region' then
observing that bridge resources will have the IORESOURCE_STARTALIGN flag
set rather than IORESOURCE_SIZEALIGN and by making the least significant
bit decoded 1 according to the resource type, either memory or I/O.

With this in place the system in question we have:

pci 0000:07:00.0: BAR 0: assigned [io 0x0008-0x000f]
pci 0000:07:00.1: BAR 0: assigned [io 0x0010-0x0017]
pci 0000:06:01.0: PCI bridge to [bus 07]
pci 0000:06:01.0: bridge window [io 0x0000-0x0fff]

and then devices work correctly:

Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
serial 0000:07:00.0: enabling device (0000 -> 0001)
serial 0000:07:00.0: detected caps 00000700 should be 00000500
0000:07:00.0: ttyS0 at I/O 0x8 (irq = 38, base_baud = 15625000) is a 16C950/954
serial 0000:07:00.1: enabling device (0000 -> 0001)
serial 0000:07:00.1: detected caps 00000700 should be 00000500
0000:07:00.1: ttyS1 at I/O 0x10 (irq = 39, base_baud = 15625000) is a 16C950/954

Signed-off-by: Maciej W. Rozycki <macro@orcam.me.uk>
---
Hi,

NB I have an OxSemi OXPCIe952 based card that can be wired to either the
native or the legacy mode via a jumper block and I am so glad that I have
checked whether it works in the legacy mode as well. I guess there are so
few legacy-free platforms still for nobody else to notice this issue yet.

I think I've chosen the right solution, but I'll be happy to hear any
suggestions for an alternative one. Otherwise please apply.

Maciej
---
drivers/pci/bus.c | 9 +++++++++
1 file changed, 9 insertions(+)

linux-pci-bus-alloc-from-region-min.diff
Index: linux-macro/drivers/pci/bus.c
===================================================================
--- linux-macro.orig/drivers/pci/bus.c
+++ linux-macro/drivers/pci/bus.c
@@ -194,6 +194,15 @@ static int pci_bus_alloc_from_region(str
*/
if (avail.start)
min_used = avail.start;
+ /*
+ * For non-bridge resources avoid assigning address 0 as
+ * we assume that to mean no assignment in many places,
+ * starting from `pci_iomap_range'.
+ */
+ if (min_used == 0 && (res->flags & IORESOURCE_SIZEALIGN))
+ min_used = res->flags & IORESOURCE_IO ?
+ ~PCI_BASE_ADDRESS_IO_MASK + 1 :
+ ~PCI_BASE_ADDRESS_MEM_MASK + 1;

max = avail.end;

\
 
 \ /
  Last update: 2022-02-26 11:48    [W:0.336 / U:0.008 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site