From 43d84147d9c5d520e0b4e03d6162fc866ecff64c Mon Sep 17 00:00:00 2001 From: Andy Ross Date: Thu, 11 Jun 2020 06:49:49 -0700 Subject: [PATCH] drivers/pcie: Fix BAR address size limitation The PCI API was originally limited to 32 bit addresses. Even though it had code to skip over the high word in 64 bit BAR entries, it refused to use it and returned a 32 bit value. Some devices in the wild have default mappings from the firmware for devices above 4G. Also remove the "iobar" API. It's dead code, we don't call it and we don't test it. IO space BAR entries are a legacy feature from way, way back in PCI history (I genuinely have never heard of a real device that uses them!). And there's no difference in format between one of these and a 32 bit "memory" BAR anyway, someone who actually had this requirement could just use the existing API without worry. Signed-off-by: Andy Ross --- drivers/pcie/pcie.c | 44 ++++++++++++------------------------- include/drivers/pcie/pcie.h | 16 +++----------- 2 files changed, 17 insertions(+), 43 deletions(-) diff --git a/drivers/pcie/pcie.c b/drivers/pcie/pcie.c index 42693165945..147e37fc8d0 100644 --- a/drivers/pcie/pcie.c +++ b/drivers/pcie/pcie.c @@ -46,37 +46,26 @@ void pcie_set_cmd(pcie_bdf_t bdf, uint32_t bits, bool on) pcie_conf_write(bdf, PCIE_CONF_CMDSTAT, cmdstat); } -static uint32_t pcie_get_bar(pcie_bdf_t bdf, unsigned int index, bool io) +uintptr_t pcie_get_mbar(pcie_bdf_t bdf, unsigned int index) { - int bar; - uint32_t data; + uint32_t reg, bar; + uintptr_t addr = PCIE_CONF_BAR_NONE; - for (bar = PCIE_CONF_BAR0; bar <= PCIE_CONF_BAR5; ++bar) { - data = pcie_conf_read(bdf, bar); - if (data == PCIE_CONF_BAR_NONE) { - continue; - } - - if ((PCIE_CONF_BAR_IO(data) && io) || - (PCIE_CONF_BAR_MEM(data) && !io)) { - if (index == 0) { - return PCIE_CONF_BAR_ADDR(data); - } - - --index; - } - - if (PCIE_CONF_BAR_64(data)) { - ++bar; + reg = PCIE_CONF_BAR0; + for (bar = 0; bar < index && reg <= PCIE_CONF_BAR5; bar++) { + if (PCIE_CONF_BAR_64(pcie_conf_read(bdf, reg++))) { + reg++; } } - return PCIE_CONF_BAR_NONE; -} + if (bar == index) { + addr = pcie_conf_read(bdf, reg++); + if (IS_ENABLED(CONFIG_64BIT) && PCIE_CONF_BAR_64(addr)) { + addr |= ((uint64_t)pcie_conf_read(bdf, reg)) << 32; + } + } -uint32_t pcie_get_mbar(pcie_bdf_t bdf, unsigned int index) -{ - return pcie_get_bar(bdf, index, false); + return PCIE_CONF_BAR_ADDR(addr); } unsigned int pcie_wired_irq(pcie_bdf_t bdf) @@ -86,11 +75,6 @@ unsigned int pcie_wired_irq(pcie_bdf_t bdf) return PCIE_CONF_INTR_IRQ(data); } -uint32_t pcie_get_iobar(pcie_bdf_t bdf, unsigned int index) -{ - return pcie_get_bar(bdf, index, true); -} - void pcie_irq_enable(pcie_bdf_t bdf, unsigned int irq) { #if CONFIG_PCIE_MSI diff --git a/include/drivers/pcie/pcie.h b/include/drivers/pcie/pcie.h index 4db3243a28c..c77caef8e27 100644 --- a/include/drivers/pcie/pcie.h +++ b/include/drivers/pcie/pcie.h @@ -74,7 +74,7 @@ extern bool pcie_probe(pcie_bdf_t bdf, pcie_id_t id); * @brief Get the nth MMIO address assigned to an endpoint. * @param bdf the PCI(e) endpoint * @param index (0-based) index - * @return the (32-bit) address, or PCI_CONF_BAR_NONE if nonexistent. + * @return the address, or PCI_CONF_BAR_NONE if nonexistent. * * A PCI(e) endpoint has 0 or more memory-mapped regions. This function * allows the caller to enumerate them by calling with index=0..n. If @@ -82,17 +82,7 @@ extern bool pcie_probe(pcie_bdf_t bdf, pcie_id_t id); * are order-preserving with respect to the endpoint BARs: e.g., index 0 * will return the lowest-numbered memory BAR on the endpoint. */ -extern uint32_t pcie_get_mbar(pcie_bdf_t bdf, unsigned int index); - -/** - * @brief Get the nth I/O address assigned to an endpoint. - * @param bdf the PCI(e) endpoint - * @param index (0-based) index - * @return the (32-bit) address, or PCI_CONF_BAR_NONE if nonexistent. - * - * Analogous to pcie_get_mbar(), except returns I/O region data. - */ -extern uint32_t pcie_get_iobar(pcie_bdf_t bdf, unsigned int index); +extern uintptr_t pcie_get_mbar(pcie_bdf_t bdf, unsigned int index); /** * @brief Set or reset bits in the endpoint command/status register. @@ -180,7 +170,7 @@ extern void pcie_irq_enable(pcie_bdf_t bdf, unsigned int irq); #define PCIE_CONF_BAR_IO(w) (((w) & 0x00000001U) == 0x00000001U) #define PCIE_CONF_BAR_MEM(w) (((w) & 0x00000001U) != 0x00000001U) #define PCIE_CONF_BAR_64(w) (((w) & 0x00000006U) == 0x00000004U) -#define PCIE_CONF_BAR_ADDR(w) ((w) & 0xFFFFFFF0U) +#define PCIE_CONF_BAR_ADDR(w) ((w) & ~0xfUL) #define PCIE_CONF_BAR_NONE 0U /*