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 /arch/powerpc/kernel | |
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>
Diffstat (limited to 'arch/powerpc/kernel')
-rw-r--r-- | arch/powerpc/kernel/pci_32.c | 45 | ||||
-rw-r--r-- | arch/powerpc/kernel/pci_64.c | 42 |
2 files changed, 63 insertions, 24 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) |