2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
26 #include <ipxe/uaccess.h>
27 #include <ipxe/init.h>
28 #include <ipxe/profile.h>
30 #include <registers.h>
40 /* Disambiguate the various error causes */
41 #define EINFO_EPXENBP \
42 __einfo_uniqify ( EINFO_EPLATFORM, 0x01, \
43 "External PXE NBP error" )
44 #define EPXENBP( status ) EPLATFORM ( EINFO_EPXENBP, status )
46 /** Vector for chaining INT 1A */
47 extern struct segoff __text16 ( pxe_int_1a_vector );
48 #define pxe_int_1a_vector __use_text16 ( pxe_int_1a_vector )
51 extern void pxe_int_1a ( void );
53 /** INT 1A hooked flag */
54 static int int_1a_hooked = 0;
56 /** PXENV_UNDI_TRANSMIT API call profiler */
57 static struct profiler pxe_api_tx_profiler __profiler =
58 { .name = "pxeapi.tx" };
60 /** PXENV_UNDI_ISR API call profiler */
61 static struct profiler pxe_api_isr_profiler __profiler =
62 { .name = "pxeapi.isr" };
64 /** PXE unknown API call profiler
66 * This profiler can be used to measure the overhead of a dummy PXE
69 static struct profiler pxe_api_unknown_profiler __profiler =
70 { .name = "pxeapi.unknown" };
72 /** Miscellaneous PXE API call profiler */
73 static struct profiler pxe_api_misc_profiler __profiler =
74 { .name = "pxeapi.misc" };
77 * Handle an unknown PXE API call
79 * @v pxenv_unknown Pointer to a struct s_PXENV_UNKNOWN
80 * @ret #PXENV_EXIT_FAILURE Always
81 * @err #PXENV_STATUS_UNSUPPORTED Always
83 static PXENV_EXIT_t pxenv_unknown ( struct s_PXENV_UNKNOWN *pxenv_unknown ) {
84 pxenv_unknown->Status = PXENV_STATUS_UNSUPPORTED;
85 return PXENV_EXIT_FAILURE;
88 /** Unknown PXE API call list */
89 struct pxe_api_call pxenv_unknown_api __pxe_api_call =
90 PXE_API_CALL ( PXENV_UNKNOWN, pxenv_unknown, struct s_PXENV_UNKNOWN );
96 * @ret call PXE API call, or NULL
98 static struct pxe_api_call * find_pxe_api_call ( uint16_t opcode ) {
99 struct pxe_api_call *call;
101 for_each_table_entry ( call, PXE_API_CALLS ) {
102 if ( call->opcode == opcode )
109 * Determine applicable profiler (for debugging)
111 * @v opcode PXE opcode
112 * @ret profiler Profiler
114 static struct profiler * pxe_api_profiler ( unsigned int opcode ) {
116 /* Determine applicable profiler */
118 case PXENV_UNDI_TRANSMIT:
119 return &pxe_api_tx_profiler;
121 return &pxe_api_isr_profiler;
123 return &pxe_api_unknown_profiler;
125 return &pxe_api_misc_profiler;
130 * Dispatch PXE API call
133 * @v es:di Address of PXE parameter block
134 * @ret ax PXE exit code
136 __asmcall void pxe_api_call ( struct i386_all_regs *ix86 ) {
137 uint16_t opcode = ix86->regs.bx;
138 userptr_t uparams = real_to_user ( ix86->segs.es, ix86->regs.di );
139 struct profiler *profiler = pxe_api_profiler ( opcode );
140 struct pxe_api_call *call;
141 union u_PXENV_ANY params;
144 /* Start profiling */
145 profile_start ( profiler );
147 /* Locate API call */
148 call = find_pxe_api_call ( opcode );
150 DBGC ( &pxe_netdev, "PXENV_UNKNOWN_%04x\n", opcode );
151 call = &pxenv_unknown_api;
154 /* Copy parameter block from caller */
155 copy_from_user ( ¶ms, uparams, 0, call->params_len );
157 /* Set default status in case child routine fails to do so */
158 params.Status = PXENV_STATUS_FAILURE;
160 /* Hand off to relevant API routine */
161 ret = call->entry ( ¶ms );
163 /* Copy modified parameter block back to caller and return */
164 copy_to_user ( uparams, 0, ¶ms, call->params_len );
167 /* Stop profiling, if applicable */
168 profile_stop ( profiler );
172 * Dispatch weak PXE API call with PXE stack available
174 * @v ix86 Registers for PXE call
175 * @ret present Zero (PXE stack present)
177 int pxe_api_call_weak ( struct i386_all_regs *ix86 ) {
178 pxe_api_call ( ix86 );
183 * Dispatch PXE loader call
185 * @v es:di Address of PXE parameter block
186 * @ret ax PXE exit code
188 __asmcall void pxe_loader_call ( struct i386_all_regs *ix86 ) {
189 userptr_t uparams = real_to_user ( ix86->segs.es, ix86->regs.di );
190 struct s_UNDI_LOADER params;
193 /* Copy parameter block from caller */
194 copy_from_user ( ¶ms, uparams, 0, sizeof ( params ) );
196 /* Fill in ROM segment address */
197 ppxe.UNDIROMID.segment = ix86->segs.ds;
199 /* Set default status in case child routine fails to do so */
200 params.Status = PXENV_STATUS_FAILURE;
202 /* Call UNDI loader */
203 ret = undi_loader ( ¶ms );
205 /* Copy modified parameter block back to caller and return */
206 copy_to_user ( uparams, 0, ¶ms, sizeof ( params ) );
211 * Calculate byte checksum as used by PXE
214 * @v size Length of data
217 static uint8_t pxe_checksum ( void *data, size_t size ) {
218 uint8_t *bytes = data;
228 * Initialise !PXE and PXENV+ structures
231 static void pxe_init_structures ( void ) {
232 uint32_t rm_cs_phys = ( rm_cs << 4 );
233 uint32_t rm_ds_phys = ( rm_ds << 4 );
235 /* Fill in missing segment fields */
236 ppxe.EntryPointSP.segment = rm_cs;
237 ppxe.EntryPointESP.segment = rm_cs;
238 ppxe.Stack.segment_address = rm_ds;
239 ppxe.Stack.Physical_address = rm_ds_phys;
240 ppxe.UNDIData.segment_address = rm_ds;
241 ppxe.UNDIData.Physical_address = rm_ds_phys;
242 ppxe.UNDICode.segment_address = rm_cs;
243 ppxe.UNDICode.Physical_address = rm_cs_phys;
244 ppxe.UNDICodeWrite.segment_address = rm_cs;
245 ppxe.UNDICodeWrite.Physical_address = rm_cs_phys;
246 pxenv.RMEntry.segment = rm_cs;
247 pxenv.StackSeg = rm_ds;
248 pxenv.UNDIDataSeg = rm_ds;
249 pxenv.UNDICodeSeg = rm_cs;
250 pxenv.PXEPtr.segment = rm_cs;
252 /* Update checksums */
253 ppxe.StructCksum -= pxe_checksum ( &ppxe, sizeof ( ppxe ) );
254 pxenv.Checksum -= pxe_checksum ( &pxenv, sizeof ( pxenv ) );
257 /** PXE structure initialiser */
258 struct init_fn pxe_init_fn __init_fn ( INIT_NORMAL ) = {
259 .initialise = pxe_init_structures,
265 * @v netdev Net device to use as PXE net device
267 void pxe_activate ( struct net_device *netdev ) {
269 /* Ensure INT 1A is hooked */
270 if ( ! int_1a_hooked ) {
271 hook_bios_interrupt ( 0x1a, ( unsigned int ) pxe_int_1a,
272 &pxe_int_1a_vector );
277 /* Set PXE network device */
278 pxe_set_netdev ( netdev );
282 * Deactivate PXE stack
284 * @ret rc Return status code
286 int pxe_deactivate ( void ) {
289 /* Clear PXE network device */
290 pxe_set_netdev ( NULL );
292 /* Ensure INT 1A is unhooked, if possible */
293 if ( int_1a_hooked ) {
294 if ( ( rc = unhook_bios_interrupt ( 0x1a,
295 (unsigned int) pxe_int_1a,
296 &pxe_int_1a_vector ))!= 0){
297 DBG ( "Could not unhook INT 1A: %s\n",
308 /** Jump buffer for PXENV_RESTART_TFTP */
309 rmjmp_buf pxe_restart_nbp;
312 * Start PXE NBP at 0000:7c00
314 * @ret rc Return status code
316 int pxe_start_nbp ( void ) {
318 int discard_b, discard_c, discard_d, discard_D;
321 /* Allow restarting NBP via PXENV_RESTART_TFTP */
322 jmp = rmsetjmp ( pxe_restart_nbp );
324 DBG ( "Restarting NBP (%x)\n", jmp );
326 /* Far call to PXE NBP */
327 __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
328 "movw %%cx, %%es\n\t"
332 "lcall $0, $0x7c00\n\t"
333 "popl %%ebp\n\t" /* discard */
334 "popl %%ebp\n\t" /* gcc bug */ )
335 : "=a" ( status ), "=b" ( discard_b ),
336 "=c" ( discard_c ), "=d" ( discard_d ),
338 : "a" ( 0 ), "b" ( __from_text16 ( &pxenv ) ),
340 "d" ( virt_to_phys ( &pxenv ) ),
341 "D" ( __from_text16 ( &ppxe ) )
344 return -EPXENBP ( status );
349 REQUIRING_SYMBOL ( pxe_api_call );
350 REQUIRE_OBJECT ( pxe_preboot );
351 REQUIRE_OBJECT ( pxe_undi );
352 REQUIRE_OBJECT ( pxe_udp );
353 REQUIRE_OBJECT ( pxe_tftp );
354 REQUIRE_OBJECT ( pxe_file );