iPXE
efi_pci.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <stdlib.h>
00027 #include <errno.h>
00028 #include <ipxe/pci.h>
00029 #include <ipxe/efi/efi.h>
00030 #include <ipxe/efi/efi_pci.h>
00031 #include <ipxe/efi/efi_driver.h>
00032 #include <ipxe/efi/Protocol/PciIo.h>
00033 #include <ipxe/efi/Protocol/PciRootBridgeIo.h>
00034 
00035 /** @file
00036  *
00037  * iPXE PCI I/O API for EFI
00038  *
00039  */
00040 
00041 /* Disambiguate the various error causes */
00042 #define EINFO_EEFI_PCI                                                  \
00043         __einfo_uniqify ( EINFO_EPLATFORM, 0x01,                        \
00044                           "Could not open PCI I/O protocol" )
00045 #define EINFO_EEFI_PCI_NOT_PCI                                          \
00046         __einfo_platformify ( EINFO_EEFI_PCI, EFI_UNSUPPORTED,          \
00047                               "Not a PCI device" )
00048 #define EEFI_PCI_NOT_PCI __einfo_error ( EINFO_EEFI_PCI_NOT_PCI )
00049 #define EINFO_EEFI_PCI_IN_USE                                           \
00050         __einfo_platformify ( EINFO_EEFI_PCI, EFI_ACCESS_DENIED,        \
00051                               "PCI device already has a driver" )
00052 #define EEFI_PCI_IN_USE __einfo_error ( EINFO_EEFI_PCI_IN_USE )
00053 #define EEFI_PCI( efirc )                                               \
00054         EPLATFORM ( EINFO_EEFI_PCI, efirc,                              \
00055                     EEFI_PCI_NOT_PCI, EEFI_PCI_IN_USE )
00056 
00057 /******************************************************************************
00058  *
00059  * iPXE PCI API
00060  *
00061  ******************************************************************************
00062  */
00063 
00064 /**
00065  * Locate EFI PCI root bridge I/O protocol
00066  *
00067  * @v pci               PCI device
00068  * @ret handle          EFI PCI root bridge handle
00069  * @ret root            EFI PCI root bridge I/O protocol, or NULL if not found
00070  * @ret rc              Return status code
00071  */
00072 static int efipci_root ( struct pci_device *pci, EFI_HANDLE *handle,
00073                          EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL **root ) {
00074         EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
00075         EFI_HANDLE *handles;
00076         UINTN num_handles;
00077         union {
00078                 void *interface;
00079                 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root;
00080         } u;
00081         EFI_STATUS efirc;
00082         UINTN i;
00083         int rc;
00084 
00085         /* Enumerate all handles */
00086         if ( ( efirc = bs->LocateHandleBuffer ( ByProtocol,
00087                         &efi_pci_root_bridge_io_protocol_guid,
00088                         NULL, &num_handles, &handles ) ) != 0 ) {
00089                 rc = -EEFI ( efirc );
00090                 DBGC ( pci, "EFIPCI " PCI_FMT " cannot locate root bridges: "
00091                        "%s\n", PCI_ARGS ( pci ), strerror ( rc ) );
00092                 goto err_locate;
00093         }
00094 
00095         /* Look for matching root bridge I/O protocol */
00096         for ( i = 0 ; i < num_handles ; i++ ) {
00097                 *handle = handles[i];
00098                 if ( ( efirc = bs->OpenProtocol ( *handle,
00099                                 &efi_pci_root_bridge_io_protocol_guid,
00100                                 &u.interface, efi_image_handle, *handle,
00101                                 EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) {
00102                         rc = -EEFI ( efirc );
00103                         DBGC ( pci, "EFIPCI " PCI_FMT " cannot open %s: %s\n",
00104                                PCI_ARGS ( pci ), efi_handle_name ( *handle ),
00105                                strerror ( rc ) );
00106                         continue;
00107                 }
00108                 if ( u.root->SegmentNumber == PCI_SEG ( pci->busdevfn ) ) {
00109                         *root = u.root;
00110                         bs->FreePool ( handles );
00111                         return 0;
00112                 }
00113                 bs->CloseProtocol ( *handle,
00114                                     &efi_pci_root_bridge_io_protocol_guid,
00115                                     efi_image_handle, *handle );
00116         }
00117         DBGC ( pci, "EFIPCI " PCI_FMT " found no root bridge\n",
00118                PCI_ARGS ( pci ) );
00119         rc = -ENOENT;
00120 
00121         bs->FreePool ( handles );
00122  err_locate:
00123         return rc;
00124 }
00125 
00126 /**
00127  * Calculate EFI PCI configuration space address
00128  *
00129  * @v pci               PCI device
00130  * @v location          Encoded offset and width
00131  * @ret address         EFI PCI address
00132  */
00133 static unsigned long efipci_address ( struct pci_device *pci,
00134                                       unsigned long location ) {
00135 
00136         return EFI_PCI_ADDRESS ( PCI_BUS ( pci->busdevfn ),
00137                                  PCI_SLOT ( pci->busdevfn ),
00138                                  PCI_FUNC ( pci->busdevfn ),
00139                                  EFIPCI_OFFSET ( location ) );
00140 }
00141 
00142 /**
00143  * Read from PCI configuration space
00144  *
00145  * @v pci               PCI device
00146  * @v location          Encoded offset and width
00147  * @ret value           Value
00148  * @ret rc              Return status code
00149  */
00150 int efipci_read ( struct pci_device *pci, unsigned long location,
00151                   void *value ) {
00152         EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
00153         EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root;
00154         EFI_HANDLE handle;
00155         EFI_STATUS efirc;
00156         int rc;
00157 
00158         /* Identify root bridge */
00159         if ( ( rc = efipci_root ( pci, &handle, &root ) ) != 0 )
00160                 goto err_root;
00161 
00162         /* Read from configuration space */
00163         if ( ( efirc = root->Pci.Read ( root, EFIPCI_WIDTH ( location ),
00164                                         efipci_address ( pci, location ), 1,
00165                                         value ) ) != 0 ) {
00166                 rc = -EEFI ( efirc );
00167                 DBGC ( pci, "EFIPCI " PCI_FMT " config read from offset %02lx "
00168                        "failed: %s\n", PCI_ARGS ( pci ),
00169                        EFIPCI_OFFSET ( location ), strerror ( rc ) );
00170                 goto err_read;
00171         }
00172 
00173  err_read:
00174         bs->CloseProtocol ( handle, &efi_pci_root_bridge_io_protocol_guid,
00175                             efi_image_handle, handle );
00176  err_root:
00177         return rc;
00178 }
00179 
00180 /**
00181  * Write to PCI configuration space
00182  *
00183  * @v pci               PCI device
00184  * @v location          Encoded offset and width
00185  * @v value             Value
00186  * @ret rc              Return status code
00187  */
00188 int efipci_write ( struct pci_device *pci, unsigned long location,
00189                    unsigned long value ) {
00190         EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
00191         EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root;
00192         EFI_HANDLE handle;
00193         EFI_STATUS efirc;
00194         int rc;
00195 
00196         /* Identify root bridge */
00197         if ( ( rc = efipci_root ( pci, &handle, &root ) ) != 0 )
00198                 goto err_root;
00199 
00200         /* Read from configuration space */
00201         if ( ( efirc = root->Pci.Write ( root, EFIPCI_WIDTH ( location ),
00202                                          efipci_address ( pci, location ), 1,
00203                                          &value ) ) != 0 ) {
00204                 rc = -EEFI ( efirc );
00205                 DBGC ( pci, "EFIPCI " PCI_FMT " config write to offset %02lx "
00206                        "failed: %s\n", PCI_ARGS ( pci ),
00207                        EFIPCI_OFFSET ( location ), strerror ( rc ) );
00208                 goto err_write;
00209         }
00210 
00211  err_write:
00212         bs->CloseProtocol ( handle, &efi_pci_root_bridge_io_protocol_guid,
00213                             efi_image_handle, handle );
00214  err_root:
00215         return rc;
00216 }
00217 
00218 PROVIDE_PCIAPI_INLINE ( efi, pci_num_bus );
00219 PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_byte );
00220 PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_word );
00221 PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_dword );
00222 PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_byte );
00223 PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_word );
00224 PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_dword );
00225 
00226 /******************************************************************************
00227  *
00228  * EFI PCI device instantiation
00229  *
00230  ******************************************************************************
00231  */
00232 
00233 /**
00234  * Open EFI PCI device
00235  *
00236  * @v device            EFI device handle
00237  * @v attributes        Protocol opening attributes
00238  * @v pci               PCI device to fill in
00239  * @ret rc              Return status code
00240  */
00241 int efipci_open ( EFI_HANDLE device, UINT32 attributes,
00242                   struct pci_device *pci ) {
00243         EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
00244         union {
00245                 EFI_PCI_IO_PROTOCOL *pci_io;
00246                 void *interface;
00247         } pci_io;
00248         UINTN pci_segment, pci_bus, pci_dev, pci_fn;
00249         unsigned int busdevfn;
00250         EFI_STATUS efirc;
00251         int rc;
00252 
00253         /* See if device is a PCI device */
00254         if ( ( efirc = bs->OpenProtocol ( device, &efi_pci_io_protocol_guid,
00255                                           &pci_io.interface, efi_image_handle,
00256                                           device, attributes ) ) != 0 ) {
00257                 rc = -EEFI_PCI ( efirc );
00258                 DBGCP ( device, "EFIPCI %s cannot open PCI protocols: %s\n",
00259                         efi_handle_name ( device ), strerror ( rc ) );
00260                 goto err_open_protocol;
00261         }
00262 
00263         /* Get PCI bus:dev.fn address */
00264         if ( ( efirc = pci_io.pci_io->GetLocation ( pci_io.pci_io, &pci_segment,
00265                                                     &pci_bus, &pci_dev,
00266                                                     &pci_fn ) ) != 0 ) {
00267                 rc = -EEFI ( efirc );
00268                 DBGC ( device, "EFIPCI %s could not get PCI location: %s\n",
00269                        efi_handle_name ( device ), strerror ( rc ) );
00270                 goto err_get_location;
00271         }
00272         busdevfn = PCI_BUSDEVFN ( pci_segment, pci_bus, pci_dev, pci_fn );
00273         pci_init ( pci, busdevfn );
00274         DBGCP ( device, "EFIPCI " PCI_FMT " is %s\n",
00275                 PCI_ARGS ( pci ), efi_handle_name ( device ) );
00276 
00277         /* Try to enable I/O cycles, memory cycles, and bus mastering.
00278          * Some platforms will 'helpfully' report errors if these bits
00279          * can't be enabled (for example, if the card doesn't actually
00280          * support I/O cycles).  Work around any such platforms by
00281          * enabling bits individually and simply ignoring any errors.
00282          */
00283         pci_io.pci_io->Attributes ( pci_io.pci_io,
00284                                     EfiPciIoAttributeOperationEnable,
00285                                     EFI_PCI_IO_ATTRIBUTE_IO, NULL );
00286         pci_io.pci_io->Attributes ( pci_io.pci_io,
00287                                     EfiPciIoAttributeOperationEnable,
00288                                     EFI_PCI_IO_ATTRIBUTE_MEMORY, NULL );
00289         pci_io.pci_io->Attributes ( pci_io.pci_io,
00290                                     EfiPciIoAttributeOperationEnable,
00291                                     EFI_PCI_IO_ATTRIBUTE_BUS_MASTER, NULL );
00292 
00293         /* Populate PCI device */
00294         if ( ( rc = pci_read_config ( pci ) ) != 0 ) {
00295                 DBGC ( device, "EFIPCI " PCI_FMT " cannot read PCI "
00296                        "configuration: %s\n",
00297                        PCI_ARGS ( pci ), strerror ( rc ) );
00298                 goto err_pci_read_config;
00299         }
00300 
00301         return 0;
00302 
00303  err_pci_read_config:
00304  err_get_location:
00305         bs->CloseProtocol ( device, &efi_pci_io_protocol_guid,
00306                             efi_image_handle, device );
00307  err_open_protocol:
00308         return rc;
00309 }
00310 
00311 /**
00312  * Close EFI PCI device
00313  *
00314  * @v device            EFI device handle
00315  */
00316 void efipci_close ( EFI_HANDLE device ) {
00317         EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
00318 
00319         bs->CloseProtocol ( device, &efi_pci_io_protocol_guid,
00320                             efi_image_handle, device );
00321 }
00322 
00323 /**
00324  * Get EFI PCI device information
00325  *
00326  * @v device            EFI device handle
00327  * @v pci               PCI device to fill in
00328  * @ret rc              Return status code
00329  */
00330 int efipci_info ( EFI_HANDLE device, struct pci_device *pci ) {
00331         int rc;
00332 
00333         /* Open PCI device, if possible */
00334         if ( ( rc = efipci_open ( device, EFI_OPEN_PROTOCOL_GET_PROTOCOL,
00335                                   pci ) ) != 0 )
00336                 return rc;
00337 
00338         /* Close PCI device */
00339         efipci_close ( device );
00340 
00341         return 0;
00342 }
00343 
00344 /******************************************************************************
00345  *
00346  * EFI PCI driver
00347  *
00348  ******************************************************************************
00349  */
00350 
00351 /**
00352  * Check to see if driver supports a device
00353  *
00354  * @v device            EFI device handle
00355  * @ret rc              Return status code
00356  */
00357 static int efipci_supported ( EFI_HANDLE device ) {
00358         struct pci_device pci;
00359         int rc;
00360 
00361         /* Get PCI device information */
00362         if ( ( rc = efipci_info ( device, &pci ) ) != 0 )
00363                 return rc;
00364 
00365         /* Look for a driver */
00366         if ( ( rc = pci_find_driver ( &pci ) ) != 0 ) {
00367                 DBGC ( device, "EFIPCI " PCI_FMT " (%04x:%04x class %06x) "
00368                        "has no driver\n", PCI_ARGS ( &pci ), pci.vendor,
00369                        pci.device, pci.class );
00370                 return rc;
00371         }
00372         DBGC ( device, "EFIPCI " PCI_FMT " (%04x:%04x class %06x) has driver "
00373                "\"%s\"\n", PCI_ARGS ( &pci ), pci.vendor, pci.device,
00374                pci.class, pci.id->name );
00375 
00376         return 0;
00377 }
00378 
00379 /**
00380  * Attach driver to device
00381  *
00382  * @v efidev            EFI device
00383  * @ret rc              Return status code
00384  */
00385 static int efipci_start ( struct efi_device *efidev ) {
00386         EFI_HANDLE device = efidev->device;
00387         struct pci_device *pci;
00388         int rc;
00389 
00390         /* Allocate PCI device */
00391         pci = zalloc ( sizeof ( *pci ) );
00392         if ( ! pci ) {
00393                 rc = -ENOMEM;
00394                 goto err_alloc;
00395         }
00396 
00397         /* Open PCI device */
00398         if ( ( rc = efipci_open ( device, ( EFI_OPEN_PROTOCOL_BY_DRIVER |
00399                                             EFI_OPEN_PROTOCOL_EXCLUSIVE ),
00400                                   pci ) ) != 0 ) {
00401                 DBGC ( device, "EFIPCI %s could not open PCI device: %s\n",
00402                        efi_handle_name ( device ), strerror ( rc ) );
00403                 DBGC_EFI_OPENERS ( device, device, &efi_pci_io_protocol_guid );
00404                 goto err_open;
00405         }
00406 
00407         /* Find driver */
00408         if ( ( rc = pci_find_driver ( pci ) ) != 0 ) {
00409                 DBGC ( device, "EFIPCI " PCI_FMT " has no driver\n",
00410                        PCI_ARGS ( pci ) );
00411                 goto err_find_driver;
00412         }
00413 
00414         /* Mark PCI device as a child of the EFI device */
00415         pci->dev.parent = &efidev->dev;
00416         list_add ( &pci->dev.siblings, &efidev->dev.children );
00417 
00418         /* Probe driver */
00419         if ( ( rc = pci_probe ( pci ) ) != 0 ) {
00420                 DBGC ( device, "EFIPCI " PCI_FMT " could not probe driver "
00421                        "\"%s\": %s\n", PCI_ARGS ( pci ), pci->id->name,
00422                        strerror ( rc ) );
00423                 goto err_probe;
00424         }
00425         DBGC ( device, "EFIPCI " PCI_FMT " using driver \"%s\"\n",
00426                PCI_ARGS ( pci ), pci->id->name );
00427 
00428         efidev_set_drvdata ( efidev, pci );
00429         return 0;
00430 
00431         pci_remove ( pci );
00432  err_probe:
00433         list_del ( &pci->dev.siblings );
00434  err_find_driver:
00435         efipci_close ( device );
00436  err_open:
00437         free ( pci );
00438  err_alloc:
00439         return rc;
00440 }
00441 
00442 /**
00443  * Detach driver from device
00444  *
00445  * @v efidev            EFI device
00446   */
00447 static void efipci_stop ( struct efi_device *efidev ) {
00448         struct pci_device *pci = efidev_get_drvdata ( efidev );
00449         EFI_HANDLE device = efidev->device;
00450 
00451         pci_remove ( pci );
00452         list_del ( &pci->dev.siblings );
00453         efipci_close ( device );
00454         free ( pci );
00455 }
00456 
00457 /** EFI PCI driver */
00458 struct efi_driver efipci_driver __efi_driver ( EFI_DRIVER_NORMAL ) = {
00459         .name = "PCI",
00460         .supported = efipci_supported,
00461         .start = efipci_start,
00462         .stop = efipci_stop,
00463 };