]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[pci] Handle sizing of 64-bit BARs
authorMichael Brown <mcb30@ipxe.org>
Tue, 14 Oct 2025 12:46:54 +0000 (13:46 +0100)
committerMichael Brown <mcb30@ipxe.org>
Tue, 14 Oct 2025 13:43:50 +0000 (14:43 +0100)
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 <mcb30@ipxe.org>
src/drivers/bus/pci.c
src/drivers/bus/pciextra.c
src/include/ipxe/pci.h

index 60740ac18ab7b353deeba9f65b729df0d2ee5e45..f3ea6d47808dc09e63fdc9a5ea5881ba8bb9cffe 100644 (file)
@@ -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
  *
index 1eeb9b2a0f0a3fc26c83399d9ef8475cb3167242..3654a2d1cbb24ca4c78982c156b3f513356ad869 100644 (file)
@@ -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)
  *
index 8c6d9e4e28744d75c5bbeecdec322bbadc0b3352..6eeabef3067e5ca20701d62c56a80263ed5713d7 100644 (file)
@@ -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 );
 
 /**