Implement the "overwrite TFTP filename" Microsoft RIS bug workaround.
[people/pcmattman/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 <dhcp_basemem.h>
32 #include "pxe.h"
33 #include "pxe_call.h"
34
35 /** Filename used for last TFTP request
36  *
37  * This is a bug-for-bug compatibility hack needed in order to work
38  * with Microsoft Remote Installation Services (RIS).  The filename
39  * used in a call to PXENV_RESTART_TFTP must be returned as the DHCP
40  * filename in subsequent calls to PXENV_GET_CACHED_INFO.
41  */
42 static char *pxe_ris_filename = NULL;
43
44 /**
45  * UNLOAD BASE CODE STACK
46  *
47  * @v None                              -
48  * @ret ...
49  *
50  */
51 PXENV_EXIT_t pxenv_unload_stack ( struct s_PXENV_UNLOAD_STACK *unload_stack ) {
52         DBG ( "PXENV_UNLOAD_STACK" );
53
54 #if 0
55         /* We need to call cleanup() at some point.  The network card
56          * has already been disabled by ENSURE_CAN_UNLOAD(), but for
57          * the sake of completeness we should call the console_fini()
58          * etc. that are part of cleanup().
59          *
60          * There seems to be a lack of consensus on which is the final
61          * PXE API call to make, but it's a fairly safe bet that all
62          * the potential shutdown sequences will include a call to
63          * PXENV_UNLOAD_STACK at some point, so we may as well do it
64          * here.
65          */
66         cleanup();
67
68         if ( ! success ) {
69                 unload_stack->Status = PXENV_STATUS_KEEP_ALL;
70                 return PXENV_EXIT_FAILURE;
71         }
72 #endif
73
74         unload_stack->Status = PXENV_STATUS_SUCCESS;
75         return PXENV_EXIT_SUCCESS;
76 }
77
78 /* PXENV_GET_CACHED_INFO
79  *
80  * Status: working
81  */
82 PXENV_EXIT_t pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO
83                                      *get_cached_info ) {
84         struct dhcp_packet dhcppkt;
85         void *data = NULL;
86         size_t len;
87         int msgtype;
88         struct dhcp_option_block *options;
89         userptr_t buffer;
90         int rc;
91
92         DBG ( "PXENV_GET_CACHED_INFO %d", get_cached_info->PacketType );
93
94         DBG ( " to %04x:%04x+%x", get_cached_info->Buffer.segment,
95               get_cached_info->Buffer.offset, get_cached_info->BufferSize );
96
97         /* This is really, really awkward to support with our multiple
98          * sources of options.
99          */
100         len = get_cached_info->BufferSize;
101         if ( len == 0 ) {
102                 len = sizeof ( dhcp_basemem );
103                 get_cached_info->Buffer.segment = rm_ds;
104                 get_cached_info->Buffer.offset =
105                         ( unsigned int ) ( & __from_data16 ( dhcp_basemem ) );
106                 get_cached_info->BufferLimit = len;
107         }
108
109         /* Allocate space for temporary copy */
110         data = malloc ( len );
111         if ( ! data ) {
112                 DBG ( " out of memory" );
113                 goto err;
114         }
115
116         /* Construct DHCP packet */
117         if ( get_cached_info->PacketType == PXENV_PACKET_TYPE_DHCP_DISCOVER ) {
118                 msgtype = DHCPDISCOVER;
119                 options = &dhcp_request_options;
120         } else {
121                 msgtype = DHCPACK;
122                 options = NULL;
123         }
124         if ( ( rc = create_dhcp_packet ( pxe_netdev, msgtype, data, len,
125                                          &dhcppkt ) ) != 0 ) {
126                 DBG ( " failed to build packet" );
127                 goto err;
128         }
129         if ( ( rc = copy_dhcp_packet_options ( &dhcppkt, options ) ) != 0 ) {
130                 DBG ( " failed to copy options" );
131                 goto err;
132         }
133
134         /* Overwrite filename to work around Microsoft RIS bug */
135         if ( pxe_ris_filename ) {
136                 strncpy ( dhcppkt.dhcphdr->file, pxe_ris_filename,
137                           sizeof ( dhcppkt.dhcphdr->file ) );
138         }
139
140         /* Copy packet to client buffer */
141         buffer = real_to_user ( get_cached_info->Buffer.segment,
142                                 get_cached_info->Buffer.offset );
143         len = dhcppkt.len;
144         copy_to_user ( buffer, 0, data, len );
145         get_cached_info->BufferSize = len;
146
147         free ( data );
148         get_cached_info->Status = PXENV_STATUS_SUCCESS;
149         return PXENV_EXIT_SUCCESS;
150
151  err:
152         if ( data )
153                 free ( data );
154         get_cached_info->Status = PXENV_STATUS_OUT_OF_RESOURCES;
155         return PXENV_EXIT_FAILURE;
156 }
157
158 /* PXENV_RESTART_TFTP
159  *
160  * Status: working
161  */
162 PXENV_EXIT_t pxenv_restart_tftp ( struct s_PXENV_TFTP_READ_FILE
163                                   *restart_tftp ) {
164         PXENV_EXIT_t tftp_exit;
165
166         DBG ( "PXENV_RESTART_TFTP " );
167
168         /* Work around Microsoft RIS bug */
169         free ( pxe_ris_filename );
170         pxe_ris_filename = strdup ( ( char * ) restart_tftp->FileName );
171         if ( ! pxe_ris_filename ) {
172                 restart_tftp->Status = PXENV_STATUS_OUT_OF_RESOURCES;
173                 return PXENV_EXIT_FAILURE;
174         }
175
176         /* Words cannot describe the complete mismatch between the PXE
177          * specification and any possible version of reality...
178          */
179         restart_tftp->Buffer = PXE_LOAD_PHYS; /* Fixed by spec, apparently */
180         restart_tftp->BufferSize = ( 0xa0000 - PXE_LOAD_PHYS ); /* Near enough */
181         tftp_exit = pxenv_tftp_read_file ( restart_tftp );
182         if ( tftp_exit != PXENV_EXIT_SUCCESS )
183                 return tftp_exit;
184
185         /* Fire up the new NBP */
186         restart_tftp->Status = pxe_start_nbp();
187
188         /* Not sure what "SUCCESS" actually means, since we can only
189          * return if the new NBP failed to boot...
190          */
191         return PXENV_EXIT_SUCCESS;
192 }
193
194 /* PXENV_START_UNDI
195  *
196  * Status: working
197  */
198 PXENV_EXIT_t pxenv_start_undi ( struct s_PXENV_START_UNDI *start_undi ) {
199
200         DBG ( "PXENV_START_UNDI" );
201
202 #if 0
203         /* Record PCI bus & devfn passed by caller, so we know which
204          * NIC they want to use.
205          *
206          * If they don't match our already-existing NIC structure, set
207          * values to ensure that the specified NIC is used at the next
208          * call to pxe_intialise_nic().
209          */
210         bus = ( start_undi->AX >> 8 ) & 0xff;
211         devfn = start_undi->AX & 0xff;
212
213 #warning "device probing mechanism has completely changed"
214 #if 0
215         if ( ( pci->dev.driver == NULL ) ||
216              ( pci->dev.bus != bus ) || ( pci->dev.devfn != devfn ) ) {
217                 /* This is quite a bit of a hack and relies on
218                  * knowledge of the internal operation of Etherboot's
219                  * probe mechanism.
220                  */
221                 DBG ( " set PCI %hhx:%hhx.%hhx",
222                       bus, PCI_SLOT(devfn), PCI_FUNC(devfn) );
223                 dev->type = BOOT_NIC;
224                 dev->to_probe = PROBE_PCI;
225                 memset ( &dev->state, 0, sizeof(dev->state) );
226                 pci->advance = 1;
227                 pci->dev.use_specified = 1;
228                 pci->dev.bus = bus;
229                 pci->dev.devfn = devfn;
230         }
231 #endif
232
233 #endif
234
235         start_undi->Status = PXENV_STATUS_SUCCESS;
236         return PXENV_EXIT_SUCCESS;
237 }
238
239 /* PXENV_STOP_UNDI
240  *
241  * Status: working
242  */
243 PXENV_EXIT_t pxenv_stop_undi ( struct s_PXENV_STOP_UNDI *stop_undi ) {
244         DBG ( "PXENV_STOP_UNDI" );
245
246 #if 0
247         if ( ! ensure_pxe_state(CAN_UNLOAD) ) {
248                 stop_undi->Status = PXENV_STATUS_KEEP_UNDI;
249                 return PXENV_EXIT_FAILURE;
250         }
251 #endif
252
253         stop_undi->Status = PXENV_STATUS_SUCCESS;
254         return PXENV_EXIT_SUCCESS;
255 }
256
257 /* PXENV_START_BASE
258  *
259  * Status: won't implement (requires major structural changes)
260  */
261 PXENV_EXIT_t pxenv_start_base ( struct s_PXENV_START_BASE *start_base ) {
262         DBG ( "PXENV_START_BASE" );
263
264         start_base->Status = PXENV_STATUS_UNSUPPORTED;
265         return PXENV_EXIT_FAILURE;
266 }
267
268 /* PXENV_STOP_BASE
269  *
270  * Status: working
271  */
272 PXENV_EXIT_t pxenv_stop_base ( struct s_PXENV_STOP_BASE *stop_base ) {
273         DBG ( "PXENV_STOP_BASE" );
274
275         /* The only time we will be called is when the NBP is trying
276          * to shut down the PXE stack.  There's nothing we need to do
277          * in this call.
278          */
279
280         stop_base->Status = PXENV_STATUS_SUCCESS;
281         return PXENV_EXIT_SUCCESS;
282 }