Add functions to verify and obtain PCI BARs (Base Address Registers). These new functions check that the requested BAR is of the right type and appears valid.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/hw/pci.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/hw/pci.h | 3 +++ 2 files changed, 61 insertions(+)
diff --git a/src/hw/pci.c b/src/hw/pci.c index a241d06..86b7d54 100644 --- a/src/hw/pci.c +++ b/src/hw/pci.c @@ -10,6 +10,7 @@ #include "pci.h" // pci_config_writel #include "pci_regs.h" // PCI_VENDOR_ID #include "romfile.h" // romfile_loadint +#include "stacks.h" // wait_preempt #include "string.h" // memset #include "util.h" // udelay #include "x86.h" // outl @@ -271,6 +272,63 @@ int pci_bridge_has_region(struct pci_device *pci, return pci_config_readb(pci->bdf, base) != 0; }
+// Enable PCI bus-mastering (ie, DMA) support on a pci device +void +pci_enable_busmaster(struct pci_device *pci) +{ + ASSERT32FLAT(); + wait_preempt(); + pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_MASTER); +} + +// Verify an IO bar and return it to the caller +u16 +pci_enable_iobar(struct pci_device *pci, u32 addr) +{ + ASSERT32FLAT(); + wait_preempt(); + u32 bar = pci_config_readl(pci->bdf, addr); + if (!(bar & PCI_BASE_ADDRESS_SPACE_IO)) { + warn_internalerror(); + return 0; + } + bar &= PCI_BASE_ADDRESS_IO_MASK; + if (bar == 0 || bar > 0xffff) { + warn_internalerror(); + return 0; + } + pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_IO); + return bar; +} + +// Verify a memory bar and return it to the caller +void * +pci_enable_membar(struct pci_device *pci, u32 addr) +{ + ASSERT32FLAT(); + wait_preempt(); + u32 bar = pci_config_readl(pci->bdf, addr); + if (bar & PCI_BASE_ADDRESS_SPACE_IO) { + warn_internalerror(); + return NULL; + } + if (bar & PCI_BASE_ADDRESS_MEM_TYPE_64) { + u32 high = pci_config_readl(pci->bdf, addr+4); + if (high) { + dprintf(1, "Can not map memory bar over 4Gig\n"); + return NULL; + } + } + bar &= PCI_BASE_ADDRESS_MEM_MASK; + if (bar + 4*1024*1024 < 20*1024*1024) { + // Bar doesn't look valid (it is in last 4M or first 16M) + warn_internalerror(); + return NULL; + } + pci_config_maskw(pci->bdf, PCI_COMMAND, 0, PCI_COMMAND_MEMORY); + return (void*)bar; +} + void pci_reboot(void) { diff --git a/src/hw/pci.h b/src/hw/pci.h index fc5e7b9..8e39753 100644 --- a/src/hw/pci.h +++ b/src/hw/pci.h @@ -126,6 +126,9 @@ struct pci_device *pci_find_init_device(const struct pci_device_id *ids u8 pci_find_capability(struct pci_device *pci, u8 cap_id, u8 cap); int pci_bridge_has_region(struct pci_device *pci, enum pci_region_type region_type); +void pci_enable_busmaster(struct pci_device *pci); +u16 pci_enable_iobar(struct pci_device *pci, u32 addr); +void *pci_enable_membar(struct pci_device *pci, u32 addr); void pci_reboot(void);
#endif