summaryrefslogtreecommitdiffstats
path: root/src/interface/efi/efi_pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/interface/efi/efi_pci.c')
-rw-r--r--src/interface/efi/efi_pci.c423
1 files changed, 217 insertions, 206 deletions
diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c
index e2eeeb344..4bf3977c5 100644
--- a/src/interface/efi/efi_pci.c
+++ b/src/interface/efi/efi_pci.c
@@ -22,8 +22,10 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
#include <ipxe/pci.h>
#include <ipxe/acpi.h>
@@ -63,33 +65,43 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
/**
- * Check for a matching PCI root bridge I/O protocol
+ * Find closest bus:dev.fn address range within a root bridge
*
- * @v pci PCI device
+ * @v pci Starting PCI device
* @v handle EFI PCI root bridge handle
- * @v root EFI PCI root bridge I/O protocol
+ * @v range PCI bus:dev.fn address range to fill in
* @ret rc Return status code
*/
-static int efipci_root_match ( struct pci_device *pci, EFI_HANDLE handle,
- EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root ) {
+static int efipci_discover_one ( struct pci_device *pci, EFI_HANDLE handle,
+ struct pci_range *range ) {
+ EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root;
union {
union acpi_resource *res;
void *raw;
- } u;
- unsigned int segment = PCI_SEG ( pci->busdevfn );
- unsigned int bus = PCI_BUS ( pci->busdevfn );
- unsigned int start;
- unsigned int end;
+ } acpi;
+ uint32_t best = 0;
+ uint32_t start;
+ uint32_t count;
+ uint32_t index;
unsigned int tag;
EFI_STATUS efirc;
int rc;
- /* Check segment number */
- if ( root->SegmentNumber != segment )
- return -ENOENT;
+ /* Return empty range on error */
+ range->start = 0;
+ range->count = 0;
+
+ /* Open root bridge I/O protocol */
+ if ( ( rc = efi_open ( handle, &efi_pci_root_bridge_io_protocol_guid,
+ &root ) ) != 0 ) {
+ DBGC ( pci, "EFIPCI " PCI_FMT " cannot open %s: %s\n",
+ PCI_ARGS ( pci ), efi_handle_name ( handle ),
+ strerror ( rc ) );
+ return rc;
+ }
/* Get ACPI resource descriptors */
- if ( ( efirc = root->Configuration ( root, &u.raw ) ) != 0 ) {
+ if ( ( efirc = root->Configuration ( root, &acpi.raw ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( pci, "EFIPCI " PCI_FMT " cannot get configuration for "
"%s: %s\n", PCI_ARGS ( pci ),
@@ -97,57 +109,79 @@ static int efipci_root_match ( struct pci_device *pci, EFI_HANDLE handle,
return rc;
}
- /* Assume success if no bus number range descriptors are found */
- rc = 0;
-
/* Parse resource descriptors */
- for ( ; ( ( tag = acpi_resource_tag ( u.res ) ) != ACPI_END_RESOURCE ) ;
- u.res = acpi_resource_next ( u.res ) ) {
+ for ( ; ( ( tag = acpi_resource_tag ( acpi.res ) ) !=
+ ACPI_END_RESOURCE ) ;
+ acpi.res = acpi_resource_next ( acpi.res ) ) {
/* Ignore anything other than a bus number range descriptor */
if ( tag != ACPI_QWORD_ADDRESS_SPACE_RESOURCE )
continue;
- if ( u.res->qword.type != ACPI_ADDRESS_TYPE_BUS )
+ if ( acpi.res->qword.type != ACPI_ADDRESS_TYPE_BUS )
continue;
- /* Check for a matching bus number */
- start = le64_to_cpu ( u.res->qword.min );
- end = ( start + le64_to_cpu ( u.res->qword.len ) );
- if ( ( bus >= start ) && ( bus < end ) )
- return 0;
+ /* Get range for this descriptor */
+ start = PCI_BUSDEVFN ( root->SegmentNumber,
+ le64_to_cpu ( acpi.res->qword.min ),
+ 0, 0 );
+ count = PCI_BUSDEVFN ( 0, le64_to_cpu ( acpi.res->qword.len ),
+ 0, 0 );
+ DBGC2 ( pci, "EFIPCI " PCI_FMT " found %04x:[%02x-%02x] via "
+ "%s\n", PCI_ARGS ( pci ), root->SegmentNumber,
+ PCI_BUS ( start ), PCI_BUS ( start + count - 1 ),
+ efi_handle_name ( handle ) );
+
+ /* Check for a matching or new closest range */
+ index = ( pci->busdevfn - start );
+ if ( ( index < count ) || ( index > best ) ) {
+ range->start = start;
+ range->count = count;
+ best = index;
+ }
- /* We have seen at least one non-matching range
- * descriptor, so assume failure unless we find a
- * subsequent match.
- */
- rc = -ENOENT;
+ /* Stop if this range contains the target bus:dev.fn address */
+ if ( index < count )
+ break;
}
- return rc;
+ /* If no range descriptors were seen, assume that the root
+ * bridge has a single bus.
+ */
+ if ( ! range->count ) {
+ range->start = PCI_BUSDEVFN ( root->SegmentNumber, 0, 0, 0 );
+ range->count = PCI_BUSDEVFN ( 0, 1, 0, 0 );
+ }
+
+ return 0;
}
/**
- * Open EFI PCI root bridge I/O protocol
+ * Find closest bus:dev.fn address range within any root bridge
*
- * @v pci PCI device
- * @ret handle EFI PCI root bridge handle
- * @ret root EFI PCI root bridge I/O protocol, or NULL if not found
+ * @v pci Starting PCI device
+ * @v range PCI bus:dev.fn address range to fill in
+ * @v handle PCI root bridge I/O handle to fill in
* @ret rc Return status code
*/
-static int efipci_root_open ( struct pci_device *pci, EFI_HANDLE *handle,
- EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL **root ) {
+static int efipci_discover_any ( struct pci_device *pci,
+ struct pci_range *range,
+ EFI_HANDLE *handle ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ uint32_t best = 0;
+ uint32_t index;
+ struct pci_range tmp;
EFI_HANDLE *handles;
UINTN num_handles;
- union {
- void *interface;
- EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root;
- } u;
- EFI_STATUS efirc;
UINTN i;
+ EFI_STATUS efirc;
int rc;
- /* Enumerate all handles */
+ /* Return an empty range and no handle on error */
+ range->start = 0;
+ range->count = 0;
+ *handle = NULL;
+
+ /* Enumerate all root bridge I/O protocol handles */
if ( ( efirc = bs->LocateHandleBuffer ( ByProtocol,
&efi_pci_root_bridge_io_protocol_guid,
NULL, &num_handles, &handles ) ) != 0 ) {
@@ -157,48 +191,87 @@ static int efipci_root_open ( struct pci_device *pci, EFI_HANDLE *handle,
goto err_locate;
}
- /* Look for matching root bridge I/O protocol */
+ /* Iterate over all root bridge I/O protocols */
for ( i = 0 ; i < num_handles ; i++ ) {
- *handle = handles[i];
- if ( ( efirc = bs->OpenProtocol ( *handle,
- &efi_pci_root_bridge_io_protocol_guid,
- &u.interface, efi_image_handle, *handle,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) {
- rc = -EEFI ( efirc );
- DBGC ( pci, "EFIPCI " PCI_FMT " cannot open %s: %s\n",
- PCI_ARGS ( pci ), efi_handle_name ( *handle ),
- strerror ( rc ) );
+
+ /* Get matching or closest range for this root bridge */
+ if ( ( rc = efipci_discover_one ( pci, handles[i],
+ &tmp ) ) != 0 )
continue;
+
+ /* Check for a matching or new closest range */
+ index = ( pci->busdevfn - tmp.start );
+ if ( ( index < tmp.count ) || ( index > best ) ) {
+ range->start = tmp.start;
+ range->count = tmp.count;
+ best = index;
}
- if ( efipci_root_match ( pci, *handle, u.root ) == 0 ) {
- *root = u.root;
- bs->FreePool ( handles );
- return 0;
+
+ /* Stop if this range contains the target bus:dev.fn address */
+ if ( index < tmp.count ) {
+ *handle = handles[i];
+ break;
}
- bs->CloseProtocol ( *handle,
- &efi_pci_root_bridge_io_protocol_guid,
- efi_image_handle, *handle );
}
- DBGC ( pci, "EFIPCI " PCI_FMT " found no root bridge\n",
- PCI_ARGS ( pci ) );
- rc = -ENOENT;
+ /* Check for a range containing the target bus:dev.fn address */
+ if ( ! *handle ) {
+ rc = -ENOENT;
+ goto err_range;
+ }
+
+ /* Success */
+ rc = 0;
+
+ err_range:
bs->FreePool ( handles );
err_locate:
return rc;
}
/**
- * Close EFI PCI root bridge I/O protocol
+ * Find next PCI bus:dev.fn address range in system
*
- * @v handle EFI PCI root bridge handle
+ * @v busdevfn Starting PCI bus:dev.fn address
+ * @v range PCI bus:dev.fn address range to fill in
*/
-static void efipci_root_close ( EFI_HANDLE handle ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+static void efipci_discover ( uint32_t busdevfn, struct pci_range *range ) {
+ struct pci_device pci;
+ EFI_HANDLE handle;
+
+ /* Find range */
+ memset ( &pci, 0, sizeof ( pci ) );
+ pci_init ( &pci, busdevfn );
+ efipci_discover_any ( &pci, range, &handle );
+}
+
+/**
+ * Open EFI PCI root bridge I/O protocol for ephemeral use
+ *
+ * @v pci PCI device
+ * @ret handle EFI PCI root bridge handle
+ * @ret root EFI PCI root bridge I/O protocol, or NULL if not found
+ * @ret rc Return status code
+ */
+static int efipci_root_open ( struct pci_device *pci, EFI_HANDLE *handle,
+ EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL **root ) {
+ struct pci_range tmp;
+ int rc;
+
+ /* Find matching root bridge I/O protocol handle */
+ if ( ( rc = efipci_discover_any ( pci, &tmp, handle ) ) != 0 )
+ return rc;
- /* Close protocol */
- bs->CloseProtocol ( handle, &efi_pci_root_bridge_io_protocol_guid,
- efi_image_handle, handle );
+ /* Open PCI root bridge I/O protocol */
+ if ( ( rc = efi_open ( *handle, &efi_pci_root_bridge_io_protocol_guid,
+ root ) ) != 0 ) {
+ DBGC ( pci, "EFIPCI " PCI_FMT " cannot open %s: %s\n",
+ PCI_ARGS ( pci ), efi_handle_name ( *handle ),
+ strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
}
/**
@@ -234,7 +307,7 @@ int efipci_read ( struct pci_device *pci, unsigned long location,
/* Open root bridge */
if ( ( rc = efipci_root_open ( pci, &handle, &root ) ) != 0 )
- goto err_root;
+ return rc;
/* Read from configuration space */
if ( ( efirc = root->Pci.Read ( root, EFIPCI_WIDTH ( location ),
@@ -244,13 +317,10 @@ int efipci_read ( struct pci_device *pci, unsigned long location,
DBGC ( pci, "EFIPCI " PCI_FMT " config read from offset %02lx "
"failed: %s\n", PCI_ARGS ( pci ),
EFIPCI_OFFSET ( location ), strerror ( rc ) );
- goto err_read;
+ return rc;
}
- err_read:
- efipci_root_close ( handle );
- err_root:
- return rc;
+ return 0;
}
/**
@@ -270,7 +340,7 @@ int efipci_write ( struct pci_device *pci, unsigned long location,
/* Open root bridge */
if ( ( rc = efipci_root_open ( pci, &handle, &root ) ) != 0 )
- goto err_root;
+ return rc;
/* Read from configuration space */
if ( ( efirc = root->Pci.Write ( root, EFIPCI_WIDTH ( location ),
@@ -280,13 +350,10 @@ int efipci_write ( struct pci_device *pci, unsigned long location,
DBGC ( pci, "EFIPCI " PCI_FMT " config write to offset %02lx "
"failed: %s\n", PCI_ARGS ( pci ),
EFIPCI_OFFSET ( location ), strerror ( rc ) );
- goto err_write;
+ return rc;
}
- err_write:
- efipci_root_close ( handle );
- err_root:
- return rc;
+ return 0;
}
/**
@@ -357,12 +424,12 @@ void * efipci_ioremap ( struct pci_device *pci, unsigned long bus_addr,
}
err_config:
- efipci_root_close ( handle );
err_root:
return ioremap ( bus_addr, len );
}
-PROVIDE_PCIAPI_INLINE ( efi, pci_discover );
+PROVIDE_PCIAPI_INLINE ( efi, pci_can_probe );
+PROVIDE_PCIAPI ( efi, pci_discover, efipci_discover );
PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_byte );
PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_word );
PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_dword );
@@ -370,6 +437,7 @@ 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 ( efi, pci_ioremap, efipci_ioremap );
+PROVIDE_PCIAPI_RUNTIME ( efi, PCIAPI_PRIORITY_EFI );
/******************************************************************************
*
@@ -389,7 +457,7 @@ PROVIDE_PCIAPI ( efi, pci_ioremap, efipci_ioremap );
* @ret rc Return status code
*/
static int efipci_dma_map ( struct dma_device *dma, struct dma_mapping *map,
- physaddr_t addr, size_t len, int flags ) {
+ void *addr, size_t len, int flags ) {
struct efi_pci_device *efipci =
container_of ( dma, struct efi_pci_device, pci.dma );
struct pci_device *pci = &efipci->pci;
@@ -398,6 +466,7 @@ static int efipci_dma_map ( struct dma_device *dma, struct dma_mapping *map,
EFI_PHYSICAL_ADDRESS bus;
UINTN count;
VOID *mapping;
+ physaddr_t phys;
EFI_STATUS efirc;
int rc;
@@ -420,18 +489,19 @@ static int efipci_dma_map ( struct dma_device *dma, struct dma_mapping *map,
}
/* Map buffer (if non-zero length) */
+ phys = virt_to_phys ( addr );
count = len;
if ( len ) {
- if ( ( efirc = pci_io->Map ( pci_io, op, phys_to_virt ( addr ),
- &count, &bus, &mapping ) ) != 0 ) {
+ if ( ( efirc = pci_io->Map ( pci_io, op, addr, &count, &bus,
+ &mapping ) ) != 0 ) {
rc = -EEFI ( efirc );
- DBGC ( pci, "EFIPCI " PCI_FMT " cannot map %08lx+%zx: "
+ DBGC ( pci, "EFIPCI " PCI_FMT " cannot map %p+%zx: "
"%s\n", PCI_ARGS ( pci ), addr, len,
strerror ( rc ) );
goto err_map;
}
} else {
- bus = addr;
+ bus = phys;
mapping = NULL;
}
@@ -442,14 +512,14 @@ static int efipci_dma_map ( struct dma_device *dma, struct dma_mapping *map,
*/
if ( count != len ) {
DBGC ( pci, "EFIPCI " PCI_FMT " attempted split mapping for "
- "%08lx+%zx\n", PCI_ARGS ( pci ), addr, len );
+ "%p+%zx\n", PCI_ARGS ( pci ), addr, len );
rc = -ENOTSUP;
goto err_len;
}
/* Populate mapping */
map->dma = dma;
- map->offset = ( bus - addr );
+ map->offset = ( bus - phys );
map->token = mapping;
/* Increment mapping count (for debugging) */
@@ -469,9 +539,10 @@ static int efipci_dma_map ( struct dma_device *dma, struct dma_mapping *map,
*
* @v dma DMA device
* @v map DMA mapping
+ * @v len Used length
*/
static void efipci_dma_unmap ( struct dma_device *dma,
- struct dma_mapping *map ) {
+ struct dma_mapping *map, size_t len __unused ) {
struct efi_pci_device *efipci =
container_of ( dma, struct efi_pci_device, pci.dma );
EFI_PCI_IO_PROTOCOL *pci_io = efipci->io;
@@ -528,8 +599,7 @@ static void * efipci_dma_alloc ( struct dma_device *dma,
memset ( addr, 0, ( pages * EFI_PAGE_SIZE ) );
/* Map buffer */
- if ( ( rc = efipci_dma_map ( dma, map, virt_to_phys ( addr ),
- ( pages * EFI_PAGE_SIZE ),
+ if ( ( rc = efipci_dma_map ( dma, map, addr, ( pages * EFI_PAGE_SIZE ),
DMA_BI ) ) != 0 )
goto err_map;
@@ -539,7 +609,7 @@ static void * efipci_dma_alloc ( struct dma_device *dma,
return addr;
- efipci_dma_unmap ( dma, map );
+ efipci_dma_unmap ( dma, map, len );
err_map:
pci_io->FreeBuffer ( pci_io, pages, addr );
err_alloc:
@@ -565,7 +635,7 @@ static void efipci_dma_free ( struct dma_device *dma, struct dma_mapping *map,
pages = ( ( len + EFI_PAGE_SIZE - 1 ) / EFI_PAGE_SIZE );
/* Unmap buffer */
- efipci_dma_unmap ( dma, map );
+ efipci_dma_unmap ( dma, map, len );
/* Free buffer */
pci_io->FreeBuffer ( pci_io, pages, addr );
@@ -576,38 +646,6 @@ static void efipci_dma_free ( struct dma_device *dma, struct dma_mapping *map,
}
/**
- * Allocate and map DMA-coherent buffer from external (user) memory
- *
- * @v dma DMA device
- * @v map DMA mapping to fill in
- * @v len Length of buffer
- * @v align Physical alignment
- * @ret addr Buffer address, or NULL on error
- */
-static userptr_t efipci_dma_umalloc ( struct dma_device *dma,
- struct dma_mapping *map,
- size_t len, size_t align ) {
- void *addr;
-
- addr = efipci_dma_alloc ( dma, map, len, align );
- return virt_to_user ( addr );
-}
-
-/**
- * Unmap and free DMA-coherent buffer from external (user) memory
- *
- * @v dma DMA device
- * @v map DMA mapping
- * @v addr Buffer address
- * @v len Length of buffer
- */
-static void efipci_dma_ufree ( struct dma_device *dma, struct dma_mapping *map,
- userptr_t addr, size_t len ) {
-
- efipci_dma_free ( dma, map, user_to_virt ( addr, 0 ), len );
-}
-
-/**
* Set addressable space mask
*
* @v dma DMA device
@@ -645,8 +683,8 @@ static struct dma_operations efipci_dma_operations = {
.unmap = efipci_dma_unmap,
.alloc = efipci_dma_alloc,
.free = efipci_dma_free,
- .umalloc = efipci_dma_umalloc,
- .ufree = efipci_dma_ufree,
+ .umalloc = efipci_dma_alloc,
+ .ufree = efipci_dma_free,
.set_mask = efipci_dma_set_mask,
};
@@ -658,44 +696,35 @@ static struct dma_operations efipci_dma_operations = {
*/
/**
- * Open EFI PCI device
+ * Get EFI PCI device information
*
* @v device EFI device handle
- * @v attributes Protocol opening attributes
* @v efipci EFI PCI device to fill in
* @ret rc Return status code
*/
-int efipci_open ( EFI_HANDLE device, UINT32 attributes,
- struct efi_pci_device *efipci ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
- union {
- EFI_PCI_IO_PROTOCOL *pci_io;
- void *interface;
- } pci_io;
+int efipci_info ( EFI_HANDLE device, struct efi_pci_device *efipci ) {
+ EFI_PCI_IO_PROTOCOL *pci_io;
UINTN pci_segment, pci_bus, pci_dev, pci_fn;
unsigned int busdevfn;
EFI_STATUS efirc;
int rc;
/* See if device is a PCI device */
- if ( ( efirc = bs->OpenProtocol ( device, &efi_pci_io_protocol_guid,
- &pci_io.interface, efi_image_handle,
- device, attributes ) ) != 0 ) {
- rc = -EEFI_PCI ( efirc );
+ if ( ( rc = efi_open ( device, &efi_pci_io_protocol_guid,
+ &pci_io ) ) != 0 ) {
DBGCP ( device, "EFIPCI %s cannot open PCI protocols: %s\n",
efi_handle_name ( device ), strerror ( rc ) );
- goto err_open_protocol;
+ return rc;
}
- efipci->io = pci_io.pci_io;
+ efipci->io = pci_io;
/* Get PCI bus:dev.fn address */
- if ( ( efirc = pci_io.pci_io->GetLocation ( pci_io.pci_io, &pci_segment,
- &pci_bus, &pci_dev,
- &pci_fn ) ) != 0 ) {
+ if ( ( efirc = pci_io->GetLocation ( pci_io, &pci_segment, &pci_bus,
+ &pci_dev, &pci_fn ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( device, "EFIPCI %s could not get PCI location: %s\n",
efi_handle_name ( device ), strerror ( rc ) );
- goto err_get_location;
+ return rc;
}
busdevfn = PCI_BUSDEVFN ( pci_segment, pci_bus, pci_dev, pci_fn );
pci_init ( &efipci->pci, busdevfn );
@@ -709,63 +738,20 @@ int efipci_open ( EFI_HANDLE device, UINT32 attributes,
* support I/O cycles). Work around any such platforms by
* enabling bits individually and simply ignoring any errors.
*/
- pci_io.pci_io->Attributes ( pci_io.pci_io,
- EfiPciIoAttributeOperationEnable,
- EFI_PCI_IO_ATTRIBUTE_IO, NULL );
- pci_io.pci_io->Attributes ( pci_io.pci_io,
- EfiPciIoAttributeOperationEnable,
- EFI_PCI_IO_ATTRIBUTE_MEMORY, NULL );
- pci_io.pci_io->Attributes ( pci_io.pci_io,
- EfiPciIoAttributeOperationEnable,
- EFI_PCI_IO_ATTRIBUTE_BUS_MASTER, NULL );
+ pci_io->Attributes ( pci_io, EfiPciIoAttributeOperationEnable,
+ EFI_PCI_IO_ATTRIBUTE_IO, NULL );
+ pci_io->Attributes ( pci_io, EfiPciIoAttributeOperationEnable,
+ EFI_PCI_IO_ATTRIBUTE_MEMORY, NULL );
+ pci_io->Attributes ( pci_io, EfiPciIoAttributeOperationEnable,
+ EFI_PCI_IO_ATTRIBUTE_BUS_MASTER, NULL );
/* Populate PCI device */
if ( ( rc = pci_read_config ( &efipci->pci ) ) != 0 ) {
DBGC ( device, "EFIPCI " PCI_FMT " cannot read PCI "
"configuration: %s\n",
PCI_ARGS ( &efipci->pci ), strerror ( rc ) );
- goto err_pci_read_config;
- }
-
- return 0;
-
- err_pci_read_config:
- err_get_location:
- bs->CloseProtocol ( device, &efi_pci_io_protocol_guid,
- efi_image_handle, device );
- err_open_protocol:
- return rc;
-}
-
-/**
- * Close EFI PCI device
- *
- * @v device EFI device handle
- */
-void efipci_close ( EFI_HANDLE device ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
-
- bs->CloseProtocol ( device, &efi_pci_io_protocol_guid,
- efi_image_handle, device );
-}
-
-/**
- * Get EFI PCI device information
- *
- * @v device EFI device handle
- * @v efipci EFI PCI device to fill in
- * @ret rc Return status code
- */
-int efipci_info ( EFI_HANDLE device, struct efi_pci_device *efipci ) {
- int rc;
-
- /* Open PCI device, if possible */
- if ( ( rc = efipci_open ( device, EFI_OPEN_PROTOCOL_GET_PROTOCOL,
- efipci ) ) != 0 )
return rc;
-
- /* Close PCI device */
- efipci_close ( device );
+ }
return 0;
}
@@ -817,6 +803,26 @@ static int efipci_supported ( EFI_HANDLE device ) {
}
/**
+ * Exclude existing drivers
+ *
+ * @v device EFI device handle
+ * @ret rc Return status code
+ */
+static int efipci_exclude ( EFI_HANDLE device ) {
+ EFI_GUID *protocol = &efi_pci_io_protocol_guid;
+ int rc;
+
+ /* Exclude existing PCI I/O protocol drivers */
+ if ( ( rc = efi_driver_exclude ( device, protocol ) ) != 0 ) {
+ DBGC ( device, "EFIPCI %s could not exclude drivers: %s\n",
+ efi_handle_name ( device ), strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
* Attach driver to device
*
* @v efidev EFI device
@@ -834,10 +840,13 @@ static int efipci_start ( struct efi_device *efidev ) {
goto err_alloc;
}
- /* Open PCI device */
- if ( ( rc = efipci_open ( device, ( EFI_OPEN_PROTOCOL_BY_DRIVER |
- EFI_OPEN_PROTOCOL_EXCLUSIVE ),
- efipci ) ) != 0 ) {
+ /* Get PCI device information */
+ if ( ( rc = efipci_info ( device, efipci ) ) != 0 )
+ goto err_info;
+
+ /* Open PCI I/O protocol */
+ if ( ( rc = efi_open_by_driver ( device, &efi_pci_io_protocol_guid,
+ &efipci->io ) ) != 0 ) {
DBGC ( device, "EFIPCI %s could not open PCI device: %s\n",
efi_handle_name ( device ), strerror ( rc ) );
DBGC_EFI_OPENERS ( device, device, &efi_pci_io_protocol_guid );
@@ -872,8 +881,9 @@ static int efipci_start ( struct efi_device *efidev ) {
err_probe:
list_del ( &efipci->pci.dev.siblings );
err_find_driver:
- efipci_close ( device );
+ efi_close_by_driver ( device, &efi_pci_io_protocol_guid );
err_open:
+ err_info:
free ( efipci );
err_alloc:
return rc;
@@ -892,14 +902,15 @@ static void efipci_stop ( struct efi_device *efidev ) {
list_del ( &efipci->pci.dev.siblings );
assert ( efipci->pci.dma.mapped == 0 );
assert ( efipci->pci.dma.allocated == 0 );
- efipci_close ( device );
+ efi_close_by_driver ( device, &efi_pci_io_protocol_guid );
free ( efipci );
}
/** EFI PCI driver */
-struct efi_driver efipci_driver __efi_driver ( EFI_DRIVER_NORMAL ) = {
+struct efi_driver efipci_driver __efi_driver ( EFI_DRIVER_HARDWARE ) = {
.name = "PCI",
.supported = efipci_supported,
+ .exclude = efipci_exclude,
.start = efipci_start,
.stop = efipci_stop,
};