b4e2206a91577f04cf8738517d60551faf35a11c
[people/xl0/gpxe.git] / src / interface / pxe / pxe_preboot.c
1 /** @file
2  *
3  * PXE Preboot API
4  *
5  */
6
7 /* PXE API interface for Etherboot.
8  *
9  * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License as
13  * published by the Free Software Foundation; either version 2 of the
14  * License, or any later version.
15  *
16  * This program is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24  */
25
26 #include <stdint.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <gpxe/uaccess.h>
30 #include <gpxe/dhcp.h>
31 #include <gpxe/device.h>
32 #include <gpxe/netdevice.h>
33 #include <gpxe/isapnp.h>
34 #include <gpxe/init.h>
35 #include <basemem_packet.h>
36 #include "pxe.h"
37 #include "pxe_call.h"
38
39 /** Filename used for last TFTP request
40  *
41  * This is a bug-for-bug compatibility hack needed in order to work
42  * with Microsoft Remote Installation Services (RIS).  The filename
43  * used in a call to PXENV_RESTART_TFTP must be returned as the DHCP
44  * filename in subsequent calls to PXENV_GET_CACHED_INFO.
45  */
46 static char *pxe_ris_filename = NULL;
47
48 /* Avoid dragging in isapnp.o unnecessarily */
49 uint16_t isapnp_read_port;
50
51 /**
52  * UNLOAD BASE CODE STACK
53  *
54  * @v None                              -
55  * @ret ...
56  *
57  */
58 PXENV_EXIT_t pxenv_unload_stack ( struct s_PXENV_UNLOAD_STACK *unload_stack ) {
59         DBG ( "PXENV_UNLOAD_STACK" );
60
61         unload_stack->Status = PXENV_STATUS_SUCCESS;
62         return PXENV_EXIT_SUCCESS;
63 }
64
65 /* PXENV_GET_CACHED_INFO
66  *
67  * Status: working
68  */
69 PXENV_EXIT_t pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO
70                                      *get_cached_info ) {
71         struct dhcp_packet dhcppkt;
72         void *data = NULL;
73         size_t len;
74         int msgtype;
75         struct dhcp_option_block *options;
76         userptr_t buffer;
77         int rc;
78
79         DBG ( "PXENV_GET_CACHED_INFO %d", get_cached_info->PacketType );
80
81         DBG ( " to %04x:%04x+%x", get_cached_info->Buffer.segment,
82               get_cached_info->Buffer.offset, get_cached_info->BufferSize );
83
84         /* This is really, really awkward to support with our multiple
85          * sources of options.
86          */
87         len = get_cached_info->BufferSize;
88         if ( len == 0 ) {
89                 len = sizeof ( basemem_packet );
90                 get_cached_info->Buffer.segment = rm_ds;
91                 get_cached_info->Buffer.offset =
92                         ( unsigned int ) ( & __from_data16 ( basemem_packet ) );
93                 get_cached_info->BufferLimit = len;
94         }
95
96         /* Allocate space for temporary copy */
97         data = malloc ( len );
98         if ( ! data ) {
99                 DBG ( " out of memory" );
100                 goto err;
101         }
102
103         /* Construct DHCP packet */
104         if ( get_cached_info->PacketType == PXENV_PACKET_TYPE_DHCP_DISCOVER ) {
105                 msgtype = DHCPDISCOVER;
106                 options = &dhcp_request_options;
107         } else {
108                 msgtype = DHCPACK;
109                 options = NULL;
110         }
111         if ( ( rc = create_dhcp_packet ( pxe_netdev, msgtype, data, len,
112                                          &dhcppkt ) ) != 0 ) {
113                 DBG ( " failed to build packet" );
114                 goto err;
115         }
116         if ( ( rc = copy_dhcp_packet_options ( &dhcppkt, options ) ) != 0 ) {
117                 DBG ( " failed to copy options" );
118                 goto err;
119         }
120
121         /* Overwrite filename to work around Microsoft RIS bug */
122         if ( pxe_ris_filename ) {
123                 strncpy ( dhcppkt.dhcphdr->file, pxe_ris_filename,
124                           sizeof ( dhcppkt.dhcphdr->file ) );
125         }
126
127         /* Copy packet to client buffer */
128         buffer = real_to_user ( get_cached_info->Buffer.segment,
129                                 get_cached_info->Buffer.offset );
130         len = dhcppkt.len;
131         copy_to_user ( buffer, 0, data, len );
132         get_cached_info->BufferSize = len;
133
134         free ( data );
135         get_cached_info->Status = PXENV_STATUS_SUCCESS;
136         return PXENV_EXIT_SUCCESS;
137
138  err:
139         if ( data )
140                 free ( data );
141         get_cached_info->Status = PXENV_STATUS_OUT_OF_RESOURCES;
142         return PXENV_EXIT_FAILURE;
143 }
144
145 /* PXENV_RESTART_TFTP
146  *
147  * Status: working
148  */
149 PXENV_EXIT_t pxenv_restart_tftp ( struct s_PXENV_TFTP_READ_FILE
150                                   *restart_tftp ) {
151         PXENV_EXIT_t tftp_exit;
152
153         DBG ( "PXENV_RESTART_TFTP " );
154
155         /* Work around Microsoft RIS bug */
156         free ( pxe_ris_filename );
157         pxe_ris_filename = strdup ( ( char * ) restart_tftp->FileName );
158         if ( ! pxe_ris_filename ) {
159                 restart_tftp->Status = PXENV_STATUS_OUT_OF_RESOURCES;
160                 return PXENV_EXIT_FAILURE;
161         }
162
163         /* Words cannot describe the complete mismatch between the PXE
164          * specification and any possible version of reality...
165          */
166         restart_tftp->Buffer = PXE_LOAD_PHYS; /* Fixed by spec, apparently */
167         restart_tftp->BufferSize = ( 0xa0000 - PXE_LOAD_PHYS ); /* Near enough */
168         tftp_exit = pxenv_tftp_read_file ( restart_tftp );
169         if ( tftp_exit != PXENV_EXIT_SUCCESS )
170                 return tftp_exit;
171
172         /* Fire up the new NBP */
173         restart_tftp->Status = pxe_start_nbp();
174
175         /* Not sure what "SUCCESS" actually means, since we can only
176          * return if the new NBP failed to boot...
177          */
178         return PXENV_EXIT_SUCCESS;
179 }
180
181 /* PXENV_START_UNDI
182  *
183  * Status: working
184  */
185 PXENV_EXIT_t pxenv_start_undi ( struct s_PXENV_START_UNDI *start_undi ) {
186         unsigned int bus_type;
187         unsigned int location;
188         struct net_device *netdev;
189
190         DBG ( "PXENV_START_UNDI %04x:%04x:%04x",
191               start_undi->AX, start_undi->BX, start_undi->DX );
192
193         /* Determine bus type and location.  Use a heuristic to decide
194          * whether we are PCI or ISAPnP
195          */
196         if ( ( start_undi->DX >= ISAPNP_READ_PORT_MIN ) &&
197              ( start_undi->DX <= ISAPNP_READ_PORT_MAX ) &&
198              ( start_undi->BX >= ISAPNP_CSN_MIN ) &&
199              ( start_undi->BX <= ISAPNP_CSN_MAX ) ) {
200                 bus_type = BUS_TYPE_ISAPNP;
201                 location = start_undi->BX;
202                 /* Record ISAPnP read port for use by isapnp.c */
203                 isapnp_read_port = start_undi->DX;
204         } else {
205                 bus_type = BUS_TYPE_PCI;
206                 location = start_undi->AX;
207         }
208
209         /* Probe for devices, etc. */
210         startup();
211
212         /* Look for a matching net device */
213         netdev = find_netdev_by_location ( bus_type, location );
214         if ( ! netdev ) {
215                 DBG ( " no net device found" );
216                 start_undi->Status = PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC;
217                 return PXENV_EXIT_FAILURE;
218         }
219         DBG ( " using netdev %s", netdev->name );
220
221         /* Save as PXE net device */
222         pxe_set_netdev ( netdev );
223
224         /* Hook INT 1A */
225         pxe_hook_int1a();
226
227         start_undi->Status = PXENV_STATUS_SUCCESS;
228         return PXENV_EXIT_SUCCESS;
229 }
230
231 /* PXENV_STOP_UNDI
232  *
233  * Status: working
234  */
235 PXENV_EXIT_t pxenv_stop_undi ( struct s_PXENV_STOP_UNDI *stop_undi ) {
236         DBG ( "PXENV_STOP_UNDI" );
237
238         /* Unhook INT 1A */
239         pxe_unhook_int1a();
240
241         /* Clear PXE net device */
242         pxe_set_netdev ( NULL );
243
244         /* Prepare for unload */
245         shutdown();
246
247         stop_undi->Status = PXENV_STATUS_SUCCESS;
248         return PXENV_EXIT_SUCCESS;
249 }
250
251 /* PXENV_START_BASE
252  *
253  * Status: won't implement (requires major structural changes)
254  */
255 PXENV_EXIT_t pxenv_start_base ( struct s_PXENV_START_BASE *start_base ) {
256         DBG ( "PXENV_START_BASE" );
257
258         start_base->Status = PXENV_STATUS_UNSUPPORTED;
259         return PXENV_EXIT_FAILURE;
260 }
261
262 /* PXENV_STOP_BASE
263  *
264  * Status: working
265  */
266 PXENV_EXIT_t pxenv_stop_base ( struct s_PXENV_STOP_BASE *stop_base ) {
267         DBG ( "PXENV_STOP_BASE" );
268
269         /* The only time we will be called is when the NBP is trying
270          * to shut down the PXE stack.  There's nothing we need to do
271          * in this call.
272          */
273
274         stop_base->Status = PXENV_STATUS_SUCCESS;
275         return PXENV_EXIT_SUCCESS;
276 }