[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>
This commit is contained in:
parent
f09b5ce018
commit
396a1a5832
|
@ -1544,7 +1544,7 @@ pci_resource_to_bus(struct pci_dev *pdev, struct resource *res)
|
|||
|
||||
|
||||
static struct resource *__pci_mmap_make_offset(struct pci_dev *dev,
|
||||
unsigned long *offset,
|
||||
resource_size_t *offset,
|
||||
enum pci_mmap_state mmap_state)
|
||||
{
|
||||
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,
|
|||
|
||||
/* If memory, add on the PCI bridge address offset */
|
||||
if (mmap_state == pci_mmap_mem) {
|
||||
#if 0 /* See comment in pci_resource_to_user() for why this is disabled */
|
||||
*offset += hose->pci_mem_offset;
|
||||
#endif
|
||||
res_bit = IORESOURCE_MEM;
|
||||
} else {
|
||||
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,
|
|||
else
|
||||
prot |= _PAGE_GUARDED;
|
||||
|
||||
printk("PCI map for %s:%llx, prot: %lx\n", pci_name(dev),
|
||||
(unsigned long long)rp->start, prot);
|
||||
|
||||
return __pgprot(prot);
|
||||
}
|
||||
|
||||
|
@ -1695,7 +1694,7 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
|
|||
enum pci_mmap_state mmap_state,
|
||||
int write_combine)
|
||||
{
|
||||
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
|
||||
resource_size_t offset = vma->vm_pgoff << PAGE_SHIFT;
|
||||
struct resource *rp;
|
||||
int ret;
|
||||
|
||||
|
@ -1808,22 +1807,42 @@ void pci_resource_to_user(const struct pci_dev *dev, int bar,
|
|||
resource_size_t *start, resource_size_t *end)
|
||||
{
|
||||
struct pci_controller *hose = pci_bus_to_hose(dev->bus->number);
|
||||
unsigned long offset = 0;
|
||||
resource_size_t offset = 0;
|
||||
|
||||
if (hose == NULL)
|
||||
return;
|
||||
|
||||
if (rsrc->flags & IORESOURCE_IO)
|
||||
offset = (void __iomem *)_IO_BASE - hose->io_base_virt
|
||||
+ hose->io_base_phys;
|
||||
offset = (unsigned long)hose->io_base_virt - _IO_BASE;
|
||||
|
||||
*start = rsrc->start + offset;
|
||||
*end = rsrc->end + offset;
|
||||
/* We pass a fully fixed up address to userland for MMIO instead of
|
||||
* a BAR value because X is lame and expects to be able to use that
|
||||
* to pass to /dev/mem !
|
||||
*
|
||||
* That means that we'll have potentially 64 bits values where some
|
||||
* userland apps only expect 32 (like X itself since it thinks only
|
||||
* Sparc has 64 bits MMIO) but if we don't do that, we break it on
|
||||
* 32 bits CHRPs :-(
|
||||
*
|
||||
* Hopefully, the sysfs insterface is immune to that gunk. Once X
|
||||
* has been fixed (and the fix spread enough), we can re-enable the
|
||||
* 2 lines below and pass down a BAR value to userland. In that case
|
||||
* we'll also have to re-enable the matching code in
|
||||
* __pci_mmap_make_offset().
|
||||
*
|
||||
* BenH.
|
||||
*/
|
||||
#if 0
|
||||
else if (rsrc->flags & IORESOURCE_MEM)
|
||||
offset = hose->pci_mem_offset;
|
||||
#endif
|
||||
|
||||
*start = rsrc->start - offset;
|
||||
*end = rsrc->end - offset;
|
||||
}
|
||||
|
||||
void __init
|
||||
pci_init_resource(struct resource *res, unsigned long start, unsigned long end,
|
||||
int flags, char *name)
|
||||
void __init pci_init_resource(struct resource *res, resource_size_t start,
|
||||
resource_size_t end, int flags, char *name)
|
||||
{
|
||||
res->start = start;
|
||||
res->end = end;
|
||||
|
|
|
@ -682,7 +682,7 @@ int pci_proc_domain(struct pci_bus *bus)
|
|||
* Returns negative error code on failure, zero on success.
|
||||
*/
|
||||
static struct resource *__pci_mmap_make_offset(struct pci_dev *dev,
|
||||
unsigned long *offset,
|
||||
resource_size_t *offset,
|
||||
enum pci_mmap_state mmap_state)
|
||||
{
|
||||
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,
|
|||
|
||||
/* If memory, add on the PCI bridge address offset */
|
||||
if (mmap_state == pci_mmap_mem) {
|
||||
#if 0 /* See comment in pci_resource_to_user() for why this is disabled */
|
||||
*offset += hose->pci_mem_offset;
|
||||
#endif
|
||||
res_bit = IORESOURCE_MEM;
|
||||
} else {
|
||||
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,
|
|||
else
|
||||
prot |= _PAGE_GUARDED;
|
||||
|
||||
printk(KERN_DEBUG "PCI map for %s:%lx, prot: %lx\n", pci_name(dev), rp->start,
|
||||
prot);
|
||||
|
||||
return __pgprot(prot);
|
||||
}
|
||||
|
||||
|
@ -832,7 +831,7 @@ pgprot_t pci_phys_mem_access_prot(struct file *file,
|
|||
int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
|
||||
enum pci_mmap_state mmap_state, int write_combine)
|
||||
{
|
||||
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
|
||||
resource_size_t offset = vma->vm_pgoff << PAGE_SHIFT;
|
||||
struct resource *rp;
|
||||
int ret;
|
||||
|
||||
|
@ -1333,20 +1332,41 @@ EXPORT_SYMBOL(pci_read_irq_line);
|
|||
|
||||
void pci_resource_to_user(const struct pci_dev *dev, int bar,
|
||||
const struct resource *rsrc,
|
||||
u64 *start, u64 *end)
|
||||
resource_size_t *start, resource_size_t *end)
|
||||
{
|
||||
struct pci_controller *hose = pci_bus_to_host(dev->bus);
|
||||
unsigned long offset = 0;
|
||||
resource_size_t offset = 0;
|
||||
|
||||
if (hose == NULL)
|
||||
return;
|
||||
|
||||
if (rsrc->flags & IORESOURCE_IO)
|
||||
offset = pci_io_base - (unsigned long)hose->io_base_virt +
|
||||
hose->io_base_phys;
|
||||
offset = (unsigned long)hose->io_base_virt - pci_io_base;
|
||||
|
||||
*start = rsrc->start + offset;
|
||||
*end = rsrc->end + offset;
|
||||
/* We pass a fully fixed up address to userland for MMIO instead of
|
||||
* a BAR value because X is lame and expects to be able to use that
|
||||
* to pass to /dev/mem !
|
||||
*
|
||||
* That means that we'll have potentially 64 bits values where some
|
||||
* userland apps only expect 32 (like X itself since it thinks only
|
||||
* Sparc has 64 bits MMIO) but if we don't do that, we break it on
|
||||
* 32 bits CHRPs :-(
|
||||
*
|
||||
* Hopefully, the sysfs insterface is immune to that gunk. Once X
|
||||
* has been fixed (and the fix spread enough), we can re-enable the
|
||||
* 2 lines below and pass down a BAR value to userland. In that case
|
||||
* we'll also have to re-enable the matching code in
|
||||
* __pci_mmap_make_offset().
|
||||
*
|
||||
* BenH.
|
||||
*/
|
||||
#if 0
|
||||
else if (rsrc->flags & IORESOURCE_MEM)
|
||||
offset = hose->pci_mem_offset;
|
||||
#endif
|
||||
|
||||
*start = rsrc->start - offset;
|
||||
*end = rsrc->end - offset;
|
||||
}
|
||||
|
||||
struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node)
|
||||
|
|
|
@ -879,7 +879,7 @@ pci_resource_to_bus(struct pci_dev *pdev, struct resource *res)
|
|||
|
||||
|
||||
static struct resource *__pci_mmap_make_offset(struct pci_dev *dev,
|
||||
unsigned long *offset,
|
||||
resource_size_t *offset,
|
||||
enum pci_mmap_state mmap_state)
|
||||
{
|
||||
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,
|
|||
|
||||
/* If memory, add on the PCI bridge address offset */
|
||||
if (mmap_state == pci_mmap_mem) {
|
||||
#if 0 /* See comment in pci_resource_to_user() for why this is disabled */
|
||||
*offset += hose->pci_mem_offset;
|
||||
#endif
|
||||
res_bit = IORESOURCE_MEM;
|
||||
} else {
|
||||
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,
|
|||
enum pci_mmap_state mmap_state,
|
||||
int write_combine)
|
||||
{
|
||||
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
|
||||
resource_size_t offset = vma->vm_pgoff << PAGE_SHIFT;
|
||||
struct resource *rp;
|
||||
int ret;
|
||||
|
||||
|
@ -1132,21 +1134,42 @@ void pci_resource_to_user(const struct pci_dev *dev, int bar,
|
|||
resource_size_t *start, resource_size_t *end)
|
||||
{
|
||||
struct pci_controller *hose = pci_bus_to_hose(dev->bus->number);
|
||||
unsigned long offset = 0;
|
||||
resource_size_t offset = 0;
|
||||
|
||||
if (hose == NULL)
|
||||
return;
|
||||
|
||||
if (rsrc->flags & IORESOURCE_IO)
|
||||
offset = ___IO_BASE - hose->io_base_virt + hose->io_base_phys;
|
||||
offset = (unsigned long)hose->io_base_virt - _IO_BASE;
|
||||
|
||||
*start = rsrc->start + offset;
|
||||
*end = rsrc->end + offset;
|
||||
/* We pass a fully fixed up address to userland for MMIO instead of
|
||||
* a BAR value because X is lame and expects to be able to use that
|
||||
* to pass to /dev/mem !
|
||||
*
|
||||
* That means that we'll have potentially 64 bits values where some
|
||||
* userland apps only expect 32 (like X itself since it thinks only
|
||||
* Sparc has 64 bits MMIO) but if we don't do that, we break it on
|
||||
* 32 bits CHRPs :-(
|
||||
*
|
||||
* Hopefully, the sysfs insterface is immune to that gunk. Once X
|
||||
* has been fixed (and the fix spread enough), we can re-enable the
|
||||
* 2 lines below and pass down a BAR value to userland. In that case
|
||||
* we'll also have to re-enable the matching code in
|
||||
* __pci_mmap_make_offset().
|
||||
*
|
||||
* BenH.
|
||||
*/
|
||||
#if 0
|
||||
else if (rsrc->flags & IORESOURCE_MEM)
|
||||
offset = hose->pci_mem_offset;
|
||||
#endif
|
||||
|
||||
*start = rsrc->start - offset;
|
||||
*end = rsrc->end - offset;
|
||||
}
|
||||
|
||||
void __init
|
||||
pci_init_resource(struct resource *res, unsigned long start, unsigned long end,
|
||||
int flags, char *name)
|
||||
void __init pci_init_resource(struct resource *res, resource_size_t start,
|
||||
resource_size_t end, int flags, char *name)
|
||||
{
|
||||
res->start = start;
|
||||
res->end = end;
|
||||
|
|
|
@ -31,12 +31,12 @@ struct pci_controller {
|
|||
int last_busno;
|
||||
|
||||
void __iomem *io_base_virt;
|
||||
unsigned long io_base_phys;
|
||||
resource_size_t io_base_phys;
|
||||
|
||||
/* Some machines have a non 1:1 mapping of
|
||||
* the PCI memory space in the CPU bus space
|
||||
*/
|
||||
unsigned long pci_mem_offset;
|
||||
resource_size_t pci_mem_offset;
|
||||
unsigned long pci_io_size;
|
||||
|
||||
struct pci_ops *ops;
|
||||
|
|
|
@ -20,8 +20,8 @@ extern unsigned long pci_bus_mem_base_phys(unsigned int bus);
|
|||
extern struct pci_controller* pcibios_alloc_controller(void);
|
||||
|
||||
/* Helper function for setting up resources */
|
||||
extern void pci_init_resource(struct resource *res, unsigned long start,
|
||||
unsigned long end, int flags, char *name);
|
||||
extern void pci_init_resource(struct resource *res, resource_size_t start,
|
||||
resource_size_t end, int flags, char *name);
|
||||
|
||||
/* Get the PCI host controller for a bus */
|
||||
extern struct pci_controller* pci_bus_to_hose(int bus);
|
||||
|
@ -50,12 +50,12 @@ struct pci_controller {
|
|||
int bus_offset;
|
||||
|
||||
void __iomem *io_base_virt;
|
||||
unsigned long io_base_phys;
|
||||
resource_size_t io_base_phys;
|
||||
|
||||
/* Some machines (PReP) have a non 1:1 mapping of
|
||||
* the PCI memory space in the CPU bus space
|
||||
*/
|
||||
unsigned long pci_mem_offset;
|
||||
resource_size_t pci_mem_offset;
|
||||
|
||||
struct pci_ops *ops;
|
||||
volatile unsigned int __iomem *cfg_addr;
|
||||
|
|
Loading…
Reference in New Issue