Skip to content

Commit

Permalink
[efi] Allow EFI to control PCI bus enumeration
Browse files Browse the repository at this point in the history
EFI performs its own PCI bus enumeration.  Respect this, and start
controlling devices only when instructed to do so by EFI.

As a side benefit, we should now correctly create multiple SNP
instances for multi-port devices.

This should also fix the problem of failing to enumerate devices
because the PCI bridges have not yet been enabled at the time the iPXE
driver is loaded.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
mcb30 committed Feb 17, 2011
1 parent e2b5a58 commit d7736fb
Show file tree
Hide file tree
Showing 7 changed files with 685 additions and 288 deletions.
5 changes: 3 additions & 2 deletions src/arch/x86/prefix/efidrvprefix.c
Expand Up @@ -41,6 +41,7 @@ EFI_STATUS EFIAPI _efidrv_start ( EFI_HANDLE image_handle,
initialise();
startup();

/* Install SNP driver and return */
return RC_TO_EFIRC ( efi_snp_install () );
return 0;
}

REQUIRE_OBJECT ( efi_snp );
1 change: 0 additions & 1 deletion src/include/ipxe/efi/efi.h
Expand Up @@ -142,6 +142,5 @@ extern EFI_SYSTEM_TABLE *efi_systab;
extern const char * efi_strerror ( EFI_STATUS efirc );
extern EFI_STATUS efi_init ( EFI_HANDLE image_handle,
EFI_SYSTEM_TABLE *systab );
extern int efi_snp_install ( void );

#endif /* _IPXE_EFI_H */
49 changes: 49 additions & 0 deletions src/include/ipxe/efi/efi_driver.h
@@ -0,0 +1,49 @@
#ifndef _IPXE_EFI_DRIVER_H
#define _IPXE_EFI_DRIVER_H

/** @file
*
* EFI driver interface
*/

FILE_LICENCE ( GPL2_OR_LATER );

#include <ipxe/efi/efi.h>
#include <ipxe/efi/Protocol/DriverBinding.h>
#include <ipxe/efi/Protocol/ComponentName2.h>
#include <ipxe/efi/Protocol/DevicePath.h>

/** An EFI driver */
struct efi_driver {
/** Name */
const char *name;
/** EFI name */
CHAR16 *wname;
/** EFI driver binding protocol */
EFI_DRIVER_BINDING_PROTOCOL driver;
/** EFI component name protocol */
EFI_COMPONENT_NAME2_PROTOCOL wtf;
};

/** Initialise an EFI driver
*
* @v name Driver name
* @v supported Device supported method
* @v start Device start method
* @v stop Device stop method
*/
#define EFI_DRIVER_INIT( _name, _supported, _start, _stop ) { \
.name = _name, \
.driver = { \
.Supported = _supported, \
.Start = _start, \
.Stop = _stop, \
.Version = 0x10, \
} }

extern EFI_DEVICE_PATH_PROTOCOL *
efi_devpath_end ( EFI_DEVICE_PATH_PROTOCOL *path );

extern EFI_STATUS efi_driver_install ( struct efi_driver *efidrv );

#endif /* _IPXE_EFI_DRIVER_H */
40 changes: 40 additions & 0 deletions src/include/ipxe/efi/efi_pci.h
@@ -0,0 +1,40 @@
#ifndef _IPXE_EFI_PCI_H
#define _IPXE_EFI_PCI_H

/** @file
*
* EFI driver interface
*/

FILE_LICENCE ( GPL2_OR_LATER );

#include <ipxe/efi/efi.h>
#include <ipxe/efi/Protocol/PciIo.h>
#include <ipxe/efi/Protocol/DevicePath.h>

struct efi_driver;
struct device;

/** An EFI PCI device */
struct efi_pci_device {
/** List of EFI PCI devices */
struct list_head list;
/** iPXE PCI device */
struct pci_device pci;
/** Underlying EFI device */
EFI_HANDLE device;
/** PCI I/O protocol */
EFI_PCI_IO_PROTOCOL *pci_io;
/** Device path */
EFI_DEVICE_PATH_PROTOCOL *path;
};

extern struct efi_pci_device * efipci_create ( struct efi_driver *efidrv,
EFI_HANDLE device );
extern EFI_STATUS efipci_enable ( struct efi_pci_device *efipci );
extern struct efi_pci_device * efipci_find_efi ( EFI_HANDLE device );
extern struct efi_pci_device * efipci_find ( struct device *dev );
extern void efipci_destroy ( struct efi_driver *efidrv,
struct efi_pci_device *efipci );

#endif /* _IPXE_EFI_PCI_H */
143 changes: 143 additions & 0 deletions src/interface/efi/efi_driver.c
@@ -0,0 +1,143 @@
/*
* Copyright (C) 2011 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

FILE_LICENCE ( GPL2_OR_LATER );

#include <stddef.h>
#include <stdio.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/Protocol/DriverBinding.h>
#include <ipxe/efi/Protocol/ComponentName2.h>
#include <ipxe/efi/efi_driver.h>
#include <config/general.h>

/** @file
*
* EFI driver interface
*
*/

/** EFI driver binding protocol GUID */
static EFI_GUID efi_driver_binding_protocol_guid
= EFI_DRIVER_BINDING_PROTOCOL_GUID;

/** EFI component name protocol GUID */
static EFI_GUID efi_component_name2_protocol_guid
= EFI_COMPONENT_NAME2_PROTOCOL_GUID;

/**
* Find end of device path
*
* @v path Path to device
* @ret path_end End of device path
*/
EFI_DEVICE_PATH_PROTOCOL * efi_devpath_end ( EFI_DEVICE_PATH_PROTOCOL *path ) {

while ( path->Type != END_DEVICE_PATH_TYPE ) {
path = ( ( ( void * ) path ) +
/* There's this amazing new-fangled thing known as
* a UINT16, but who wants to use one of those? */
( ( path->Length[1] << 8 ) | path->Length[0] ) );
}

return path;
}

/**
* Look up driver name
*
* @v wtf Component name protocol
* @v language Language to use
* @v driver_name Driver name to fill in
* @ret efirc EFI status code
*/
static EFI_STATUS EFIAPI
efi_driver_get_driver_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf,
CHAR8 *language __unused, CHAR16 **driver_name ) {
struct efi_driver *efidrv =
container_of ( wtf, struct efi_driver, wtf );

*driver_name = efidrv->wname;
return 0;
}

/**
* Look up controller name
*
* @v wtf Component name protocol
* @v device Device
* @v child Child device, or NULL
* @v language Language to use
* @v driver_name Device name to fill in
* @ret efirc EFI status code
*/
static EFI_STATUS EFIAPI
efi_driver_get_controller_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused,
EFI_HANDLE device __unused,
EFI_HANDLE child __unused,
CHAR8 *language __unused,
CHAR16 **controller_name __unused ) {

/* Just let EFI use the default Device Path Name */
return EFI_UNSUPPORTED;
}

/**
* Install EFI driver
*
* @v efidrv EFI driver
* @ret efirc EFI status code
*/
EFI_STATUS efi_driver_install ( struct efi_driver *efidrv ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_DRIVER_BINDING_PROTOCOL *driver = &efidrv->driver;
EFI_COMPONENT_NAME2_PROTOCOL *wtf = &efidrv->wtf;
char buf[ sizeof ( efidrv->wname ) / sizeof ( efidrv->wname[0] ) ];
unsigned int i;
EFI_STATUS efirc;

/* Configure driver binding protocol */
driver->ImageHandle = efi_image_handle;

/* Configure component name protocol */
wtf->GetDriverName = efi_driver_get_driver_name;
wtf->GetControllerName = efi_driver_get_controller_name;
wtf->SupportedLanguages = "en";

/* Fill in driver name */
snprintf ( buf, sizeof ( buf ), PRODUCT_SHORT_NAME " - %s",
efidrv->name );
for ( i = 0 ; i < sizeof ( buf ) ; i++ ) {
/* Damn Unicode names */
efidrv->wname[i] = *( ( ( unsigned char * ) buf ) + i );
}

/* Install driver */
if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
&driver->DriverBindingHandle,
&efi_driver_binding_protocol_guid, driver,
&efi_component_name2_protocol_guid, wtf,
NULL ) ) != 0 ) {
DBGC ( efidrv, "EFIDRV %s could not install protocol: %s\n",
efidrv->name, efi_strerror ( efirc ) );
return efirc;
}

DBGC ( efidrv, "EFIDRV %s installed\n", efidrv->name );
return 0;
}

0 comments on commit d7736fb

Please sign in to comment.