Remove redundant debug message
[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 #include <bios.h>
25 #include <pnpbios.h>
26
27 /** @file
28  *
29  * PXE drivers
30  *
31  */
32
33 static LIST_HEAD ( pxe_drivers );
34
35 /**
36  * Parse PXE ROM ID structure
37  *
38  * @v pxedrv            PXE driver
39  * @v pxeromid          Offset within ROM to PXE ROM ID structure
40  * @ret rc              Return status code
41  */
42 static int pxedrv_parse_pxeromid ( struct pxe_driver *pxedrv,
43                                    unsigned int pxeromid ) {
44         struct undi_rom_id undi_rom_id;
45         unsigned int undiloader;
46
47         DBGC ( pxedrv, "PXEDRV %p has PXE ROM ID at %04x:%04x\n", pxedrv,
48                pxedrv->rom_segment, pxeromid );
49
50         /* Read PXE ROM ID structure and verify */
51         copy_from_real ( &undi_rom_id, pxedrv->rom_segment, pxeromid,
52                          sizeof ( undi_rom_id ) );
53         if ( undi_rom_id.Signature != UNDI_ROM_ID_SIGNATURE ) {
54                 DBGC ( pxedrv, "PXEDRV %p has bad PXE ROM ID signature "
55                        "%08lx\n", pxedrv, undi_rom_id.Signature );
56                 return -EINVAL;
57         }
58
59         /* Check for UNDI loader */
60         undiloader = undi_rom_id.UNDILoader;
61         if ( ! undiloader ) {
62                 DBGC ( pxedrv, "PXEDRV %p has no UNDI loader\n", pxedrv );
63                 return -EINVAL;
64         }
65
66         /* Fill in PXE driver loader fields */
67         pxedrv->loader_entry.segment = pxedrv->rom_segment;
68         pxedrv->loader_entry.offset = undiloader;
69         pxedrv->code_size = undi_rom_id.CodeSize;
70         pxedrv->data_size = undi_rom_id.DataSize;
71
72         DBGC ( pxedrv, "PXEDRV %p has UNDI loader at %04x:%04x "
73                "(code %04x data %04x)\n", pxedrv,
74                pxedrv->loader_entry.segment, pxedrv->loader_entry.offset,
75                pxedrv->code_size, pxedrv->data_size );
76         return 0;
77 }
78
79 /**
80  * Parse PCI expansion header
81  *
82  * @v pxedrv            PXE driver
83  * @v pcirheader        Offset within ROM to PCI expansion header
84  */
85 static int pxedrv_parse_pcirheader ( struct pxe_driver *pxedrv,
86                                      unsigned int pcirheader ) {
87         struct pcir_header pcir_header;
88
89         DBGC ( pxedrv, "PXEDRV %p has PCI expansion header at %04x:%04x\n",
90                pxedrv, pxedrv->rom_segment, pcirheader );
91
92         /* Read PCI expansion header and verify */
93         copy_from_real ( &pcir_header, pxedrv->rom_segment, pcirheader,
94                          sizeof ( pcir_header ) );
95         if ( pcir_header.signature != PCIR_SIGNATURE ) {
96                 DBGC ( pxedrv, "PXEDRV %p has bad PCI expansion header "
97                        "signature %08lx\n", pxedrv, pcir_header.signature );
98                 return -EINVAL;
99         }
100
101         /* Fill in PXE driver PCI device fields */
102         pxedrv->bus_type = PCI_NIC;
103         pxedrv->bus_id.pci.vendor_id = pcir_header.vendor_id;
104         pxedrv->bus_id.pci.device_id = pcir_header.device_id;
105
106         DBGC ( pxedrv, "PXEDRV %p is for PCI devices %04x:%04x\n", pxedrv,
107                pxedrv->bus_id.pci.vendor_id, pxedrv->bus_id.pci.device_id );
108         return 0;
109         
110 }
111
112 /**
113  * Create PXE driver for expansion ROM
114  *
115  * @v rom_segment       ROM segment address
116  * @ret rc              Return status code
117  */
118 static int pxedrv_probe_rom ( unsigned int rom_segment ) {
119         struct pxe_driver *pxedrv = NULL;
120         struct undi_rom rom;
121         size_t rom_len;
122         unsigned int pxeromid;
123         unsigned int pcirheader;
124         int rc;
125
126         /* Read expansion ROM header and verify */
127         copy_from_real ( &rom, rom_segment, 0, sizeof ( rom ) );
128         if ( rom.Signature != ROM_SIGNATURE ) {
129                 rc = -EINVAL;
130                 goto err;
131         }
132         rom_len = ( rom.ROMLength * 512 );
133
134         /* Allocate memory for PXE driver */
135         pxedrv = malloc ( sizeof ( *pxedrv ) );
136         if ( ! pxedrv ) {
137                 DBG ( "Could not allocate PXE driver structure\n" );
138                 rc = -ENOMEM;
139                 goto err;
140         }
141         memset ( pxedrv, 0, sizeof ( *pxedrv ) );
142         DBGC ( pxedrv, "PXEDRV %p trying expansion ROM at %04x:0000 (%zdkB)\n",
143                pxedrv, rom_segment, ( rom_len / 1024 ) );
144         pxedrv->rom_segment = rom_segment;
145
146         /* Check for and parse PXE ROM ID */
147         pxeromid = rom.PXEROMID;
148         if ( ! pxeromid ) {
149                 DBGC ( pxedrv, "PXEDRV %p has no PXE ROM ID\n", pxedrv );
150                 rc = -EINVAL;
151                 goto err;
152         }
153         if ( pxeromid > rom_len ) {
154                 DBGC ( pxedrv, "PXEDRV %p PXE ROM ID outside ROM\n", pxedrv );
155                 rc = -EINVAL;
156                 goto err;
157         }
158         if ( ( rc = pxedrv_parse_pxeromid ( pxedrv, pxeromid ) ) != 0 )
159                 goto err;
160
161         /* Parse PCIR header, if present */
162         pcirheader = rom.PCIRHeader;
163         if ( pcirheader )
164                 pxedrv_parse_pcirheader ( pxedrv, pcirheader );
165
166         /* Add to PXE driver list and return */
167         DBGC ( pxedrv, "PXEDRV %p registered\n", pxedrv );
168         list_add ( &pxedrv->list, &pxe_drivers );
169         return 0;
170
171  err:
172         free ( pxedrv );
173         return rc;
174 }
175
176 /**
177  * Create PXE drivers for all possible expansion ROMs
178  *
179  * @ret 
180  */
181 static void pxedrv_probe_all_roms ( void ) {
182         static int probed = 0;
183         unsigned int rom_segment;
184
185         /* Perform probe only once */
186         if ( probed )
187                 return;
188
189         DBG ( "Scanning for PXE expansion ROMs\n" );
190
191         /* Scan through expansion ROM region at 2kB intervals */
192         for ( rom_segment = 0xc000 ; rom_segment < 0x10000 ;
193               rom_segment += 0x80 ) {
194                 pxedrv_probe_rom ( rom_segment );
195         }
196
197         probed = 1;
198 }
199
200 /**
201  * Find PXE driver for PCI device
202  *
203  * @v vendor_id         PCI vendor ID
204  * @v device_id         PCI device ID
205  * @v rombase           ROM base address, or 0 for any
206  * @ret pxedrv          PXE driver, or NULL
207  */
208 struct pxe_driver * pxedrv_find_pci_driver ( unsigned int vendor_id,
209                                              unsigned int device_id,
210                                              unsigned int rombase ) {
211         struct pxe_driver *pxedrv;
212
213         pxedrv_probe_all_roms();
214
215         list_for_each_entry ( pxedrv, &pxe_drivers, list ) {
216                 if ( pxedrv->bus_type != PCI_NIC )
217                         continue;
218                 if ( pxedrv->bus_id.pci.vendor_id != vendor_id )
219                         continue;
220                 if ( pxedrv->bus_id.pci.device_id != device_id )
221                         continue;
222                 if ( rombase && ( ( pxedrv->rom_segment << 4 ) != rombase ) )
223                         continue;
224                 DBGC ( pxedrv, "PXEDRV %p matched PCI %04x:%04x (%08x)\n",
225                        pxedrv, vendor_id, device_id, rombase );
226                 return pxedrv;
227         }
228
229         DBG ( "No PXE driver matched PCI %04x:%04x (%08x)\n",
230               vendor_id, device_id, rombase );
231         return NULL;
232 }
233
234 /** Parameter block for calling UNDI loader */
235 static struct s_UNDI_LOADER __data16 ( undi_loader );
236 #define undi_loader __use_data16 ( undi_loader )
237
238 /** UNDI loader entry point */
239 static SEGOFF16_t __data16 ( undi_loader_entry );
240 #define undi_loader_entry __use_data16 ( undi_loader_entry )
241
242 /**
243  * Call UNDI loader to create a pixie
244  *
245  * @v pxedrv            PXE driver
246  * @v pxe               PXE device to be created
247  * @v pci_busdevfn      PCI bus:dev.fn (PCI devices only), or 0
248  * @v isapnp_csn        ISAPnP Card Select Number, or -1U
249  * @v isapnp_read_port  ISAPnP read port, or -1U
250  * @ret rc              Return status code
251  */
252 static int pxedrv_load ( struct pxe_driver *pxedrv, struct pxe_device *pxe,
253                          unsigned int pci_busdevfn, unsigned int isapnp_csn,
254                          unsigned int isapnp_read_port ) {
255         int pnpbios_offset;
256         uint16_t fbms;
257         unsigned int fbms_seg;
258         int discard;
259         uint16_t exit;
260         int rc;
261
262         /* Record device location information */
263         memset ( &undi_loader, 0, sizeof ( undi_loader ) );
264         undi_loader.AX = pci_busdevfn;
265         undi_loader.BX = isapnp_csn;
266         undi_loader.DX = isapnp_read_port;
267
268         /* Set up PnP BIOS pointer, if PnP BIOS present */
269         pnpbios_offset = find_pnp_bios();
270         if ( pnpbios_offset >= 0 ) {
271                 undi_loader.ES = BIOS_SEG;
272                 undi_loader.DI = pnpbios_offset;
273         }
274
275         /* Allocate base memory for PXE stack */
276         get_real ( fbms, BDA_SEG, BDA_FBMS );
277         fbms_seg = ( fbms << 6 );
278         fbms_seg -= ( ( pxedrv->data_size + 0x0f ) >> 4 );
279         undi_loader.UNDI_DS = fbms_seg;
280         fbms_seg -= ( ( pxedrv->code_size + 0x0f ) >> 4 );
281         undi_loader.UNDI_CS = fbms_seg;
282         DBGC ( pxedrv, "PXEDRV %p loading to CS %04x and DS %04x\n", pxedrv,
283                undi_loader.UNDI_CS, undi_loader.UNDI_DS );
284
285         /* Call loader */
286         undi_loader_entry = pxedrv->loader_entry;
287         __asm__ __volatile__ ( REAL_CODE ( "pushw %%ds\n\t"
288                                            "pushw %w0\n\t"
289                                            "lcall *%c3\n\t"
290                                            "addw $4, %%sp\n\t" )
291                                : "=a" ( exit ), "=r" ( discard )
292                                : "0" ( & __from_data16 ( undi_loader ) ),
293                                  "p" ( & __from_data16 ( undi_loader_entry )));
294         if ( exit != PXENV_EXIT_SUCCESS ) {
295                 rc = -undi_loader.Status;
296                 if ( rc == 0 ) /* Paranoia */
297                         rc = -EIO;
298                 DBGC ( pxedrv, "PXEDRV %p loader failed: %s\n", pxedrv,
299                        strerror ( rc ) );
300                 return rc;
301         }
302
303         /* Update free base memory counter */
304         fbms = ( fbms_seg >> 6 );
305         put_real ( fbms, BDA_SEG, BDA_FBMS );
306
307         /* Record location of pixie in PXE device structure */
308         pxe->pxenv = undi_loader.PXENVptr;
309         pxe->ppxe = undi_loader.PXEptr;
310         return 0;
311 }
312
313 /**
314  * Call UNDI loader to create a pixie
315  *
316  * @v pxedrv            PXE driver
317  * @v pxe               PXE device to be created
318  * @v pci_busdevfn      PCI bus:dev.fn
319  * @ret rc              Return status code
320  */
321 int pxedrv_load_pci ( struct pxe_driver *pxedrv, struct pxe_device *pxe,
322                       unsigned int bus, unsigned int devfn ) {
323         return pxedrv_load ( pxedrv, pxe, ( ( bus << 8 ) | devfn ), -1U, -1U );
324 }