forked from Minki/linux
sparc: Use generic pci_mmap_resource_range()
The main feature of the sparc-specific implementation of
pci_mmap_resource_range() is that it allows mapping the entire PCI I/O
space for a PCI host bridge using the /proc/bus/pci interface on a bridge
device.
The generic implementation cannot do this, but it also appears that this
got broken for sparc by commit 9eff02e204
("PCI: check mmap range of
/proc/bus/pci files too"), which enforces that each address is part of a
BAR for kernels after 2.6.28.
Remove it all, assuming that the corresponding user space code has already
been changed to access /dev/ioport instead a long time ago. Add
pci_iobar_pfn() to make it possible to map I/O resources. This is adapted
from the powerpc version.
Link: https://lore.kernel.org/lkml/1519887203.622.3.camel@infradead.org/t/
Link: https://lore.kernel.org/r/20220715153617.3393420-2-arnd@kernel.org
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
This commit is contained in:
parent
0ad722f159
commit
c1ef322491
@ -37,6 +37,7 @@ static inline int pci_proc_domain(struct pci_bus *bus)
|
||||
#define HAVE_PCI_MMAP
|
||||
#define arch_can_pci_mmap_io() 1
|
||||
#define HAVE_ARCH_PCI_GET_UNMAPPED_AREA
|
||||
#define ARCH_GENERIC_PCI_MMAP_RESOURCE
|
||||
#define get_pci_unmapped_area get_fb_unmapped_area
|
||||
#endif /* CONFIG_SPARC64 */
|
||||
|
||||
|
@ -751,161 +751,15 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
|
||||
}
|
||||
|
||||
/* Platform support for /proc/bus/pci/X/Y mmap()s. */
|
||||
|
||||
/* If the user uses a host-bridge as the PCI device, he may use
|
||||
* this to perform a raw mmap() of the I/O or MEM space behind
|
||||
* that controller.
|
||||
*
|
||||
* This can be useful for execution of x86 PCI bios initialization code
|
||||
* on a PCI card, like the xfree86 int10 stuff does.
|
||||
*/
|
||||
static int __pci_mmap_make_offset_bus(struct pci_dev *pdev, struct vm_area_struct *vma,
|
||||
enum pci_mmap_state mmap_state)
|
||||
int pci_iobar_pfn(struct pci_dev *pdev, int bar, struct vm_area_struct *vma)
|
||||
{
|
||||
struct pci_pbm_info *pbm = pdev->dev.archdata.host_controller;
|
||||
unsigned long space_size, user_offset, user_size;
|
||||
resource_size_t ioaddr = pci_resource_start(pdev, bar);
|
||||
|
||||
if (mmap_state == pci_mmap_io) {
|
||||
space_size = resource_size(&pbm->io_space);
|
||||
} else {
|
||||
space_size = resource_size(&pbm->mem_space);
|
||||
}
|
||||
|
||||
/* Make sure the request is in range. */
|
||||
user_offset = vma->vm_pgoff << PAGE_SHIFT;
|
||||
user_size = vma->vm_end - vma->vm_start;
|
||||
|
||||
if (user_offset >= space_size ||
|
||||
(user_offset + user_size) > space_size)
|
||||
if (!pbm)
|
||||
return -EINVAL;
|
||||
|
||||
if (mmap_state == pci_mmap_io) {
|
||||
vma->vm_pgoff = (pbm->io_space.start +
|
||||
user_offset) >> PAGE_SHIFT;
|
||||
} else {
|
||||
vma->vm_pgoff = (pbm->mem_space.start +
|
||||
user_offset) >> PAGE_SHIFT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Adjust vm_pgoff of VMA such that it is the physical page offset
|
||||
* corresponding to the 32-bit pci bus offset for DEV requested by the user.
|
||||
*
|
||||
* Basically, the user finds the base address for his device which he wishes
|
||||
* to mmap. They read the 32-bit value from the config space base register,
|
||||
* add whatever PAGE_SIZE multiple offset they wish, and feed this into the
|
||||
* offset parameter of mmap on /proc/bus/pci/XXX for that device.
|
||||
*
|
||||
* Returns negative error code on failure, zero on success.
|
||||
*/
|
||||
static int __pci_mmap_make_offset(struct pci_dev *pdev,
|
||||
struct vm_area_struct *vma,
|
||||
enum pci_mmap_state mmap_state)
|
||||
{
|
||||
unsigned long user_paddr, user_size;
|
||||
int i, err;
|
||||
|
||||
/* First compute the physical address in vma->vm_pgoff,
|
||||
* making sure the user offset is within range in the
|
||||
* appropriate PCI space.
|
||||
*/
|
||||
err = __pci_mmap_make_offset_bus(pdev, vma, mmap_state);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* If this is a mapping on a host bridge, any address
|
||||
* is OK.
|
||||
*/
|
||||
if ((pdev->class >> 8) == PCI_CLASS_BRIDGE_HOST)
|
||||
return err;
|
||||
|
||||
/* Otherwise make sure it's in the range for one of the
|
||||
* device's resources.
|
||||
*/
|
||||
user_paddr = vma->vm_pgoff << PAGE_SHIFT;
|
||||
user_size = vma->vm_end - vma->vm_start;
|
||||
|
||||
for (i = 0; i <= PCI_ROM_RESOURCE; i++) {
|
||||
struct resource *rp = &pdev->resource[i];
|
||||
resource_size_t aligned_end;
|
||||
|
||||
/* Active? */
|
||||
if (!rp->flags)
|
||||
continue;
|
||||
|
||||
/* Same type? */
|
||||
if (i == PCI_ROM_RESOURCE) {
|
||||
if (mmap_state != pci_mmap_mem)
|
||||
continue;
|
||||
} else {
|
||||
if ((mmap_state == pci_mmap_io &&
|
||||
(rp->flags & IORESOURCE_IO) == 0) ||
|
||||
(mmap_state == pci_mmap_mem &&
|
||||
(rp->flags & IORESOURCE_MEM) == 0))
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Align the resource end to the next page address.
|
||||
* PAGE_SIZE intentionally added instead of (PAGE_SIZE - 1),
|
||||
* because actually we need the address of the next byte
|
||||
* after rp->end.
|
||||
*/
|
||||
aligned_end = (rp->end + PAGE_SIZE) & PAGE_MASK;
|
||||
|
||||
if ((rp->start <= user_paddr) &&
|
||||
(user_paddr + user_size) <= aligned_end)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i > PCI_ROM_RESOURCE)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set vm_page_prot of VMA, as appropriate for this architecture, for a pci
|
||||
* device mapping.
|
||||
*/
|
||||
static void __pci_mmap_set_pgprot(struct pci_dev *dev, struct vm_area_struct *vma,
|
||||
enum pci_mmap_state mmap_state)
|
||||
{
|
||||
/* Our io_remap_pfn_range takes care of this, do nothing. */
|
||||
}
|
||||
|
||||
/* Perform the actual remap of the pages for a PCI device mapping, as appropriate
|
||||
* for this architecture. The region in the process to map is described by vm_start
|
||||
* and vm_end members of VMA, the BAR relative address is found in vm_pgoff.
|
||||
* The pci device structure is provided so that architectures may make mapping
|
||||
* decisions on a per-device or per-bus basis.
|
||||
*
|
||||
* Returns a negative error code on failure, zero on success.
|
||||
*/
|
||||
int pci_mmap_resource_range(struct pci_dev *dev, int bar,
|
||||
struct vm_area_struct *vma,
|
||||
enum pci_mmap_state mmap_state, int write_combine)
|
||||
{
|
||||
int ret;
|
||||
resource_size_t start, end;
|
||||
|
||||
/* convert per-BAR address to PCI bus address */
|
||||
pci_resource_to_user(dev, bar, &dev->resource[bar], &start, &end);
|
||||
vma->vm_pgoff += start >> PAGE_SHIFT;
|
||||
|
||||
ret = __pci_mmap_make_offset(dev, vma, mmap_state);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
__pci_mmap_set_pgprot(dev, vma, mmap_state);
|
||||
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
ret = io_remap_pfn_range(vma, vma->vm_start,
|
||||
vma->vm_pgoff,
|
||||
vma->vm_end - vma->vm_start,
|
||||
vma->vm_page_prot);
|
||||
if (ret)
|
||||
return ret;
|
||||
vma->vm_pgoff += (ioaddr + pbm->io_space.start) >> PAGE_SHIFT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user