From 27e886c67b63b9a381f87238ed7a856775199fd9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 24 Sep 2020 21:41:35 +0100 Subject: [efi] Use address offset as reported by EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL Retrieve the address windows and translation offsets for the appropriate PCI root bridge and use them to adjust the PCI BAR address prior to calling ioremap(). Originally-implemented-by: Pankaj Bansal Signed-off-by: Michael Brown --- src/include/ipxe/acpi.h | 132 +++++++++++++++++++++++++++++++++++++ src/include/ipxe/efi/efi_pci_api.h | 13 ---- src/interface/efi/efi_pci.c | 75 ++++++++++++++++++++- 3 files changed, 206 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/include/ipxe/acpi.h b/src/include/ipxe/acpi.h index 78f4025..e41bd28 100644 --- a/src/include/ipxe/acpi.h +++ b/src/include/ipxe/acpi.h @@ -19,6 +19,138 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +/** An ACPI small resource descriptor header */ +struct acpi_small_resource { + /** Tag byte */ + uint8_t tag; +} __attribute__ (( packed )); + +/** ACPI small resource length mask */ +#define ACPI_SMALL_LEN_MASK 0x03 + +/** An ACPI end resource descriptor */ +#define ACPI_END_RESOURCE 0x78 + +/** An ACPI end resource descriptor */ +struct acpi_end_resource { + /** Header */ + struct acpi_small_resource hdr; + /** Checksum */ + uint8_t checksum; +} __attribute__ (( packed )); + +/** An ACPI large resource descriptor header */ +struct acpi_large_resource { + /** Tag byte */ + uint8_t tag; + /** Length of data items */ + uint16_t len; +} __attribute__ (( packed )); + +/** ACPI large resource flag */ +#define ACPI_LARGE 0x80 + +/** An ACPI QWORD address space resource descriptor */ +#define ACPI_QWORD_ADDRESS_SPACE_RESOURCE 0x8a + +/** An ACPI QWORD address space resource descriptor */ +struct acpi_qword_address_space_resource { + /** Header */ + struct acpi_large_resource hdr; + /** Resource type */ + uint8_t type; + /** General flags */ + uint8_t general; + /** Type-specific flags */ + uint8_t specific; + /** Granularity */ + uint64_t granularity; + /** Minimum address */ + uint64_t min; + /** Maximum address */ + uint64_t max; + /** Translation offset */ + uint64_t offset; + /** Length */ + uint64_t len; +} __attribute__ (( packed )); + +/** A memory address space type */ +#define ACPI_ADDRESS_TYPE_MEM 0x00 + +/** An ACPI resource descriptor */ +union acpi_resource { + /** Tag byte */ + uint8_t tag; + /** Small resource descriptor */ + struct acpi_small_resource small; + /** End resource descriptor */ + struct acpi_end_resource end; + /** Large resource descriptor */ + struct acpi_large_resource large; + /** QWORD address space resource descriptor */ + struct acpi_qword_address_space_resource qword; +}; + +/** + * Get ACPI resource tag + * + * @v res ACPI resource descriptor + * @ret tag Resource tag + */ +static inline unsigned int acpi_resource_tag ( union acpi_resource *res ) { + + return ( ( res->tag & ACPI_LARGE ) ? + res->tag : ( res->tag & ~ACPI_SMALL_LEN_MASK ) ); +} + +/** + * Get length of ACPI small resource descriptor + * + * @v res Small resource descriptor + * @ret len Length of descriptor + */ +static inline size_t acpi_small_len ( struct acpi_small_resource *res ) { + + return ( sizeof ( *res ) + ( res->tag & ACPI_SMALL_LEN_MASK ) ); +} + +/** + * Get length of ACPI large resource descriptor + * + * @v res Large resource descriptor + * @ret len Length of descriptor + */ +static inline size_t acpi_large_len ( struct acpi_large_resource *res ) { + + return ( sizeof ( *res ) + le16_to_cpu ( res->len ) ); +} + +/** + * Get length of ACPI resource descriptor + * + * @v res ACPI resource descriptor + * @ret len Length of descriptor + */ +static inline size_t acpi_resource_len ( union acpi_resource *res ) { + + return ( ( res->tag & ACPI_LARGE ) ? + acpi_large_len ( &res->large ) : + acpi_small_len ( &res->small ) ); +} + +/** + * Get next ACPI resource descriptor + * + * @v res ACPI resource descriptor + * @ret next Next ACPI resource descriptor + */ +static inline union acpi_resource * +acpi_resource_next ( union acpi_resource *res ) { + + return ( ( ( void * ) res ) + acpi_resource_len ( res ) ); +} + /** * An ACPI description header * diff --git a/src/include/ipxe/efi/efi_pci_api.h b/src/include/ipxe/efi/efi_pci_api.h index df28cef..887d5ee 100644 --- a/src/include/ipxe/efi/efi_pci_api.h +++ b/src/include/ipxe/efi/efi_pci_api.h @@ -148,17 +148,4 @@ PCIAPI_INLINE ( efi, pci_write_config_dword ) ( struct pci_device *pci, value ); } -/** - * Map PCI bus address as an I/O address - * - * @v bus_addr PCI bus address - * @v len Length of region - * @ret io_addr I/O address, or NULL on error - */ -static inline __always_inline void * -PCIAPI_INLINE ( efi, pci_ioremap ) ( struct pci_device *pci __unused, - unsigned long bus_addr, size_t len ) { - return ioremap ( bus_addr, len ); -} - #endif /* _IPXE_EFI_PCI_API_H */ diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c index 6a92909..93a3738 100644 --- a/src/interface/efi/efi_pci.c +++ b/src/interface/efi/efi_pci.c @@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -224,6 +225,78 @@ int efipci_write ( struct pci_device *pci, unsigned long location, return rc; } +/** + * Map PCI bus address as an I/O address + * + * @v bus_addr PCI bus address + * @v len Length of region + * @ret io_addr I/O address, or NULL on error + */ +void * efipci_ioremap ( struct pci_device *pci, unsigned long bus_addr, + size_t len ) { + union { + union acpi_resource *res; + void *raw; + } u; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root; + EFI_HANDLE handle; + unsigned int tag; + uint64_t offset; + uint64_t start; + uint64_t end; + void *io_addr = NULL; + EFI_STATUS efirc; + int rc; + + /* Open root bridge */ + if ( ( rc = efipci_root_open ( pci, &handle, &root ) ) != 0 ) + goto err_root; + + /* Get ACPI resource descriptors */ + if ( ( efirc = root->Configuration ( root, &u.raw ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( pci, "EFIPCI " PCI_FMT " cannot get configuration: " + "%s\n", PCI_ARGS ( pci ), strerror ( rc ) ); + goto err_config; + } + + /* Parse resource descriptors */ + for ( ; ( ( tag = acpi_resource_tag ( u.res ) ) != ACPI_END_RESOURCE ) ; + u.res = acpi_resource_next ( u.res ) ) { + + /* Ignore anything other than an address space descriptor */ + if ( tag != ACPI_QWORD_ADDRESS_SPACE_RESOURCE ) + continue; + + /* Ignore descriptors that do not cover this memory range */ + if ( u.res->qword.type != ACPI_ADDRESS_TYPE_MEM ) + continue; + offset = le64_to_cpu ( u.res->qword.offset ); + start = ( offset + le64_to_cpu ( u.res->qword.min ) ); + end = ( start + le64_to_cpu ( u.res->qword.len ) ); + if ( ( bus_addr < start ) || ( ( bus_addr + len ) > end ) ) + continue; + + /* Use this address space descriptor */ + DBGC2 ( pci, "EFIPCI " PCI_FMT " %08lx+%zx -> ", + PCI_ARGS ( pci ), bus_addr, len ); + bus_addr -= offset; + DBGC2 ( pci, "%08lx\n", bus_addr ); + io_addr = ioremap ( bus_addr, len ); + break; + } + if ( ! io_addr ) { + DBGC ( pci, "EFIPCI " PCI_FMT " %08lx+%zx is not within " + "root bridge address space\n", + PCI_ARGS ( pci ), bus_addr, len ); + } + + err_config: + efipci_root_close ( handle ); + err_root: + return io_addr; +} + PROVIDE_PCIAPI_INLINE ( efi, pci_num_bus ); PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_byte ); PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_word ); @@ -231,7 +304,7 @@ PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_dword ); PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_byte ); PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_word ); PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_dword ); -PROVIDE_PCIAPI_INLINE ( efi, pci_ioremap ); +PROVIDE_PCIAPI ( efi, pci_ioremap, efipci_ioremap ); /****************************************************************************** * -- cgit v1.1