iPXE
undirom.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2007 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 
00020 FILE_LICENCE ( GPL2_OR_LATER );
00021 
00022 #include <stdint.h>
00023 #include <stdlib.h>
00024 #include <string.h>
00025 #include <pxe.h>
00026 #include <realmode.h>
00027 #include <undirom.h>
00028 
00029 /** @file
00030  *
00031  * UNDI expansion ROMs
00032  *
00033  */
00034 
00035 /** List of all UNDI ROMs */
00036 static LIST_HEAD ( undiroms );
00037 
00038 /**
00039  * Parse PXE ROM ID structure
00040  *
00041  * @v undirom           UNDI ROM
00042  * @v pxeromid          Offset within ROM to PXE ROM ID structure
00043  * @ret rc              Return status code
00044  */
00045 static int undirom_parse_pxeromid ( struct undi_rom *undirom,
00046                                    unsigned int pxeromid ) {
00047         struct undi_rom_id undi_rom_id;
00048         unsigned int undiloader;
00049 
00050         DBGC ( undirom, "UNDIROM %p has PXE ROM ID at %04x:%04x\n", undirom,
00051                undirom->rom_segment, pxeromid );
00052 
00053         /* Read PXE ROM ID structure and verify */
00054         copy_from_real ( &undi_rom_id, undirom->rom_segment, pxeromid,
00055                          sizeof ( undi_rom_id ) );
00056         if ( undi_rom_id.Signature != UNDI_ROM_ID_SIGNATURE ) {
00057                 DBGC ( undirom, "UNDIROM %p has bad PXE ROM ID signature "
00058                        "%08x\n", undirom, undi_rom_id.Signature );
00059                 return -EINVAL;
00060         }
00061 
00062         /* Check for UNDI loader */
00063         undiloader = undi_rom_id.UNDILoader;
00064         if ( ! undiloader ) {
00065                 DBGC ( undirom, "UNDIROM %p has no UNDI loader\n", undirom );
00066                 return -EINVAL;
00067         }
00068 
00069         /* Fill in UNDI ROM loader fields */
00070         undirom->loader_entry.segment = undirom->rom_segment;
00071         undirom->loader_entry.offset = undiloader;
00072         undirom->code_size = undi_rom_id.CodeSize;
00073         undirom->data_size = undi_rom_id.DataSize;
00074 
00075         DBGC ( undirom, "UNDIROM %p has UNDI loader at %04x:%04x "
00076                "(code %04zx data %04zx)\n", undirom,
00077                undirom->loader_entry.segment, undirom->loader_entry.offset,
00078                undirom->code_size, undirom->data_size );
00079         return 0;
00080 }
00081 
00082 /**
00083  * Parse PCI expansion header
00084  *
00085  * @v undirom           UNDI ROM
00086  * @v pcirheader        Offset within ROM to PCI expansion header
00087  */
00088 static int undirom_parse_pcirheader ( struct undi_rom *undirom,
00089                                      unsigned int pcirheader ) {
00090         struct pcir_header pcir_header;
00091 
00092         DBGC ( undirom, "UNDIROM %p has PCI expansion header at %04x:%04x\n",
00093                undirom, undirom->rom_segment, pcirheader );
00094 
00095         /* Read PCI expansion header and verify */
00096         copy_from_real ( &pcir_header, undirom->rom_segment, pcirheader,
00097                          sizeof ( pcir_header ) );
00098         if ( pcir_header.signature != PCIR_SIGNATURE ) {
00099                 DBGC ( undirom, "UNDIROM %p has bad PCI expansion header "
00100                        "signature %08x\n", undirom, pcir_header.signature );
00101                 return -EINVAL;
00102         }
00103 
00104         /* Fill in UNDI ROM PCI device fields */
00105         undirom->bus_type = PCI_NIC;
00106         undirom->bus_id.pci.vendor_id = pcir_header.vendor_id;
00107         undirom->bus_id.pci.device_id = pcir_header.device_id;
00108 
00109         DBGC ( undirom, "UNDIROM %p is for PCI devices %04x:%04x\n", undirom,
00110                undirom->bus_id.pci.vendor_id, undirom->bus_id.pci.device_id );
00111         return 0;
00112         
00113 }
00114 
00115 /**
00116  * Probe UNDI ROM
00117  *
00118  * @v rom_segment       ROM segment address
00119  * @ret rc              Return status code
00120  */
00121 static int undirom_probe ( unsigned int rom_segment ) {
00122         struct undi_rom *undirom = NULL;
00123         struct undi_rom_header romheader;
00124         size_t rom_len;
00125         unsigned int pxeromid;
00126         unsigned int pcirheader;
00127         int rc;
00128 
00129         /* Read expansion ROM header and verify */
00130         copy_from_real ( &romheader, rom_segment, 0, sizeof ( romheader ) );
00131         if ( romheader.Signature != ROM_SIGNATURE ) {
00132                 rc = -EINVAL;
00133                 goto err;
00134         }
00135         rom_len = ( romheader.ROMLength * 512 );
00136 
00137         /* Allocate memory for UNDI ROM */
00138         undirom = zalloc ( sizeof ( *undirom ) );
00139         if ( ! undirom ) {
00140                 DBG ( "Could not allocate UNDI ROM structure\n" );
00141                 rc = -ENOMEM;
00142                 goto err;
00143         }
00144         DBGC ( undirom, "UNDIROM %p trying expansion ROM at %04x:0000 "
00145                "(%zdkB)\n", undirom, rom_segment, ( rom_len / 1024 ) );
00146         undirom->rom_segment = rom_segment;
00147 
00148         /* Check for and parse PXE ROM ID */
00149         pxeromid = romheader.PXEROMID;
00150         if ( ! pxeromid ) {
00151                 DBGC ( undirom, "UNDIROM %p has no PXE ROM ID\n", undirom );
00152                 rc = -EINVAL;
00153                 goto err;
00154         }
00155         if ( pxeromid > rom_len ) {
00156                 DBGC ( undirom, "UNDIROM %p PXE ROM ID outside ROM\n",
00157                        undirom );
00158                 rc = -EINVAL;
00159                 goto err;
00160         }
00161         if ( ( rc = undirom_parse_pxeromid ( undirom, pxeromid ) ) != 0 )
00162                 goto err;
00163 
00164         /* Parse PCIR header, if present */
00165         pcirheader = romheader.PCIRHeader;
00166         if ( pcirheader )
00167                 undirom_parse_pcirheader ( undirom, pcirheader );
00168 
00169         /* Add to UNDI ROM list and return */
00170         DBGC ( undirom, "UNDIROM %p registered\n", undirom );
00171         list_add_tail ( &undirom->list, &undiroms );
00172         return 0;
00173 
00174  err:
00175         free ( undirom );
00176         return rc;
00177 }
00178 
00179 /**
00180  * Create UNDI ROMs for all possible expansion ROMs
00181  *
00182  * @ret 
00183  */
00184 static void undirom_probe_all_roms ( void ) {
00185         static int probed = 0;
00186         unsigned int rom_segment;
00187 
00188         /* Perform probe only once */
00189         if ( probed )
00190                 return;
00191 
00192         DBG ( "Scanning for PXE expansion ROMs\n" );
00193 
00194         /* Scan through expansion ROM region at 512 byte intervals */
00195         for ( rom_segment = 0xc000 ; rom_segment < 0x10000 ;
00196               rom_segment += 0x20 ) {
00197                 undirom_probe ( rom_segment );
00198         }
00199 
00200         probed = 1;
00201 }
00202 
00203 /**
00204  * Find UNDI ROM for PCI device
00205  *
00206  * @v vendor_id         PCI vendor ID
00207  * @v device_id         PCI device ID
00208  * @v rombase           ROM base address, or 0 for any
00209  * @ret undirom         UNDI ROM, or NULL
00210  */
00211 struct undi_rom * undirom_find_pci ( unsigned int vendor_id,
00212                                      unsigned int device_id,
00213                                      unsigned int rombase ) {
00214         struct undi_rom *undirom;
00215 
00216         undirom_probe_all_roms();
00217 
00218         list_for_each_entry ( undirom, &undiroms, list ) {
00219                 if ( undirom->bus_type != PCI_NIC )
00220                         continue;
00221                 if ( undirom->bus_id.pci.vendor_id != vendor_id )
00222                         continue;
00223                 if ( undirom->bus_id.pci.device_id != device_id )
00224                         continue;
00225                 if ( rombase && ( ( undirom->rom_segment << 4 ) != rombase ) )
00226                         continue;
00227                 DBGC ( undirom, "UNDIROM %p matched PCI %04x:%04x (%08x)\n",
00228                        undirom, vendor_id, device_id, rombase );
00229                 return undirom;
00230         }
00231 
00232         DBG ( "No UNDI ROM matched PCI %04x:%04x (%08x)\n",
00233               vendor_id, device_id, rombase );
00234         return NULL;
00235 }