06e6121f7e555aab28a063f8319eac33f7c2361a
[people/xl0/gpxe.git] / src / interface / pxe / pxe.c
1 /** @file
2  *
3  * 
4  *
5  */
6
7 /*
8  * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as
12  * published by the Free Software Foundation; either version 2 of the
13  * License, or any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  */
24
25 #include "pxe.h"
26
27 /* Global pointer to currently installed PXE stack */
28 pxe_stack_t *pxe_stack = NULL;
29
30 /* Various startup/shutdown routines.  The startup/shutdown call
31  * sequence is incredibly badly defined in the Intel PXE spec, for
32  * example:
33  *
34  *   PXENV_UNDI_INITIALIZE says that the parameters used to initialize
35  *   the adaptor should be those supplied to the most recent
36  *   PXENV_UNDI_STARTUP call.  PXENV_UNDI_STARTUP takes no parameters.
37  *
38  *   PXENV_UNDI_CLEANUP says that the rest of the API will not be
39  *   available after making this call.  Figure 3-3 ("Early UNDI API
40  *   usage") shows a call to PXENV_UNDI_CLEANUP being followed by a
41  *   call to the supposedly now unavailable PXENV_STOP_UNDI.
42  *
43  *   PXENV_UNLOAD_BASE_STACK talks about freeing up the memory
44  *   occupied by the PXE stack.  Figure 4-3 ("PXE IPL") shows a call
45  *   to PXENV_STOP_UNDI being made after the call to
46  *   PXENV_UNLOAD_BASE_STACK, by which time the entire PXE stack
47  *   should have been freed (and, potentially, zeroed).
48  *
49  *   Nothing, anywhere, seems to mention who's responsible for freeing
50  *   up the base memory allocated for the stack segment.  It's not
51  *   even clear whether or not this is expected to be in free base
52  *   memory rather than claimed base memory.
53  *
54  * Consequently, we adopt a rather defensive strategy, designed to
55  * work with any conceivable sequence of initialisation or shutdown
56  * calls.  We have only two things that we care about:
57  *
58  *   1. Have we hooked INT 1A and INT 15,E820(etc.)?
59  *   2. Is the NIC initialised?
60  *
61  * The NIC should never be initialised without the vectors being
62  * hooked, similarly the vectors should never be unhooked with the NIC
63  * still initialised.  We do, however, want to be able to have the
64  * vectors hooked with the NIC shutdown.  We therefore have three
65  * possible states:
66  *
67  *   1. Ready to unload: interrupts unhooked, NIC shutdown.
68  *   2. Midway: interrupts hooked, NIC shutdown.
69  *   3. Fully ready: interrupts hooked, NIC initialised.
70  *
71  * We provide the three states CAN_UNLOAD, MIDWAY and READY to define
72  * these, and the call pxe_ensure_state() to ensure that the stack is
73  * in the specified state.  All our PXE API call implementations
74  * should use this call to ensure that the state is as required for
75  * that PXE API call.  This enables us to cope with whatever the
76  * end-user's interpretation of the PXE spec may be.  It even allows
77  * for someone calling e.g. PXENV_START_UNDI followed by
78  * PXENV_UDP_WRITE, without bothering with any of the intervening
79  * calls.
80  *
81  * pxe_ensure_state() returns 1 for success, 0 for failure.  In the
82  * event of failure (which can arise from e.g. asking for state READY
83  * when we don't know where our NIC is), the error code
84  * PXENV_STATUS_UNDI_INVALID_STATE should be returned to the user.
85  * The macros ENSURE_XXX() can be used to achieve this without lots of
86  * duplicated code.
87  */
88
89 /* pxe_[un]hook_stack are architecture-specific and provided in
90  * pxe_callbacks.c
91  */
92
93 int pxe_initialise_nic ( void ) {
94         if ( pxe_stack->state >= READY ) return 1;
95
96 #warning "device probing mechanism has completely changed"
97 #if 0
98
99         /* Check if NIC is initialised.  dev.disable is set to 0
100          * when disable() is called, so we use this.
101          */
102         if ( dev.disable ) {
103                 /* NIC may have been initialised independently
104                  * (e.g. when we set up the stack prior to calling the
105                  * NBP).
106                  */
107                 pxe_stack->state = READY;
108                 return 1;
109         }
110
111         /* If we already have a NIC defined, reuse that one with
112          * PROBE_AWAKE.  If one was specifed via PXENV_START_UNDI, try
113          * that one first.  Otherwise, set PROBE_FIRST.
114          */
115
116         if ( dev.state.pci.dev.use_specified == 1 ) {
117                 dev.how_probe = PROBE_NEXT;
118                 DBG ( " initialising NIC specified via START_UNDI" );
119         } else if ( dev.state.pci.dev.driver ) {
120                 DBG ( " reinitialising NIC" );
121                 dev.how_probe = PROBE_AWAKE;
122         } else {
123                 DBG ( " probing for any NIC" );
124                 dev.how_probe = PROBE_FIRST;
125         }
126
127         /* Call probe routine to bring up the NIC */
128         if ( eth_probe ( &dev ) != PROBE_WORKED ) {
129                 DBG ( " failed" );
130                 return 0;
131         }
132 #endif
133         
134
135         pxe_stack->state = READY;
136         return 1;
137 }
138
139 int pxe_shutdown_nic ( void ) {
140         if ( pxe_stack->state <= MIDWAY ) return 1;
141
142         eth_irq ( DISABLE );
143         disable ( &dev );
144         pxe_stack->state = MIDWAY;
145         return 1;
146 }
147
148 int ensure_pxe_state ( pxe_stack_state_t wanted ) {
149         int success = 1;
150
151         if ( ! pxe_stack ) return 0;
152         if ( wanted >= MIDWAY )
153                 success = success & hook_pxe_stack();
154         if ( wanted > MIDWAY ) {
155                 success = success & pxe_initialise_nic();
156         } else {
157                 success = success & pxe_shutdown_nic();
158         }
159         if ( wanted < MIDWAY )
160                 success = success & unhook_pxe_stack();
161         return success;
162 }
163
164 /* API call dispatcher
165  *
166  * Status: complete
167  */
168 PXENV_EXIT_t pxe_api_call ( int opcode, union u_PXENV_ANY *any ) {
169         PXENV_EXIT_t ret = PXENV_EXIT_FAILURE;
170
171         /* Set default status in case child routine fails to do so */
172         any->Status = PXENV_STATUS_FAILURE;
173
174         DBG ( "[" );
175
176         /* Hand off to relevant API routine */
177         switch ( opcode ) {
178         case PXENV_START_UNDI:
179                 ret = pxenv_start_undi ( &any->start_undi );
180                 break;
181         case PXENV_UNDI_STARTUP:
182                 ret = pxenv_undi_startup ( &any->undi_startup );
183                 break;
184         case PXENV_UNDI_CLEANUP:
185                 ret = pxenv_undi_cleanup ( &any->undi_cleanup );
186                 break;
187         case PXENV_UNDI_INITIALIZE:
188                 ret = pxenv_undi_initialize ( &any->undi_initialize );
189                 break;
190         case PXENV_UNDI_RESET_ADAPTER:
191                 ret = pxenv_undi_reset_adapter ( &any->undi_reset_adapter );
192                 break;
193         case PXENV_UNDI_SHUTDOWN:
194                 ret = pxenv_undi_shutdown ( &any->undi_shutdown );
195                 break;
196         case PXENV_UNDI_OPEN:
197                 ret = pxenv_undi_open ( &any->undi_open );
198                 break;
199         case PXENV_UNDI_CLOSE:
200                 ret = pxenv_undi_close ( &any->undi_close );
201                 break;
202         case PXENV_UNDI_TRANSMIT:
203                 ret = pxenv_undi_transmit ( &any->undi_transmit );
204                 break;
205         case PXENV_UNDI_SET_MCAST_ADDRESS:
206                 ret = pxenv_undi_set_mcast_address (
207                                                 &any->undi_set_mcast_address );
208                 break;
209         case PXENV_UNDI_SET_STATION_ADDRESS:
210                 ret = pxenv_undi_set_station_address (
211                                               &any->undi_set_station_address );
212                 break;
213         case PXENV_UNDI_SET_PACKET_FILTER:
214                 ret = pxenv_undi_set_packet_filter (
215                                                 &any->undi_set_packet_filter );
216                 break;
217         case PXENV_UNDI_GET_INFORMATION:
218                 ret = pxenv_undi_get_information (
219                                                &any->undi_get_information );
220                 break;
221         case PXENV_UNDI_GET_STATISTICS:
222                 ret = pxenv_undi_get_statistics ( &any->undi_get_statistics );
223                 break;
224         case PXENV_UNDI_CLEAR_STATISTICS:
225                 ret = pxenv_undi_clear_statistics (
226                                                  &any->undi_clear_statistics );
227                 break;
228         case PXENV_UNDI_INITIATE_DIAGS:
229                 ret = pxenv_undi_initiate_diags ( &any->undi_initiate_diags );
230                                                  
231                 break;
232         case PXENV_UNDI_FORCE_INTERRUPT:
233                 ret = pxenv_undi_force_interrupt (
234                                                &any->undi_force_interrupt );
235                 break;
236         case PXENV_UNDI_GET_MCAST_ADDRESS:
237                 ret = pxenv_undi_get_mcast_address (
238                                              &any->undi_get_mcast_address );
239                 break;
240         case PXENV_UNDI_GET_NIC_TYPE:
241                 ret = pxenv_undi_get_nic_type ( &any->undi_get_nic_type );
242                 break;
243         case PXENV_UNDI_GET_IFACE_INFO:
244                 ret = pxenv_undi_get_iface_info ( &any->undi_get_iface_info );
245                 break;
246         case PXENV_UNDI_ISR:
247                 ret = pxenv_undi_isr ( &any->undi_isr );
248                 break;
249         case PXENV_STOP_UNDI:
250                 ret = pxenv_stop_undi ( &any->stop_undi );
251                 break;
252         case PXENV_TFTP_OPEN:
253                 ret = pxenv_tftp_open ( &any->tftp_open );
254                 break;
255         case PXENV_TFTP_CLOSE:
256                 ret = pxenv_tftp_close ( &any->tftp_close );
257                 break;
258         case PXENV_TFTP_READ:
259                 ret = pxenv_tftp_read ( &any->tftp_read );
260                 break;
261         case PXENV_TFTP_READ_FILE:
262                 ret = pxenv_tftp_read_file ( &any->tftp_read_file );
263                 break;
264         case PXENV_TFTP_GET_FSIZE:
265                 ret = pxenv_tftp_get_fsize ( &any->tftp_get_fsize );
266                 break;
267         case PXENV_UDP_OPEN:
268                 ret = pxenv_udp_open ( &any->udp_open );
269                 break;
270         case PXENV_UDP_CLOSE:
271                 ret = pxenv_udp_close ( &any->udp_close );
272                 break;
273         case PXENV_UDP_READ:
274                 ret = pxenv_udp_read ( &any->udp_read );
275                 break;
276         case PXENV_UDP_WRITE:
277                 ret = pxenv_udp_write ( &any->udp_write );
278                 break;
279         case PXENV_UNLOAD_STACK:
280                 ret = pxenv_unload_stack ( &any->unload_stack );
281                 break;
282         case PXENV_GET_CACHED_INFO:
283                 ret = pxenv_get_cached_info ( &any->get_cached_info );
284                 break;
285         case PXENV_RESTART_TFTP:
286                 ret = pxenv_restart_tftp ( &any->restart_tftp );
287                 break;
288         case PXENV_START_BASE:
289                 ret = pxenv_start_base ( &any->start_base );
290                 break;
291         case PXENV_STOP_BASE:
292                 ret = pxenv_stop_base ( &any->stop_base );
293                 break;
294                 
295         default:
296                 DBG ( "PXENV_UNKNOWN_%hx", opcode );
297                 any->Status = PXENV_STATUS_UNSUPPORTED;
298                 ret = PXENV_EXIT_FAILURE;
299                 break;
300         }
301
302         if ( any->Status != PXENV_STATUS_SUCCESS ) {
303                 DBG ( " %hx", any->Status );
304         }
305         if ( ret != PXENV_EXIT_SUCCESS ) {
306                 DBG ( ret == PXENV_EXIT_FAILURE ? " err" : " ??" );
307         }
308         DBG ( "]" );
309
310         return ret;
311 }