[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 <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2025-10-14 13:46:54 +01:00
parent e80818e4f6
commit 94902ae187
3 changed files with 79 additions and 37 deletions

View 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
*

View 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)
*

View 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 );
/**