diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2006-12-08 01:14:33 -0500 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2006-12-08 01:21:06 -0500 |
commit | 396a1a5832ae28ce2c4150f98827873cbef554f5 (patch) | |
tree | 26d72bdf3765184f64e6231e7962152272584401 | |
parent | f09b5ce0184da6a83bac7fafda4e624629272b37 (diff) |
[POWERPC] Fix mmap of PCI resource with hack for X
The powerpc version of pci_resource_to_user() and associated hooks
used by /proc/bus/pci and /sys/bus/pci mmap have been broken for some
time on machines that don't have a 1:1 mapping of devices (basically
on non-PowerMacs) and have PCI devices above 32 bits.
This attempts to fix it as well as possible.
The rule is supposed to be that pci_resource_to_user() always converts
the resources back into a BAR values since that's what the /proc
interface was supposed to deal with. However, for X to work on
platforms where PCI MMIO is not mapped 1:1, it became a habit of
platforms like powerpc to pass "fixed up" values there since X expects
to be able to use values from /proc/bus/pci/devices as offsets to mmap
of /dev/mem...
So we keep that contraption here, causing also /sys/*/resource to
expose fully absolute MMIO addresses instead of BAR values, which is
ugly, but should still work as long as those are only used to calculate
alignment within a page.
X is still broken when built 32 bits on machines where PCI MMIO can be
above 32-bit space unfortunately.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r-- | arch/powerpc/kernel/pci_32.c | 45 | ||||
-rw-r--r-- | arch/powerpc/kernel/pci_64.c | 42 | ||||
-rw-r--r-- | arch/ppc/kernel/pci.c | 41 | ||||
-rw-r--r-- | include/asm-powerpc/pci-bridge.h | 4 | ||||
-rw-r--r-- | include/asm-ppc/pci-bridge.h | 8 |
5 files changed, 101 insertions, 39 deletions
diff --git a/arch/powerpc/kernel/pci_32.c b/arch/powerpc/kernel/pci_32.c index 2f54cd81dea5..ab5887bff02a 100644 --- a/arch/powerpc/kernel/pci_32.c +++ b/arch/powerpc/kernel/pci_32.c | |||
@@ -1544,7 +1544,7 @@ pci_resource_to_bus(struct pci_dev *pdev, struct resource *res) | |||
1544 | 1544 | ||
1545 | 1545 | ||
1546 | static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, | 1546 | static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, |
1547 | unsigned long *offset, | 1547 | resource_size_t *offset, |
1548 | enum pci_mmap_state mmap_state) | 1548 | enum pci_mmap_state mmap_state) |
1549 | { | 1549 | { |
1550 | struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); | 1550 | struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); |
@@ -1556,7 +1556,9 @@ static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, | |||
1556 | 1556 | ||
1557 | /* If memory, add on the PCI bridge address offset */ | 1557 | /* If memory, add on the PCI bridge address offset */ |
1558 | if (mmap_state == pci_mmap_mem) { | 1558 | if (mmap_state == pci_mmap_mem) { |
1559 | #if 0 /* See comment in pci_resource_to_user() for why this is disabled */ | ||
1559 | *offset += hose->pci_mem_offset; | 1560 | *offset += hose->pci_mem_offset; |
1561 | #endif | ||
1560 | res_bit = IORESOURCE_MEM; | 1562 | res_bit = IORESOURCE_MEM; |
1561 | } else { | 1563 | } else { |
1562 | io_offset = hose->io_base_virt - (void __iomem *)_IO_BASE; | 1564 | io_offset = hose->io_base_virt - (void __iomem *)_IO_BASE; |
@@ -1624,9 +1626,6 @@ static pgprot_t __pci_mmap_set_pgprot(struct pci_dev *dev, struct resource *rp, | |||
1624 | else | 1626 | else |
1625 | prot |= _PAGE_GUARDED; | 1627 | prot |= _PAGE_GUARDED; |
1626 | 1628 | ||
1627 | printk("PCI map for %s:%llx, prot: %lx\n", pci_name(dev), | ||
1628 | (unsigned long long)rp->start, prot); | ||
1629 | |||
1630 | return __pgprot(prot); | 1629 | return __pgprot(prot); |
1631 | } | 1630 | } |
1632 | 1631 | ||
@@ -1695,7 +1694,7 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, | |||
1695 | enum pci_mmap_state mmap_state, | 1694 | enum pci_mmap_state mmap_state, |
1696 | int write_combine) | 1695 | int write_combine) |
1697 | { | 1696 | { |
1698 | unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; | 1697 | resource_size_t offset = vma->vm_pgoff << PAGE_SHIFT; |
1699 | struct resource *rp; | 1698 | struct resource *rp; |
1700 | int ret; | 1699 | int ret; |
1701 | 1700 | ||
@@ -1808,22 +1807,42 @@ void pci_resource_to_user(const struct pci_dev *dev, int bar, | |||
1808 | resource_size_t *start, resource_size_t *end) | 1807 | resource_size_t *start, resource_size_t *end) |
1809 | { | 1808 | { |
1810 | struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); | 1809 | struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); |
1811 | unsigned long offset = 0; | 1810 | resource_size_t offset = 0; |
1812 | 1811 | ||
1813 | if (hose == NULL) | 1812 | if (hose == NULL) |
1814 | return; | 1813 | return; |
1815 | 1814 | ||
1816 | if (rsrc->flags & IORESOURCE_IO) | 1815 | if (rsrc->flags & IORESOURCE_IO) |
1817 | offset = (void __iomem *)_IO_BASE - hose->io_base_virt | 1816 | offset = (unsigned long)hose->io_base_virt - _IO_BASE; |
1818 | + hose->io_base_phys; | 1817 | |
1818 | /* We pass a fully fixed up address to userland for MMIO instead of | ||
1819 | * a BAR value because X is lame and expects to be able to use that | ||
1820 | * to pass to /dev/mem ! | ||
1821 | * | ||
1822 | * That means that we'll have potentially 64 bits values where some | ||
1823 | * userland apps only expect 32 (like X itself since it thinks only | ||
1824 | * Sparc has 64 bits MMIO) but if we don't do that, we break it on | ||
1825 | * 32 bits CHRPs :-( | ||
1826 | * | ||
1827 | * Hopefully, the sysfs insterface is immune to that gunk. Once X | ||
1828 | * has been fixed (and the fix spread enough), we can re-enable the | ||
1829 | * 2 lines below and pass down a BAR value to userland. In that case | ||
1830 | * we'll also have to re-enable the matching code in | ||
1831 | * __pci_mmap_make_offset(). | ||
1832 | * | ||
1833 | * BenH. | ||
1834 | */ | ||
1835 | #if 0 | ||
1836 | else if (rsrc->flags & IORESOURCE_MEM) | ||
1837 | offset = hose->pci_mem_offset; | ||
1838 | #endif | ||
1819 | 1839 | ||
1820 | *start = rsrc->start + offset; | 1840 | *start = rsrc->start - offset; |
1821 | *end = rsrc->end + offset; | 1841 | *end = rsrc->end - offset; |
1822 | } | 1842 | } |
1823 | 1843 | ||
1824 | void __init | 1844 | void __init pci_init_resource(struct resource *res, resource_size_t start, |
1825 | pci_init_resource(struct resource *res, unsigned long start, unsigned long end, | 1845 | resource_size_t end, int flags, char *name) |
1826 | int flags, char *name) | ||
1827 | { | 1846 | { |
1828 | res->start = start; | 1847 | res->start = start; |
1829 | res->end = end; | 1848 | res->end = end; |
diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c index 6fa9a0a5c8db..a6b7692c7269 100644 --- a/arch/powerpc/kernel/pci_64.c +++ b/arch/powerpc/kernel/pci_64.c | |||
@@ -682,7 +682,7 @@ int pci_proc_domain(struct pci_bus *bus) | |||
682 | * Returns negative error code on failure, zero on success. | 682 | * Returns negative error code on failure, zero on success. |
683 | */ | 683 | */ |
684 | static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, | 684 | static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, |
685 | unsigned long *offset, | 685 | resource_size_t *offset, |
686 | enum pci_mmap_state mmap_state) | 686 | enum pci_mmap_state mmap_state) |
687 | { | 687 | { |
688 | struct pci_controller *hose = pci_bus_to_host(dev->bus); | 688 | struct pci_controller *hose = pci_bus_to_host(dev->bus); |
@@ -694,7 +694,9 @@ static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, | |||
694 | 694 | ||
695 | /* If memory, add on the PCI bridge address offset */ | 695 | /* If memory, add on the PCI bridge address offset */ |
696 | if (mmap_state == pci_mmap_mem) { | 696 | if (mmap_state == pci_mmap_mem) { |
697 | #if 0 /* See comment in pci_resource_to_user() for why this is disabled */ | ||
697 | *offset += hose->pci_mem_offset; | 698 | *offset += hose->pci_mem_offset; |
699 | #endif | ||
698 | res_bit = IORESOURCE_MEM; | 700 | res_bit = IORESOURCE_MEM; |
699 | } else { | 701 | } else { |
700 | io_offset = (unsigned long)hose->io_base_virt - pci_io_base; | 702 | io_offset = (unsigned long)hose->io_base_virt - pci_io_base; |
@@ -762,9 +764,6 @@ static pgprot_t __pci_mmap_set_pgprot(struct pci_dev *dev, struct resource *rp, | |||
762 | else | 764 | else |
763 | prot |= _PAGE_GUARDED; | 765 | prot |= _PAGE_GUARDED; |
764 | 766 | ||
765 | printk(KERN_DEBUG "PCI map for %s:%lx, prot: %lx\n", pci_name(dev), rp->start, | ||
766 | prot); | ||
767 | |||
768 | return __pgprot(prot); | 767 | return __pgprot(prot); |
769 | } | 768 | } |
770 | 769 | ||
@@ -832,7 +831,7 @@ pgprot_t pci_phys_mem_access_prot(struct file *file, | |||
832 | int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, | 831 | int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, |
833 | enum pci_mmap_state mmap_state, int write_combine) | 832 | enum pci_mmap_state mmap_state, int write_combine) |
834 | { | 833 | { |
835 | unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; | 834 | resource_size_t offset = vma->vm_pgoff << PAGE_SHIFT; |
836 | struct resource *rp; | 835 | struct resource *rp; |
837 | int ret; | 836 | int ret; |
838 | 837 | ||
@@ -1333,20 +1332,41 @@ EXPORT_SYMBOL(pci_read_irq_line); | |||
1333 | 1332 | ||
1334 | void pci_resource_to_user(const struct pci_dev *dev, int bar, | 1333 | void pci_resource_to_user(const struct pci_dev *dev, int bar, |
1335 | const struct resource *rsrc, | 1334 | const struct resource *rsrc, |
1336 | u64 *start, u64 *end) | 1335 | resource_size_t *start, resource_size_t *end) |
1337 | { | 1336 | { |
1338 | struct pci_controller *hose = pci_bus_to_host(dev->bus); | 1337 | struct pci_controller *hose = pci_bus_to_host(dev->bus); |
1339 | unsigned long offset = 0; | 1338 | resource_size_t offset = 0; |
1340 | 1339 | ||
1341 | if (hose == NULL) | 1340 | if (hose == NULL) |
1342 | return; | 1341 | return; |
1343 | 1342 | ||
1344 | if (rsrc->flags & IORESOURCE_IO) | 1343 | if (rsrc->flags & IORESOURCE_IO) |
1345 | offset = pci_io_base - (unsigned long)hose->io_base_virt + | 1344 | offset = (unsigned long)hose->io_base_virt - pci_io_base; |
1346 | hose->io_base_phys; | 1345 | |
1346 | /* We pass a fully fixed up address to userland for MMIO instead of | ||
1347 | * a BAR value because X is lame and expects to be able to use that | ||
1348 | * to pass to /dev/mem ! | ||
1349 | * | ||
1350 | * That means that we'll have potentially 64 bits values where some | ||
1351 | * userland apps only expect 32 (like X itself since it thinks only | ||
1352 | * Sparc has 64 bits MMIO) but if we don't do that, we break it on | ||
1353 | * 32 bits CHRPs :-( | ||
1354 | * | ||
1355 | * Hopefully, the sysfs insterface is immune to that gunk. Once X | ||
1356 | * has been fixed (and the fix spread enough), we can re-enable the | ||
1357 | * 2 lines below and pass down a BAR value to userland. In that case | ||
1358 | * we'll also have to re-enable the matching code in | ||
1359 | * __pci_mmap_make_offset(). | ||
1360 | * | ||
1361 | * BenH. | ||
1362 | */ | ||
1363 | #if 0 | ||
1364 | else if (rsrc->flags & IORESOURCE_MEM) | ||
1365 | offset = hose->pci_mem_offset; | ||
1366 | #endif | ||
1347 | 1367 | ||
1348 | *start = rsrc->start + offset; | 1368 | *start = rsrc->start - offset; |
1349 | *end = rsrc->end + offset; | 1369 | *end = rsrc->end - offset; |
1350 | } | 1370 | } |
1351 | 1371 | ||
1352 | struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node) | 1372 | struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node) |
diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index 63808e01cb0b..5e723c4c2571 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c | |||
@@ -879,7 +879,7 @@ pci_resource_to_bus(struct pci_dev *pdev, struct resource *res) | |||
879 | 879 | ||
880 | 880 | ||
881 | static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, | 881 | static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, |
882 | unsigned long *offset, | 882 | resource_size_t *offset, |
883 | enum pci_mmap_state mmap_state) | 883 | enum pci_mmap_state mmap_state) |
884 | { | 884 | { |
885 | struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); | 885 | struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); |
@@ -891,7 +891,9 @@ static struct resource *__pci_mmap_make_offset(struct pci_dev *dev, | |||
891 | 891 | ||
892 | /* If memory, add on the PCI bridge address offset */ | 892 | /* If memory, add on the PCI bridge address offset */ |
893 | if (mmap_state == pci_mmap_mem) { | 893 | if (mmap_state == pci_mmap_mem) { |
894 | #if 0 /* See comment in pci_resource_to_user() for why this is disabled */ | ||
894 | *offset += hose->pci_mem_offset; | 895 | *offset += hose->pci_mem_offset; |
896 | #endif | ||
895 | res_bit = IORESOURCE_MEM; | 897 | res_bit = IORESOURCE_MEM; |
896 | } else { | 898 | } else { |
897 | io_offset = hose->io_base_virt - ___IO_BASE; | 899 | io_offset = hose->io_base_virt - ___IO_BASE; |
@@ -1030,7 +1032,7 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, | |||
1030 | enum pci_mmap_state mmap_state, | 1032 | enum pci_mmap_state mmap_state, |
1031 | int write_combine) | 1033 | int write_combine) |
1032 | { | 1034 | { |
1033 | unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; | 1035 | resource_size_t offset = vma->vm_pgoff << PAGE_SHIFT; |
1034 | struct resource *rp; | 1036 | struct resource *rp; |
1035 | int ret; | 1037 | int ret; |
1036 | 1038 | ||
@@ -1132,21 +1134,42 @@ void pci_resource_to_user(const struct pci_dev *dev, int bar, | |||
1132 | resource_size_t *start, resource_size_t *end) | 1134 | resource_size_t *start, resource_size_t *end) |
1133 | { | 1135 | { |
1134 | struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); | 1136 | struct pci_controller *hose = pci_bus_to_hose(dev->bus->number); |
1135 | unsigned long offset = 0; | 1137 | resource_size_t offset = 0; |
1136 | 1138 | ||
1137 | if (hose == NULL) | 1139 | if (hose == NULL) |
1138 | return; | 1140 | return; |
1139 | 1141 | ||
1140 | if (rsrc->flags & IORESOURCE_IO) | 1142 | if (rsrc->flags & IORESOURCE_IO) |
1141 | offset = ___IO_BASE - hose->io_base_virt + hose->io_base_phys; | 1143 | offset = (unsigned long)hose->io_base_virt - _IO_BASE; |
1144 | |||
1145 | /* We pass a fully fixed up address to userland for MMIO instead of | ||
1146 | * a BAR value because X is lame and expects to be able to use that | ||
1147 | * to pass to /dev/mem ! | ||
1148 | * | ||
1149 | * That means that we'll have potentially 64 bits values where some | ||
1150 | * userland apps only expect 32 (like X itself since it thinks only | ||
1151 | * Sparc has 64 bits MMIO) but if we don't do that, we break it on | ||
1152 | * 32 bits CHRPs :-( | ||
1153 | * | ||
1154 | * Hopefully, the sysfs insterface is immune to that gunk. Once X | ||
1155 | * has been fixed (and the fix spread enough), we can re-enable the | ||
1156 | * 2 lines below and pass down a BAR value to userland. In that case | ||
1157 | * we'll also have to re-enable the matching code in | ||
1158 | * __pci_mmap_make_offset(). | ||
1159 | * | ||
1160 | * BenH. | ||
1161 | */ | ||
1162 | #if 0 | ||
1163 | else if (rsrc->flags & IORESOURCE_MEM) | ||
1164 | offset = hose->pci_mem_offset; | ||
1165 | #endif | ||
1142 | 1166 | ||
1143 | *start = rsrc->start + offset; | 1167 | *start = rsrc->start - offset; |
1144 | *end = rsrc->end + offset; | 1168 | *end = rsrc->end - offset; |
1145 | } | 1169 | } |
1146 | 1170 | ||
1147 | void __init | 1171 | void __init pci_init_resource(struct resource *res, resource_size_t start, |
1148 | pci_init_resource(struct resource *res, unsigned long start, unsigned long end, | 1172 | resource_size_t end, int flags, char *name) |
1149 | int flags, char *name) | ||
1150 | { | 1173 | { |
1151 | res->start = start; | 1174 | res->start = start; |
1152 | res->end = end; | 1175 | res->end = end; |
diff --git a/include/asm-powerpc/pci-bridge.h b/include/asm-powerpc/pci-bridge.h index 7bb7f9009806..cb02c9d1ef93 100644 --- a/include/asm-powerpc/pci-bridge.h +++ b/include/asm-powerpc/pci-bridge.h | |||
@@ -31,12 +31,12 @@ struct pci_controller { | |||
31 | int last_busno; | 31 | int last_busno; |
32 | 32 | ||
33 | void __iomem *io_base_virt; | 33 | void __iomem *io_base_virt; |
34 | unsigned long io_base_phys; | 34 | resource_size_t io_base_phys; |
35 | 35 | ||
36 | /* Some machines have a non 1:1 mapping of | 36 | /* Some machines have a non 1:1 mapping of |
37 | * the PCI memory space in the CPU bus space | 37 | * the PCI memory space in the CPU bus space |
38 | */ | 38 | */ |
39 | unsigned long pci_mem_offset; | 39 | resource_size_t pci_mem_offset; |
40 | unsigned long pci_io_size; | 40 | unsigned long pci_io_size; |
41 | 41 | ||
42 | struct pci_ops *ops; | 42 | struct pci_ops *ops; |
diff --git a/include/asm-ppc/pci-bridge.h b/include/asm-ppc/pci-bridge.h index 6c955d0c1ef0..4d35b844bc58 100644 --- a/include/asm-ppc/pci-bridge.h +++ b/include/asm-ppc/pci-bridge.h | |||
@@ -20,8 +20,8 @@ extern unsigned long pci_bus_mem_base_phys(unsigned int bus); | |||
20 | extern struct pci_controller* pcibios_alloc_controller(void); | 20 | extern struct pci_controller* pcibios_alloc_controller(void); |
21 | 21 | ||
22 | /* Helper function for setting up resources */ | 22 | /* Helper function for setting up resources */ |
23 | extern void pci_init_resource(struct resource *res, unsigned long start, | 23 | extern void pci_init_resource(struct resource *res, resource_size_t start, |
24 | unsigned long end, int flags, char *name); | 24 | resource_size_t end, int flags, char *name); |
25 | 25 | ||
26 | /* Get the PCI host controller for a bus */ | 26 | /* Get the PCI host controller for a bus */ |
27 | extern struct pci_controller* pci_bus_to_hose(int bus); | 27 | extern struct pci_controller* pci_bus_to_hose(int bus); |
@@ -50,12 +50,12 @@ struct pci_controller { | |||
50 | int bus_offset; | 50 | int bus_offset; |
51 | 51 | ||
52 | void __iomem *io_base_virt; | 52 | void __iomem *io_base_virt; |
53 | unsigned long io_base_phys; | 53 | resource_size_t io_base_phys; |
54 | 54 | ||
55 | /* Some machines (PReP) have a non 1:1 mapping of | 55 | /* Some machines (PReP) have a non 1:1 mapping of |
56 | * the PCI memory space in the CPU bus space | 56 | * the PCI memory space in the CPU bus space |
57 | */ | 57 | */ |
58 | unsigned long pci_mem_offset; | 58 | resource_size_t pci_mem_offset; |
59 | 59 | ||
60 | struct pci_ops *ops; | 60 | struct pci_ops *ops; |
61 | volatile unsigned int __iomem *cfg_addr; | 61 | volatile unsigned int __iomem *cfg_addr; |