iPXE
pciea.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2016 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 (at your option) 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 <stdint.h>
00027 #include <errno.h>
00028 #include <ipxe/pci.h>
00029 #include <ipxe/pciea.h>
00030 
00031 /** @file
00032  *
00033  * PCI Enhanced Allocation
00034  *
00035  */
00036 
00037 /**
00038  * Locate PCI Enhanced Allocation BAR equivalent entry
00039  *
00040  * @v pci               PCI device
00041  * @v bei               BAR equivalent indicator
00042  * @ret offset          PCI Enhanced Allocation entry offset, or negative error
00043  */
00044 static int pciea_offset ( struct pci_device *pci, unsigned int bei ) {
00045         uint8_t entries;
00046         uint32_t desc;
00047         unsigned int i;
00048         int offset;
00049 
00050         /* Locate Enhanced Allocation capability */
00051         offset = pci_find_capability ( pci, PCI_CAP_ID_EA );
00052         if ( offset < 0 )
00053                 return offset;
00054 
00055         /* Get number of entries */
00056         pci_read_config_byte ( pci, ( offset + PCIEA_ENTRIES ), &entries );
00057         entries &= PCIEA_ENTRIES_MASK;
00058 
00059         /* Locate first entry */
00060         offset += PCIEA_FIRST;
00061 
00062         /* Search for a matching entry */
00063         for ( i = 0 ; i < entries ; i++ ) {
00064 
00065                 /* Read entry descriptor */
00066                 pci_read_config_dword ( pci, offset, &desc );
00067 
00068                 /* Check for a matching entry */
00069                 if ( ( desc & PCIEA_DESC_ENABLED ) &&
00070                      ( bei == PCIEA_DESC_BEI ( desc ) ) )
00071                         return offset;
00072 
00073                 /* Move to next entry */
00074                 offset += ( ( PCIEA_DESC_SIZE ( desc ) + 1 ) << 2 );
00075         }
00076 
00077         return -ENOENT;
00078 }
00079 
00080 /**
00081  * Read PCI Enhanced Allocation BAR equivalent value
00082  *
00083  * @v pci               PCI device
00084  * @v bei               BAR equivalent indicator
00085  * @v low_offset        Offset to low dword of value
00086  * @ret value           BAR equivalent value
00087  */
00088 static unsigned long pciea_bar_value ( struct pci_device *pci, unsigned int bei,
00089                                        unsigned int low_offset ) {
00090         uint32_t low;
00091         uint32_t high;
00092         int offset;
00093 
00094         /* Locate Enhanced Allocation offset for this BEI */
00095         offset = pciea_offset ( pci, bei );
00096         if ( offset < 0 )
00097                 return 0;
00098 
00099         /* Read BAR equivalent */
00100         offset += low_offset;
00101         pci_read_config_dword ( pci, offset, &low );
00102         if ( low & PCIEA_LOW_ATTR_64BIT ) {
00103                 offset += PCIEA_LOW_HIGH;
00104                 pci_read_config_dword ( pci, offset, &high );
00105                 if ( high ) {
00106                         if ( sizeof ( unsigned long ) > sizeof ( uint32_t ) ) {
00107                                 return ( ( ( uint64_t ) high << 32 ) | low );
00108                         } else {
00109                                 DBGC ( pci, PCI_FMT " unhandled 64-bit EA BAR "
00110                                        "%08x%08x\n",
00111                                        PCI_ARGS ( pci ), high, low );
00112                                 return 0;
00113                         }
00114                 }
00115         }
00116         return low;
00117 }
00118 
00119 /**
00120  * Find the start of a PCI Enhanced Allocation BAR equivalent
00121  *
00122  * @v pci               PCI device
00123  * @v bei               BAR equivalent indicator
00124  * @ret start           BAR start address
00125  *
00126  * If the address exceeds the size of an unsigned long (i.e. if a
00127  * 64-bit BAR has a non-zero high dword on a 32-bit machine), the
00128  * return value will be zero.
00129  */
00130 unsigned long pciea_bar_start ( struct pci_device *pci, unsigned int bei ) {
00131         unsigned long base;
00132 
00133         base = pciea_bar_value ( pci, bei, PCIEA_LOW_BASE );
00134         return ( base & ~PCIEA_LOW_ATTR_MASK );
00135 }
00136 
00137 /**
00138  * Find the size of a PCI Enhanced Allocation BAR equivalent
00139  *
00140  * @v pci               PCI device
00141  * @v bei               BAR equivalent indicator
00142  * @ret size            BAR size
00143  */
00144 unsigned long pciea_bar_size ( struct pci_device *pci, unsigned int bei ) {
00145         unsigned long limit;
00146 
00147         limit = pciea_bar_value ( pci, bei, PCIEA_LOW_LIMIT );
00148         return ( limit ? ( ( limit | PCIEA_LOW_ATTR_MASK ) + 1 ) : 0 );
00149 }