diff options
author | Jessica Clarke <jrtc27@FreeBSD.org> | 2021-10-17 14:32:35 +0000 |
---|---|---|
committer | Jessica Clarke <jrtc27@FreeBSD.org> | 2021-10-17 14:32:35 +0000 |
commit | 82098c8bb5b303c7c8b48e7537fadfe74b375bd3 (patch) | |
tree | 8deb00de65cf11a9de61f0327cd18df8b94aac25 | |
parent | 60d962e0410340f107d062e8f4389c0d467613a2 (diff) | |
download | src-82098c8bb5b303c7c8b48e7537fadfe74b375bd3.tar.gz src-82098c8bb5b303c7c8b48e7537fadfe74b375bd3.zip |
LinuxKPI: Support lazy BAR allocation
Linux KPIs like pci_resource_start/len assume that BARs have been
allocated, but FreeBSD lazily allocates BARs if it cannot allocate the
firmware-allocated BARs. Thus using the Linux KPIs must force allocation
of the BARs rather than returning 0 for the start and length, which can
crash drm-kmod drivers that assume the BARs are valid. This is needed
for the AMDGPU driver to be able to attach on SiFive's HiFive Unmatched.
Reviewed by: hselasky, jhb, mav
MFC after: 1 week
Differential Revision: https://reviews.freebsd.org/D32447
-rw-r--r-- | sys/compat/linuxkpi/common/include/linux/pci.h | 22 | ||||
-rw-r--r-- | sys/compat/linuxkpi/common/src/linux_pci.c | 25 | ||||
-rw-r--r-- | sys/dev/pci/pci.c | 2 | ||||
-rw-r--r-- | sys/dev/pci/pci_private.h | 4 |
4 files changed, 42 insertions, 11 deletions
diff --git a/sys/compat/linuxkpi/common/include/linux/pci.h b/sys/compat/linuxkpi/common/include/linux/pci.h index a80e6965915d..2bac82de2af5 100644 --- a/sys/compat/linuxkpi/common/include/linux/pci.h +++ b/sys/compat/linuxkpi/common/include/linux/pci.h @@ -311,19 +311,27 @@ pci_resource_type(struct pci_dev *pdev, int bar) return (SYS_RES_MEMORY); } +struct resource_list_entry *linux_pci_reserve_bar(struct pci_dev *pdev, + struct resource_list *rl, int type, int rid); + static inline struct resource_list_entry * -linux_pci_get_rle(struct pci_dev *pdev, int type, int rid) +linux_pci_get_rle(struct pci_dev *pdev, int type, int rid, bool reserve_bar) { struct pci_devinfo *dinfo; struct resource_list *rl; + struct resource_list_entry *rle; dinfo = device_get_ivars(pdev->dev.bsddev); rl = &dinfo->resources; - return resource_list_find(rl, type, rid); + rle = resource_list_find(rl, type, rid); + /* Reserve resources for this BAR if needed. */ + if (rle == NULL && reserve_bar) + rle = linux_pci_reserve_bar(pdev, rl, type, rid); + return (rle); } static inline struct resource_list_entry * -linux_pci_get_bar(struct pci_dev *pdev, int bar) +linux_pci_get_bar(struct pci_dev *pdev, int bar, bool reserve) { int type; @@ -331,7 +339,7 @@ linux_pci_get_bar(struct pci_dev *pdev, int bar) if (type < 0) return (NULL); bar = PCIR_BAR(bar); - return (linux_pci_get_rle(pdev, type, bar)); + return (linux_pci_get_rle(pdev, type, bar, reserve)); } static inline struct device * @@ -521,7 +529,7 @@ pci_release_region(struct pci_dev *pdev, int bar) struct pci_devres *dr; struct pci_mmio_region *mmio, *p; - if ((rle = linux_pci_get_bar(pdev, bar)) == NULL) + if ((rle = linux_pci_get_bar(pdev, bar, false)) == NULL) return; /* @@ -779,7 +787,7 @@ pci_enable_msix(struct pci_dev *pdev, struct msix_entry *entries, int nreq) pci_release_msi(pdev->dev.bsddev); return avail; } - rle = linux_pci_get_rle(pdev, SYS_RES_IRQ, 1); + rle = linux_pci_get_rle(pdev, SYS_RES_IRQ, 1, false); pdev->dev.irq_start = rle->start; pdev->dev.irq_end = rle->start + avail; for (i = 0; i < nreq; i++) @@ -832,7 +840,7 @@ pci_enable_msi(struct pci_dev *pdev) if ((error = -pci_alloc_msi(pdev->dev.bsddev, &avail)) != 0) return error; - rle = linux_pci_get_rle(pdev, SYS_RES_IRQ, 1); + rle = linux_pci_get_rle(pdev, SYS_RES_IRQ, 1, false); pdev->dev.irq_start = rle->start; pdev->dev.irq_end = rle->start + avail; pdev->irq = rle->start; diff --git a/sys/compat/linuxkpi/common/src/linux_pci.c b/sys/compat/linuxkpi/common/src/linux_pci.c index 44ed4b22de6f..780ba38d18dd 100644 --- a/sys/compat/linuxkpi/common/src/linux_pci.c +++ b/sys/compat/linuxkpi/common/src/linux_pci.c @@ -409,7 +409,7 @@ linux_pci_attach_device(device_t dev, struct pci_driver *pdrv, PCI_GET_ID(parent, dev, PCI_ID_RID, &rid); pdev->devfn = rid; pdev->pdrv = pdrv; - rle = linux_pci_get_rle(pdev, SYS_RES_IRQ, 0); + rle = linux_pci_get_rle(pdev, SYS_RES_IRQ, 0, false); if (rle != NULL) pdev->dev.irq = rle->start; else @@ -665,6 +665,25 @@ linux_pci_register_driver(struct pci_driver *pdrv) return (_linux_pci_register_driver(pdrv, dc)); } +struct resource_list_entry * +linux_pci_reserve_bar(struct pci_dev *pdev, struct resource_list *rl, + int type, int rid) +{ + device_t dev; + struct resource *res; + + KASSERT(type == SYS_RES_IOPORT || type == SYS_RES_MEMORY, + ("trying to reserve non-BAR type %d", type)); + + dev = pdev->pdrv != NULL && pdev->pdrv->isdrm ? + device_get_parent(pdev->dev.bsddev) : pdev->dev.bsddev; + res = pci_reserve_map(device_get_parent(dev), dev, type, &rid, 0, ~0, + 1, 1, 0); + if (res == NULL) + return (NULL); + return (resource_list_find(rl, type, rid)); +} + unsigned long pci_resource_start(struct pci_dev *pdev, int bar) { @@ -672,7 +691,7 @@ pci_resource_start(struct pci_dev *pdev, int bar) rman_res_t newstart; device_t dev; - if ((rle = linux_pci_get_bar(pdev, bar)) == NULL) + if ((rle = linux_pci_get_bar(pdev, bar, true)) == NULL) return (0); dev = pdev->pdrv != NULL && pdev->pdrv->isdrm ? device_get_parent(pdev->dev.bsddev) : pdev->dev.bsddev; @@ -689,7 +708,7 @@ pci_resource_len(struct pci_dev *pdev, int bar) { struct resource_list_entry *rle; - if ((rle = linux_pci_get_bar(pdev, bar)) == NULL) + if ((rle = linux_pci_get_bar(pdev, bar, true)) == NULL) return (0); return (rle->count); } diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index c215083d3121..702f9fc3aa05 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -5365,7 +5365,7 @@ DB_SHOW_COMMAND(pciregs, db_pci_dump) } #endif /* DDB */ -static struct resource * +struct resource * pci_reserve_map(device_t dev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int num, u_int flags) diff --git a/sys/dev/pci/pci_private.h b/sys/dev/pci/pci_private.h index 095f22db69a9..4c6c8ddf051a 100644 --- a/sys/dev/pci/pci_private.h +++ b/sys/dev/pci/pci_private.h @@ -163,6 +163,10 @@ void pci_read_bar(device_t dev, int reg, pci_addr_t *mapp, struct pci_map *pci_add_bar(device_t dev, int reg, pci_addr_t value, pci_addr_t size); +struct resource *pci_reserve_map(device_t dev, device_t child, int type, + int *rid, rman_res_t start, rman_res_t end, + rman_res_t count, u_int num, u_int flags); + struct resource *pci_alloc_multi_resource(device_t dev, device_t child, int type, int *rid, rman_res_t start, rman_res_t end, rman_res_t count, u_long num, u_int flags); |