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