4e4325740aa40c7b76de0d9820dda1ddab28d181
[people/xl0/gpxe.git] / src / arch / i386 / drivers / bus / pxedrv.c
1 /*
2  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <stdint.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <pxe.h>
23 #include <realmode.h>
24
25 /** @file
26  *
27  * PXE drivers
28  *
29  */
30
31 static LIST_HEAD ( pxe_drivers );
32
33 /**
34  * Parse PXE ROM ID structure
35  *
36  * @v pxedrv            PXE driver
37  * @v pxeromid          Offset within ROM to PXE ROM ID structure
38  * @ret rc              Return status code
39  */
40 static int pxedrv_parse_pxeromid ( struct pxe_driver *pxedrv,
41                                    unsigned int pxeromid ) {
42         struct undi_rom_id undi_rom_id;
43         unsigned int undiloader;
44
45         DBGC ( pxedrv, "PXEDRV %p has PXE ROM ID at %04x:%04x\n", pxedrv,
46                pxedrv->rom_segment, pxeromid );
47
48         /* Read PXE ROM ID structure and verify */
49         copy_from_real ( &undi_rom_id, pxedrv->rom_segment, pxeromid,
50                          sizeof ( undi_rom_id ) );
51         if ( undi_rom_id.Signature != UNDI_ROM_ID_SIGNATURE ) {
52                 DBGC ( pxedrv, "PXEDRV %p has bad PXE ROM ID signature "
53                        "%08lx\n", pxedrv, undi_rom_id.Signature );
54                 return -EINVAL;
55         }
56
57         /* Check for UNDI loader */
58         undiloader = undi_rom_id.UNDILoader;
59         if ( ! undiloader ) {
60                 DBGC ( pxedrv, "PXEDRV %p has no UNDI loader\n", pxedrv );
61                 return -EINVAL;
62         }
63
64         /* Fill in PXE driver loader fields */
65         pxedrv->loader.segment = pxedrv->rom_segment;
66         pxedrv->loader.offset = undiloader;
67         pxedrv->code_size = undi_rom_id.CodeSize;
68         pxedrv->data_size = undi_rom_id.DataSize;
69
70         DBGC ( pxedrv, "PXEDRV %p has UNDI loader at %04x:%04x "
71                "(code %04x data %04x)\n", pxedrv, pxedrv->loader.segment,
72                pxedrv->loader.offset, pxedrv->code_size, pxedrv->data_size );
73         return 0;
74 }
75
76 /**
77  * Parse PCI expansion header
78  *
79  * @v pxedrv            PXE driver
80  * @v pcirheader        Offset within ROM to PCI expansion header
81  */
82 static int pxedrv_parse_pcirheader ( struct pxe_driver *pxedrv,
83                                      unsigned int pcirheader ) {
84         struct pcir_header pcir_header;
85
86         DBGC ( pxedrv, "PXEDRV %p has PCI expansion header at %04x:%04x\n",
87                pxedrv, pxedrv->rom_segment, pcirheader );
88
89         /* Read PCI expansion header and verify */
90         copy_from_real ( &pcir_header, pxedrv->rom_segment, pcirheader,
91                          sizeof ( pcir_header ) );
92         if ( pcir_header.signature != PCIR_SIGNATURE ) {
93                 DBGC ( pxedrv, "PXEDRV %p has bad PCI expansion header "
94                        "signature %08lx\n", pxedrv, pcir_header.signature );
95                 return -EINVAL;
96         }
97         DBGC ( pxedrv, "PXEDRV %p is a PCI ROM\n", pxedrv );
98
99         /* Fill in PXE driver PCI device fields */
100         pxedrv->bus_type = PCI_NIC;
101         pxedrv->bus_id.pci.vendor_id = pcir_header.vendor_id;
102         pxedrv->bus_id.pci.device_id = pcir_header.device_id;
103
104         DBGC ( pxedrv, "PXEDRV %p is for PCI devices %04x:%04x\n", pxedrv,
105                pxedrv->bus_id.pci.vendor_id, pxedrv->bus_id.pci.device_id );
106         return 0;
107         
108 }
109
110 /**
111  * Create PXE driver for expansion ROM
112  *
113  * @v rom_segment       ROM segment address
114  * @ret rc              Return status code
115  */
116 static int pxedrv_probe_rom ( unsigned int rom_segment ) {
117         struct pxe_driver *pxedrv = NULL;
118         struct undi_rom rom;
119         unsigned int pxeromid;
120         unsigned int pcirheader;
121         int rc;
122
123         /* Read expansion ROM header and verify */
124         copy_from_real ( &rom, rom_segment, 0, sizeof ( rom ) );
125         if ( rom.Signature != ROM_SIGNATURE ) {
126                 rc = -EINVAL;
127                 goto err;
128         }
129
130         /* Allocate memory for PXE driver */
131         pxedrv = malloc ( sizeof ( *pxedrv ) );
132         if ( ! pxedrv ) {
133                 DBG ( "Could not allocate PXE driver structure\n" );
134                 rc = -ENOMEM;
135                 goto err;
136         }
137         memset ( pxedrv, 0, sizeof ( *pxedrv ) );
138         DBGC ( pxedrv, "PXEDRV %p using expansion ROM at %04x:0000 (%zdkB)\n",
139                pxedrv, rom_segment, ( rom.ROMLength / 2 ) );
140         pxedrv->rom_segment = rom_segment;
141
142         /* Check for and parse PXE ROM ID */
143         pxeromid = rom.PXEROMID;
144         if ( ! pxeromid ) {
145                 DBGC ( pxedrv, "PXEDRV %p has no PXE ROM ID\n", pxedrv );
146                 rc = -EINVAL;
147                 goto err;
148         }
149         if ( ( rc = pxedrv_parse_pxeromid ( pxedrv, pxeromid ) ) != 0 )
150                 goto err;
151
152         /* Parse PCIR header, if present */
153         pcirheader = rom.PCIRHeader;
154         if ( pcirheader )
155                 pxedrv_parse_pcirheader ( pxedrv, pcirheader );
156
157         /* Add to PXE driver list and return */
158         DBGC ( pxedrv, "PXEDRV %p registered\n", pxedrv );
159         list_add ( &pxedrv->list, &pxe_drivers );
160         return 0;
161
162  err:
163         free ( pxedrv );
164         return rc;
165 }
166
167 /**
168  * Create PXE drivers for all possible expansion ROMs
169  *
170  * @ret 
171  */
172 static void pxedrv_probe_all_roms ( void ) {
173         static int probed = 0;
174         unsigned int rom_segment;
175
176         /* Perform probe only once */
177         if ( probed )
178                 return;
179
180         DBG ( "Scanning for PXE expansion ROMs\n" );
181
182         /* Scan through expansion ROM region at 2kB intervals */
183         for ( rom_segment = 0xc000 ; rom_segment < 0x10000 ;
184               rom_segment += 0x80 ) {
185                 pxedrv_probe_rom ( rom_segment );
186         }
187
188         probed = 1;
189 }
190
191 /**
192  * Find PXE driver for PCI device
193  *
194  * @v vendor_id         PCI vendor ID
195  * @v device_id         PCI device ID
196  * @v rombase           ROM base address, or 0 for any
197  * @ret pxedrv          PXE driver, or NULL
198  */
199 struct pxe_driver * pxedrv_find_pci_driver ( unsigned int vendor_id,
200                                              unsigned int device_id,
201                                              unsigned int rombase ) {
202         struct pxe_driver *pxedrv;
203
204         pxedrv_probe_all_roms();
205
206         list_for_each_entry ( pxedrv, &pxe_drivers, list ) {
207                 if ( pxedrv->bus_type != PCI_NIC )
208                         continue;
209                 if ( pxedrv->bus_id.pci.vendor_id != vendor_id )
210                         continue;
211                 if ( pxedrv->bus_id.pci.device_id != device_id )
212                         continue;
213                 if ( rombase && ( ( pxedrv->rom_segment << 4 ) != rombase ) )
214                         continue;
215                 DBGC ( pxedrv, "PXEDRV %p matched PCI %04x:%04x (%08x)\n",
216                        pxedrv, vendor_id, device_id, rombase );
217                 return pxedrv;
218         }
219
220         DBG ( "No PXE driver matched PCI %04x:%04x (%08x)\n",
221               vendor_id, device_id, rombase );
222         return NULL;
223 }