Skip to content

Commit 398a9db

Browse files
Aaron Sierragregkh
authored andcommitted
serial: 8250_pci: Handle devices mapped above 4 GiB
Several init/setup functions passed the PCI BAR resource start address to ioremap_nocache() via an unsigned long. This caused address truncation for a 32-bit device mapped above 4 GiB (i.e. the CPU interacts with the device via a translated address), which resulted in a kernel panic. This patch replaces all of the instances of intermediate variable use with pci_ioremap_bar() to ensure the full resource_size_t start address is used and that ioremap_nocache() is still called. The kernel panic (Exar XR17V358 PCIe device on a Freescale P2020 SBC): Machine check in kernel mode. Caused by (from MCSR=10008): Bus - Read Data Bus Error Oops: Machine check, sig: 7 [#1] SMP NR_CPUS=2 X-ES P2020 Modules linked in: CPU: 1 PID: 1 Comm: swapper/0 Not tainted 3.14.15-xes_r2-00002-g560e401 #978 task: bf850000 ti: bffee000 task.ti: bf84c000 NIP: 80318e10 LR: 80319ecc CTR: 80318dfc REGS: bffeff10 TRAP: 0204 Not tainted (3.14.15-xes_r2-00002-g560e401) MSR: 00021000 <CE,ME> CR: 20adbe42 XER: 00000000 DEAR: c1058001 ESR: 00000000 GPR00: 00000000 bf84db30 bf850000 80cb4af8 00000001 00000000 80000007 80000000 GPR08: bf837c9c c1058001 00000001 00000000 80000007 00000000 80002a10 00000000 GPR16: 00000000 00000000 00000000 00000000 00000000 00000000 80cb0000 80c72dc4 GPR24: 80cb4900 fffffffe 00029000 00000001 bf8c11e8 ffffffea 80c72ce4 80cb4af8 NIP [80318e10] mem_serial_in+0x14/0x28 LR [80319ecc] serial8250_config_port+0x160/0xe38 Call Trace: [bf84db30] [80319d94] serial8250_config_port+0x28/0xe38 (unreliable) [bf84db60] [80315e3c] uart_add_one_port+0x148/0x3a4 [bf84dbf0] [8031bf40] serial8250_register_8250_port+0x2dc/0x3c8 [bf84dc20] [8032111c] pciserial_init_ports+0xd4/0x1c0 [bf84dd50] [803212f8] pciserial_init_one+0xf0/0x224 [bf84dd90] [802d8ff4] local_pci_probe+0x34/0x8c [bf84dda0] [802d92c8] pci_device_probe+0x84/0xa0 [bf84ddc0] [80329ee0] driver_probe_device+0xac/0x26c [bf84dde0] [8032a15c] __driver_attach+0xbc/0xc0 [bf84de00] [80328388] bus_for_each_dev+0x90/0xcc [bf84de30] [80329cd0] driver_attach+0x24/0x34 [bf84de40] [80328e28] bus_add_driver+0x104/0x1fc [bf84de60] [8032a8c8] driver_register+0x70/0x138 [bf84de70] [802d93c0] __pci_register_driver+0x48/0x58 [bf84de80] [8077e0e4] serial_pci_driver_init+0x24/0x34 [bf84de90] [80002228] do_one_initcall+0x34/0x1b0 [bf84df00] [80764294] kernel_init_freeable+0x138/0x1e8 [bf84df30] [80002a24] kernel_init+0x14/0x108 [bf84df40] [8000ef94] ret_from_kernel_thread+0x5c/0x64 Instruction dump: 800800c4 7d290214 39290001 7c0004ac 7ca049ae 7c0004ac 4e800020 88030035 81230008 7c840030 7d292214 7c0004ac <88690000> 0c030000 4c00012c 5463063e ---[ end trace e3c16443b5d573c6 ]--- Signed-off-by: Aaron Sierra <asierra@xes-inc.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 08de101 commit 398a9db

File tree

1 file changed

+17
-30
lines changed

1 file changed

+17
-30
lines changed

drivers/tty/serial/8250/8250_pci.c

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -79,29 +79,24 @@ setup_port(struct serial_private *priv, struct uart_8250_port *port,
7979
int bar, int offset, int regshift)
8080
{
8181
struct pci_dev *dev = priv->dev;
82-
unsigned long base, len;
8382

8483
if (bar >= PCI_NUM_BAR_RESOURCES)
8584
return -EINVAL;
8685

87-
base = pci_resource_start(dev, bar);
88-
8986
if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) {
90-
len = pci_resource_len(dev, bar);
91-
9287
if (!priv->remapped_bar[bar])
93-
priv->remapped_bar[bar] = ioremap_nocache(base, len);
88+
priv->remapped_bar[bar] = pci_ioremap_bar(dev, bar);
9489
if (!priv->remapped_bar[bar])
9590
return -ENOMEM;
9691

9792
port->port.iotype = UPIO_MEM;
9893
port->port.iobase = 0;
99-
port->port.mapbase = base + offset;
94+
port->port.mapbase = pci_resource_start(dev, bar) + offset;
10095
port->port.membase = priv->remapped_bar[bar] + offset;
10196
port->port.regshift = regshift;
10297
} else {
10398
port->port.iotype = UPIO_PORT;
104-
port->port.iobase = base + offset;
99+
port->port.iobase = pci_resource_start(dev, bar) + offset;
105100
port->port.mapbase = 0;
106101
port->port.membase = NULL;
107102
port->port.regshift = 0;
@@ -317,17 +312,14 @@ static void pci_plx9050_exit(struct pci_dev *dev)
317312
static void pci_ni8420_exit(struct pci_dev *dev)
318313
{
319314
void __iomem *p;
320-
unsigned long base, len;
321315
unsigned int bar = 0;
322316

323317
if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) {
324318
moan_device("no memory in bar", dev);
325319
return;
326320
}
327321

328-
base = pci_resource_start(dev, bar);
329-
len = pci_resource_len(dev, bar);
330-
p = ioremap_nocache(base, len);
322+
p = pci_ioremap_bar(dev, bar);
331323
if (p == NULL)
332324
return;
333325

@@ -349,17 +341,14 @@ static void pci_ni8420_exit(struct pci_dev *dev)
349341
static void pci_ni8430_exit(struct pci_dev *dev)
350342
{
351343
void __iomem *p;
352-
unsigned long base, len;
353344
unsigned int bar = 0;
354345

355346
if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) {
356347
moan_device("no memory in bar", dev);
357348
return;
358349
}
359350

360-
base = pci_resource_start(dev, bar);
361-
len = pci_resource_len(dev, bar);
362-
p = ioremap_nocache(base, len);
351+
p = pci_ioremap_bar(dev, bar);
363352
if (p == NULL)
364353
return;
365354

@@ -682,17 +671,14 @@ static int pci_xircom_init(struct pci_dev *dev)
682671
static int pci_ni8420_init(struct pci_dev *dev)
683672
{
684673
void __iomem *p;
685-
unsigned long base, len;
686674
unsigned int bar = 0;
687675

688676
if ((pci_resource_flags(dev, bar) & IORESOURCE_MEM) == 0) {
689677
moan_device("no memory in bar", dev);
690678
return 0;
691679
}
692680

693-
base = pci_resource_start(dev, bar);
694-
len = pci_resource_len(dev, bar);
695-
p = ioremap_nocache(base, len);
681+
p = pci_ioremap_bar(dev, bar);
696682
if (p == NULL)
697683
return -ENOMEM;
698684

@@ -714,7 +700,7 @@ static int pci_ni8420_init(struct pci_dev *dev)
714700
static int pci_ni8430_init(struct pci_dev *dev)
715701
{
716702
void __iomem *p;
717-
unsigned long base, len;
703+
struct pci_bus_region region;
718704
u32 device_window;
719705
unsigned int bar = 0;
720706

@@ -723,14 +709,17 @@ static int pci_ni8430_init(struct pci_dev *dev)
723709
return 0;
724710
}
725711

726-
base = pci_resource_start(dev, bar);
727-
len = pci_resource_len(dev, bar);
728-
p = ioremap_nocache(base, len);
712+
p = pci_ioremap_bar(dev, bar);
729713
if (p == NULL)
730714
return -ENOMEM;
731715

732-
/* Set device window address and size in BAR0 */
733-
device_window = ((base + MITE_IOWBSR1_WIN_OFFSET) & 0xffffff00)
716+
/*
717+
* Set device window address and size in BAR0, while acknowledging that
718+
* the resource structure may contain a translated address that differs
719+
* from the address the device responds to.
720+
*/
721+
pcibios_resource_to_bus(dev->bus, &region, &dev->resource[bar]);
722+
device_window = ((region.start + MITE_IOWBSR1_WIN_OFFSET) & 0xffffff00)
734723
| MITE_IOWBSR1_WENAB | MITE_IOWBSR1_WSIZE;
735724
writel(device_window, p + MITE_IOWBSR1);
736725

@@ -757,8 +746,8 @@ pci_ni8430_setup(struct serial_private *priv,
757746
const struct pciserial_board *board,
758747
struct uart_8250_port *port, int idx)
759748
{
749+
struct pci_dev *dev = priv->dev;
760750
void __iomem *p;
761-
unsigned long base, len;
762751
unsigned int bar, offset = board->first_offset;
763752

764753
if (idx >= board->num_ports)
@@ -767,9 +756,7 @@ pci_ni8430_setup(struct serial_private *priv,
767756
bar = FL_GET_BASE(board->flags);
768757
offset += idx * board->uart_offset;
769758

770-
base = pci_resource_start(priv->dev, bar);
771-
len = pci_resource_len(priv->dev, bar);
772-
p = ioremap_nocache(base, len);
759+
p = pci_ioremap_bar(dev, bar);
773760

774761
/* enable the transceiver */
775762
writeb(readb(p + offset + NI8430_PORTCON) | NI8430_PORTCON_TXVR_ENABLE,

0 commit comments

Comments
 (0)