aboutsummaryrefslogtreecommitdiff
path: root/drivers/pci/pci-uclass.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/pci-uclass.c')
-rw-r--r--drivers/pci/pci-uclass.c185
1 files changed, 85 insertions, 100 deletions
diff --git a/drivers/pci/pci-uclass.c b/drivers/pci/pci-uclass.c
index 33dda00002..970ee1adf1 100644
--- a/drivers/pci/pci-uclass.c
+++ b/drivers/pci/pci-uclass.c
@@ -645,7 +645,11 @@ int dm_pci_hose_probe_bus(struct udevice *bus)
return log_msg_ret("probe", -EINVAL);
}
- ea_pos = dm_pci_find_capability(bus, PCI_CAP_ID_EA);
+ if (IS_ENABLED(CONFIG_PCI_ENHANCED_ALLOCATION))
+ ea_pos = dm_pci_find_capability(bus, PCI_CAP_ID_EA);
+ else
+ ea_pos = 0;
+
if (ea_pos) {
dm_pci_read_config8(bus, ea_pos + sizeof(u32) + sizeof(u8),
&reg);
@@ -1013,7 +1017,22 @@ static void decode_regions(struct pci_controller *hose, ofnode parent_node,
if (!IS_ENABLED(CONFIG_SYS_PCI_64BIT) &&
type == PCI_REGION_MEM && upper_32_bits(pci_addr)) {
- debug(" - beyond the 32-bit boundary, ignoring\n");
+ debug(" - pci_addr beyond the 32-bit boundary, ignoring\n");
+ continue;
+ }
+
+ if (!IS_ENABLED(CONFIG_PHYS_64BIT) && upper_32_bits(addr)) {
+ debug(" - addr beyond the 32-bit boundary, ignoring\n");
+ continue;
+ }
+
+ if (~((pci_addr_t)0) - pci_addr < size) {
+ debug(" - PCI range exceeds max address, ignoring\n");
+ continue;
+ }
+
+ if (~((phys_addr_t)0) - addr < size) {
+ debug(" - phys range exceeds max address, ignoring\n");
continue;
}
@@ -1375,131 +1394,84 @@ void dm_pci_write_bar32(struct udevice *dev, int barnum, u32 addr)
dm_pci_write_config32(dev, bar, addr);
}
-static int _dm_pci_bus_to_phys(struct udevice *ctlr,
- pci_addr_t bus_addr, unsigned long flags,
- unsigned long skip_mask, phys_addr_t *pa)
+phys_addr_t dm_pci_bus_to_phys(struct udevice *dev, pci_addr_t bus_addr,
+ size_t len, unsigned long mask,
+ unsigned long flags)
{
- struct pci_controller *hose = dev_get_uclass_priv(ctlr);
+ struct udevice *ctlr;
+ struct pci_controller *hose;
struct pci_region *res;
+ pci_addr_t offset;
int i;
- if (hose->region_count == 0) {
- *pa = bus_addr;
- return 0;
- }
+ /* The root controller has the region information */
+ ctlr = pci_get_controller(dev);
+ hose = dev_get_uclass_priv(ctlr);
+
+ if (hose->region_count == 0)
+ return bus_addr;
for (i = 0; i < hose->region_count; i++) {
res = &hose->regions[i];
- if (((res->flags ^ flags) & PCI_REGION_TYPE) != 0)
+ if ((res->flags & mask) != flags)
continue;
- if (res->flags & skip_mask)
+ if (bus_addr < res->bus_start)
continue;
- if (bus_addr >= res->bus_start &&
- (bus_addr - res->bus_start) < res->size) {
- *pa = (bus_addr - res->bus_start + res->phys_start);
- return 0;
- }
- }
-
- return 1;
-}
-
-phys_addr_t dm_pci_bus_to_phys(struct udevice *dev, pci_addr_t bus_addr,
- unsigned long flags)
-{
- phys_addr_t phys_addr = 0;
- struct udevice *ctlr;
- int ret;
+ offset = bus_addr - res->bus_start;
+ if (offset >= res->size)
+ continue;
- /* The root controller has the region information */
- ctlr = pci_get_controller(dev);
+ if (len > res->size - offset)
+ continue;
- /*
- * if PCI_REGION_MEM is set we do a two pass search with preference
- * on matches that don't have PCI_REGION_SYS_MEMORY set
- */
- if ((flags & PCI_REGION_TYPE) == PCI_REGION_MEM) {
- ret = _dm_pci_bus_to_phys(ctlr, bus_addr,
- flags, PCI_REGION_SYS_MEMORY,
- &phys_addr);
- if (!ret)
- return phys_addr;
+ return res->phys_start + offset;
}
- ret = _dm_pci_bus_to_phys(ctlr, bus_addr, flags, 0, &phys_addr);
-
- if (ret)
- puts("pci_hose_bus_to_phys: invalid physical address\n");
-
- return phys_addr;
+ puts("pci_hose_bus_to_phys: invalid physical address\n");
+ return 0;
}
-static int _dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t phys_addr,
- unsigned long flags, unsigned long skip_mask,
- pci_addr_t *ba)
+pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t phys_addr,
+ size_t len, unsigned long mask,
+ unsigned long flags)
{
- struct pci_region *res;
struct udevice *ctlr;
- pci_addr_t bus_addr;
- int i;
struct pci_controller *hose;
+ struct pci_region *res;
+ phys_addr_t offset;
+ int i;
/* The root controller has the region information */
ctlr = pci_get_controller(dev);
hose = dev_get_uclass_priv(ctlr);
- if (hose->region_count == 0) {
- *ba = phys_addr;
- return 0;
- }
+ if (hose->region_count == 0)
+ return phys_addr;
for (i = 0; i < hose->region_count; i++) {
res = &hose->regions[i];
- if (((res->flags ^ flags) & PCI_REGION_TYPE) != 0)
+ if ((res->flags & mask) != flags)
continue;
- if (res->flags & skip_mask)
+ if (phys_addr < res->phys_start)
continue;
- bus_addr = phys_addr - res->phys_start + res->bus_start;
-
- if (bus_addr >= res->bus_start &&
- (bus_addr - res->bus_start) < res->size) {
- *ba = bus_addr;
- return 0;
- }
- }
-
- return 1;
-}
+ offset = phys_addr - res->phys_start;
+ if (offset >= res->size)
+ continue;
-pci_addr_t dm_pci_phys_to_bus(struct udevice *dev, phys_addr_t phys_addr,
- unsigned long flags)
-{
- pci_addr_t bus_addr = 0;
- int ret;
+ if (len > res->size - offset)
+ continue;
- /*
- * if PCI_REGION_MEM is set we do a two pass search with preference
- * on matches that don't have PCI_REGION_SYS_MEMORY set
- */
- if ((flags & PCI_REGION_TYPE) == PCI_REGION_MEM) {
- ret = _dm_pci_phys_to_bus(dev, phys_addr, flags,
- PCI_REGION_SYS_MEMORY, &bus_addr);
- if (!ret)
- return bus_addr;
+ return res->bus_start + offset;
}
- ret = _dm_pci_phys_to_bus(dev, phys_addr, flags, 0, &bus_addr);
-
- if (ret)
- puts("pci_hose_phys_to_bus: invalid physical address\n");
-
- return bus_addr;
+ puts("pci_hose_phys_to_bus: invalid physical address\n");
+ return 0;
}
static phys_addr_t dm_pci_map_ea_virt(struct udevice *dev, int ea_off,
@@ -1533,8 +1505,9 @@ static phys_addr_t dm_pci_map_ea_virt(struct udevice *dev, int ea_off,
return addr;
}
-static void *dm_pci_map_ea_bar(struct udevice *dev, int bar, int flags,
- int ea_off, struct pci_child_plat *pdata)
+static void *dm_pci_map_ea_bar(struct udevice *dev, int bar, size_t offset,
+ size_t len, int ea_off,
+ struct pci_child_plat *pdata)
{
int ea_cnt, i, entry_size;
int bar_id = (bar - PCI_BASE_ADDRESS_0) >> 2;
@@ -1576,14 +1549,18 @@ static void *dm_pci_map_ea_bar(struct udevice *dev, int bar, int flags,
if (IS_ENABLED(CONFIG_PCI_SRIOV))
addr += dm_pci_map_ea_virt(dev, ea_off, pdata);
+ if (~((phys_addr_t)0) - addr < offset)
+ return NULL;
+
/* size ignored for now */
- return map_physmem(addr, 0, flags);
+ return map_physmem(addr + offset, len, MAP_NOCACHE);
}
return 0;
}
-void *dm_pci_map_bar(struct udevice *dev, int bar, int flags)
+void *dm_pci_map_bar(struct udevice *dev, int bar, size_t offset, size_t len,
+ unsigned long mask, unsigned long flags)
{
struct pci_child_plat *pdata = dev_get_parent_plat(dev);
struct udevice *udev = dev;
@@ -1606,21 +1583,29 @@ void *dm_pci_map_bar(struct udevice *dev, int bar, int flags)
* Incase of virtual functions, pdata will help read VF BEI
* and EA entry size.
*/
- ea_off = dm_pci_find_capability(udev, PCI_CAP_ID_EA);
+ if (IS_ENABLED(CONFIG_PCI_ENHANCED_ALLOCATION))
+ ea_off = dm_pci_find_capability(udev, PCI_CAP_ID_EA);
+ else
+ ea_off = 0;
+
if (ea_off)
- return dm_pci_map_ea_bar(udev, bar, flags, ea_off, pdata);
+ return dm_pci_map_ea_bar(udev, bar, offset, len, ea_off, pdata);
/* read BAR address */
dm_pci_read_config32(udev, bar, &bar_response);
pci_bus_addr = (pci_addr_t)(bar_response & ~0xf);
+ if (~((pci_addr_t)0) - pci_bus_addr < offset)
+ return NULL;
+
/*
- * Pass "0" as the length argument to pci_bus_to_virt. The arg
- * isn't actually used on any platform because U-Boot assumes a static
- * linear mapping. In the future, this could read the BAR size
- * and pass that as the size if needed.
+ * Forward the length argument to dm_pci_bus_to_virt. The length will
+ * be used to check that the entire address range has been declared as
+ * a PCI range, but a better check would be to probe for the size of
+ * the bar and prevent overflow more locally.
*/
- return dm_pci_bus_to_virt(udev, pci_bus_addr, flags, 0, MAP_NOCACHE);
+ return dm_pci_bus_to_virt(udev, pci_bus_addr + offset, len, mask, flags,
+ MAP_NOCACHE);
}
static int _dm_pci_find_next_capability(struct udevice *dev, u8 pos, int cap)