From: Michael Brown Date: Tue, 14 Oct 2025 12:46:54 +0000 (+0100) Subject: [pci] Handle sizing of 64-bit BARs X-Git-Tag: rolling/bin~131 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=94902ae1877f8799cdaa6fe627034f26d610552b;p=thirdparty%2Fipxe.git [pci] Handle sizing of 64-bit BARs Provide pci_bar_set() to handle setting the base address for a potentially 64-bit BAR, and rewrite pci_bar_size() to correctly handle sizing of 64-bit BARs. Signed-off-by: Michael Brown --- diff --git a/src/drivers/bus/pci.c b/src/drivers/bus/pci.c index 60740ac18..f3ea6d478 100644 --- a/src/drivers/bus/pci.c +++ b/src/drivers/bus/pci.c @@ -104,6 +104,82 @@ unsigned long pci_bar_start ( struct pci_device *pci, unsigned int reg ) { } } +/** + * Set the start of a PCI BAR + * + * @v pci PCI device + * @v reg PCI register number + * @v start BAR start address + */ +void pci_bar_set ( struct pci_device *pci, unsigned int reg, + unsigned long start ) { + unsigned int type; + uint32_t low; + uint32_t high; + + /* Check for a 64-bit BAR */ + pci_read_config_dword ( pci, reg, &low ); + type = ( low & ( PCI_BASE_ADDRESS_SPACE_IO | + PCI_BASE_ADDRESS_MEM_TYPE_MASK ) ); + + /* Write low 32 bits */ + low = start; + pci_write_config_dword ( pci, reg, low ); + + /* Write high 32 bits, if applicable */ + if ( type == PCI_BASE_ADDRESS_MEM_TYPE_64 ) { + if ( sizeof ( unsigned long ) > sizeof ( uint32_t ) ) { + high = ( ( ( uint64_t ) start ) >> 32 ); + } else { + high = 0; + } + pci_write_config_dword ( pci, reg + 4, high ); + } +} + +/** + * Get the size of a PCI BAR + * + * @v pci PCI device + * @v reg PCI register number + * @ret size BAR size + * + * Most drivers should not need to call this function. It is not + * necessary to map the whole PCI BAR, only the portion that will be + * used for register access. Since register offsets are almost always + * fixed by hardware design, the length of the mapped portion will + * almost always be a compile-time constant. + */ +unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ) { + unsigned long start; + unsigned long size; + uint16_t cmd; + + /* Save the original command register and disable decoding */ + pci_read_config_word ( pci, PCI_COMMAND, &cmd ); + pci_write_config_word ( pci, PCI_COMMAND, + ( cmd & ~( PCI_COMMAND_MEM | + PCI_COMMAND_IO ) ) ); + + /* Save the original start address */ + start = pci_bar_start ( pci, reg ); + + /* Set all possible bits */ + pci_bar_set ( pci, reg, -1UL ); + + /* Determine size by finding lowest set bit */ + size = pci_bar_start ( pci, reg ); + size &= ( -size ); + + /* Restore the original start address */ + pci_bar_set ( pci, reg, start ); + + /* Restore the original command register */ + pci_write_config_word ( pci, PCI_COMMAND, cmd ); + + return size; +} + /** * Read membase and ioaddr for a PCI device * diff --git a/src/drivers/bus/pciextra.c b/src/drivers/bus/pciextra.c index 1eeb9b2a0..3654a2d1c 100644 --- a/src/drivers/bus/pciextra.c +++ b/src/drivers/bus/pciextra.c @@ -79,42 +79,6 @@ int pci_find_next_capability ( struct pci_device *pci, int pos, int cap ) { return pci_find_capability_common ( pci, new_pos, cap ); } -/** - * Find the size of a PCI BAR - * - * @v pci PCI device - * @v reg PCI register number - * @ret size BAR size - * - * It should not be necessary for any Etherboot code to call this - * function. - */ -unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ) { - uint16_t cmd; - uint32_t start, size; - - /* Save the original command register */ - pci_read_config_word ( pci, PCI_COMMAND, &cmd ); - /* Save the original bar */ - pci_read_config_dword ( pci, reg, &start ); - /* Compute which bits can be set */ - pci_write_config_dword ( pci, reg, ~0 ); - pci_read_config_dword ( pci, reg, &size ); - /* Restore the original size */ - pci_write_config_dword ( pci, reg, start ); - /* Find the significant bits */ - /* Restore the original command register. This reenables decoding. */ - pci_write_config_word ( pci, PCI_COMMAND, cmd ); - if ( start & PCI_BASE_ADDRESS_SPACE_IO ) { - size &= ~PCI_BASE_ADDRESS_IO_MASK; - } else { - size &= ~PCI_BASE_ADDRESS_MEM_MASK; - } - /* Find the lowest bit set */ - size = size & ~( size - 1 ); - return size; -} - /** * Perform PCI Express function-level reset (FLR) * diff --git a/src/include/ipxe/pci.h b/src/include/ipxe/pci.h index 8c6d9e4e2..6eeabef30 100644 --- a/src/include/ipxe/pci.h +++ b/src/include/ipxe/pci.h @@ -314,6 +314,9 @@ struct pci_driver { extern void adjust_pci_device ( struct pci_device *pci ); extern unsigned long pci_bar_start ( struct pci_device *pci, unsigned int reg ); +extern void pci_bar_set ( struct pci_device *pci, unsigned int reg, + unsigned long start ); +extern unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ); extern int pci_read_config ( struct pci_device *pci ); extern int pci_find_next ( struct pci_device *pci, uint32_t *busdevfn ); extern int pci_find_driver ( struct pci_device *pci ); @@ -322,7 +325,6 @@ extern void pci_remove ( struct pci_device *pci ); extern int pci_find_capability ( struct pci_device *pci, int capability ); extern int pci_find_next_capability ( struct pci_device *pci, int pos, int capability ); -extern unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ); extern void pci_reset ( struct pci_device *pci, unsigned int exp ); /**