• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3  *
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.
8  *
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.
13  *
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., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 
19 FILE_LICENCE ( GPL2_OR_LATER );
20 
21 #include <gpxe/uaccess.h>
22 #include <gpxe/init.h>
23 #include <registers.h>
24 #include <biosint.h>
25 #include <pxe.h>
26 #include <pxe_call.h>
27 
28 /** @file
29  *
30  * PXE API entry point
31  */
32 
33 /** Vector for chaining INT 1A */
34 extern struct segoff __text16 ( pxe_int_1a_vector );
35 #define pxe_int_1a_vector __use_text16 ( pxe_int_1a_vector )
36 
37 /** INT 1A handler */
38 extern void pxe_int_1a ( void );
39 
40 /** INT 1A hooked flag */
41 static int int_1a_hooked = 0;
42 
43 /** A function pointer to hold any PXE API call
44  *
45  * Used by pxe_api_call() to avoid large swathes of duplicated code.
46  */
47 union pxenv_call {
48 	PXENV_EXIT_t ( * any ) ( union u_PXENV_ANY * );
49 	PXENV_EXIT_t ( * unknown ) ( struct s_PXENV_UNKNOWN * );
50 	PXENV_EXIT_t ( * unload_stack ) ( struct s_PXENV_UNLOAD_STACK * );
51 	PXENV_EXIT_t ( * get_cached_info )
52 			( struct s_PXENV_GET_CACHED_INFO * );
53 	PXENV_EXIT_t ( * restart_tftp ) ( struct s_PXENV_TFTP_READ_FILE * );
54 	PXENV_EXIT_t ( * start_undi ) ( struct s_PXENV_START_UNDI * );
55 	PXENV_EXIT_t ( * stop_undi ) ( struct s_PXENV_STOP_UNDI * );
56 	PXENV_EXIT_t ( * start_base ) ( struct s_PXENV_START_BASE * );
57 	PXENV_EXIT_t ( * stop_base ) ( struct s_PXENV_STOP_BASE * );
58 	PXENV_EXIT_t ( * tftp_open ) ( struct s_PXENV_TFTP_OPEN * );
59 	PXENV_EXIT_t ( * tftp_close ) ( struct s_PXENV_TFTP_CLOSE * );
60 	PXENV_EXIT_t ( * tftp_read ) ( struct s_PXENV_TFTP_READ * );
61 	PXENV_EXIT_t ( * tftp_read_file ) ( struct s_PXENV_TFTP_READ_FILE * );
62 	PXENV_EXIT_t ( * tftp_get_fsize ) ( struct s_PXENV_TFTP_GET_FSIZE * );
63 	PXENV_EXIT_t ( * udp_open ) ( struct s_PXENV_UDP_OPEN * );
64 	PXENV_EXIT_t ( * udp_close ) ( struct s_PXENV_UDP_CLOSE * );
65 	PXENV_EXIT_t ( * udp_write ) ( struct s_PXENV_UDP_WRITE * );
66 	PXENV_EXIT_t ( * udp_read ) ( struct s_PXENV_UDP_READ * );
67 	PXENV_EXIT_t ( * undi_startup ) ( struct s_PXENV_UNDI_STARTUP * );
68 	PXENV_EXIT_t ( * undi_cleanup ) ( struct s_PXENV_UNDI_CLEANUP * );
69 	PXENV_EXIT_t ( * undi_initialize )
70 			( struct s_PXENV_UNDI_INITIALIZE * );
71 	PXENV_EXIT_t ( * undi_reset_adapter ) ( struct s_PXENV_UNDI_RESET * );
72 	PXENV_EXIT_t ( * undi_shutdown ) ( struct s_PXENV_UNDI_SHUTDOWN * );
73 	PXENV_EXIT_t ( * undi_open ) ( struct s_PXENV_UNDI_OPEN * );
74 	PXENV_EXIT_t ( * undi_close ) ( struct s_PXENV_UNDI_CLOSE * );
75 	PXENV_EXIT_t ( * undi_transmit ) ( struct s_PXENV_UNDI_TRANSMIT * );
76 	PXENV_EXIT_t ( * undi_set_mcast_address )
77 			( struct s_PXENV_UNDI_SET_MCAST_ADDRESS * );
78 	PXENV_EXIT_t ( * undi_set_station_address )
79 			( struct s_PXENV_UNDI_SET_STATION_ADDRESS * );
80 	PXENV_EXIT_t ( * undi_set_packet_filter )
81 			( struct s_PXENV_UNDI_SET_PACKET_FILTER * );
82 	PXENV_EXIT_t ( * undi_get_information )
83 			( struct s_PXENV_UNDI_GET_INFORMATION * );
84 	PXENV_EXIT_t ( * undi_get_statistics )
85 			( struct s_PXENV_UNDI_GET_STATISTICS * );
86 	PXENV_EXIT_t ( * undi_clear_statistics )
87 			( struct s_PXENV_UNDI_CLEAR_STATISTICS * );
88 	PXENV_EXIT_t ( * undi_initiate_diags )
89 			( struct s_PXENV_UNDI_INITIATE_DIAGS * );
90 	PXENV_EXIT_t ( * undi_force_interrupt )
91 			( struct s_PXENV_UNDI_FORCE_INTERRUPT * );
92 	PXENV_EXIT_t ( * undi_get_mcast_address )
93 			( struct s_PXENV_UNDI_GET_MCAST_ADDRESS * );
94 	PXENV_EXIT_t ( * undi_get_nic_type )
95 			( struct s_PXENV_UNDI_GET_NIC_TYPE * );
96 	PXENV_EXIT_t ( * undi_get_iface_info )
97 			( struct s_PXENV_UNDI_GET_IFACE_INFO * );
98 	PXENV_EXIT_t ( * undi_get_state ) ( struct s_PXENV_UNDI_GET_STATE * );
99 	PXENV_EXIT_t ( * undi_isr ) ( struct s_PXENV_UNDI_ISR * );
100 	PXENV_EXIT_t ( * file_open ) ( struct s_PXENV_FILE_OPEN * );
101 	PXENV_EXIT_t ( * file_close ) ( struct s_PXENV_FILE_CLOSE * );
102 	PXENV_EXIT_t ( * file_select ) ( struct s_PXENV_FILE_SELECT * );
103 	PXENV_EXIT_t ( * file_read ) ( struct s_PXENV_FILE_READ * );
104 	PXENV_EXIT_t ( * get_file_size ) ( struct s_PXENV_GET_FILE_SIZE * );
105 	PXENV_EXIT_t ( * file_exec ) ( struct s_PXENV_FILE_EXEC * );
106 	PXENV_EXIT_t ( * file_api_check ) ( struct s_PXENV_FILE_API_CHECK * );
107 	PXENV_EXIT_t ( * file_exit_hook ) ( struct s_PXENV_FILE_EXIT_HOOK * );
108 };
109 
110 /**
111  * Handle an unknown PXE API call
112  *
113  * @v pxenv_unknown 			Pointer to a struct s_PXENV_UNKNOWN
114  * @ret #PXENV_EXIT_FAILURE		Always
115  * @err #PXENV_STATUS_UNSUPPORTED	Always
116  */
pxenv_unknown(struct s_PXENV_UNKNOWN * pxenv_unknown)117 static PXENV_EXIT_t pxenv_unknown ( struct s_PXENV_UNKNOWN *pxenv_unknown ) {
118 	pxenv_unknown->Status = PXENV_STATUS_UNSUPPORTED;
119 	return PXENV_EXIT_FAILURE;
120 }
121 
122 /**
123  * Dispatch PXE API call
124  *
125  * @v bx		PXE opcode
126  * @v es:di		Address of PXE parameter block
127  * @ret ax		PXE exit code
128  */
pxe_api_call(struct i386_all_regs * ix86)129 __asmcall void pxe_api_call ( struct i386_all_regs *ix86 ) {
130 	int opcode = ix86->regs.bx;
131 	userptr_t parameters = real_to_user ( ix86->segs.es, ix86->regs.di );
132 	size_t param_len;
133 	union u_PXENV_ANY pxenv_any;
134 	union pxenv_call pxenv_call;
135 	PXENV_EXIT_t ret;
136 
137 	switch ( opcode ) {
138 	case PXENV_UNLOAD_STACK:
139 		pxenv_call.unload_stack = pxenv_unload_stack;
140 		param_len = sizeof ( pxenv_any.unload_stack );
141 		break;
142 	case PXENV_GET_CACHED_INFO:
143 		pxenv_call.get_cached_info = pxenv_get_cached_info;
144 		param_len = sizeof ( pxenv_any.get_cached_info );
145 		break;
146 	case PXENV_RESTART_TFTP:
147 		pxenv_call.restart_tftp = pxenv_restart_tftp;
148 		param_len = sizeof ( pxenv_any.restart_tftp );
149 		break;
150 	case PXENV_START_UNDI:
151 		pxenv_call.start_undi = pxenv_start_undi;
152 		param_len = sizeof ( pxenv_any.start_undi );
153 		break;
154 	case PXENV_STOP_UNDI:
155 		pxenv_call.stop_undi = pxenv_stop_undi;
156 		param_len = sizeof ( pxenv_any.stop_undi );
157 		break;
158 	case PXENV_START_BASE:
159 		pxenv_call.start_base = pxenv_start_base;
160 		param_len = sizeof ( pxenv_any.start_base );
161 		break;
162 	case PXENV_STOP_BASE:
163 		pxenv_call.stop_base = pxenv_stop_base;
164 		param_len = sizeof ( pxenv_any.stop_base );
165 		break;
166 	case PXENV_TFTP_OPEN:
167 		pxenv_call.tftp_open = pxenv_tftp_open;
168 		param_len = sizeof ( pxenv_any.tftp_open );
169 		break;
170 	case PXENV_TFTP_CLOSE:
171 		pxenv_call.tftp_close = pxenv_tftp_close;
172 		param_len = sizeof ( pxenv_any.tftp_close );
173 		break;
174 	case PXENV_TFTP_READ:
175 		pxenv_call.tftp_read = pxenv_tftp_read;
176 		param_len = sizeof ( pxenv_any.tftp_read );
177 		break;
178 	case PXENV_TFTP_READ_FILE:
179 		pxenv_call.tftp_read_file = pxenv_tftp_read_file;
180 		param_len = sizeof ( pxenv_any.tftp_read_file );
181 		break;
182 	case PXENV_TFTP_GET_FSIZE:
183 		pxenv_call.tftp_get_fsize = pxenv_tftp_get_fsize;
184 		param_len = sizeof ( pxenv_any.tftp_get_fsize );
185 		break;
186 	case PXENV_UDP_OPEN:
187 		pxenv_call.udp_open = pxenv_udp_open;
188 		param_len = sizeof ( pxenv_any.udp_open );
189 		break;
190 	case PXENV_UDP_CLOSE:
191 		pxenv_call.udp_close = pxenv_udp_close;
192 		param_len = sizeof ( pxenv_any.udp_close );
193 		break;
194 	case PXENV_UDP_WRITE:
195 		pxenv_call.udp_write = pxenv_udp_write;
196 		param_len = sizeof ( pxenv_any.udp_write );
197 		break;
198 	case PXENV_UDP_READ:
199 		pxenv_call.udp_read = pxenv_udp_read;
200 		param_len = sizeof ( pxenv_any.udp_read );
201 		break;
202 	case PXENV_UNDI_STARTUP:
203 		pxenv_call.undi_startup = pxenv_undi_startup;
204 		param_len = sizeof ( pxenv_any.undi_startup );
205 		break;
206 	case PXENV_UNDI_CLEANUP:
207 		pxenv_call.undi_cleanup = pxenv_undi_cleanup;
208 		param_len = sizeof ( pxenv_any.undi_cleanup );
209 		break;
210 	case PXENV_UNDI_INITIALIZE:
211 		pxenv_call.undi_initialize = pxenv_undi_initialize;
212 		param_len = sizeof ( pxenv_any.undi_initialize );
213 		break;
214 	case PXENV_UNDI_RESET_ADAPTER:
215 		pxenv_call.undi_reset_adapter = pxenv_undi_reset_adapter;
216 		param_len = sizeof ( pxenv_any.undi_reset_adapter );
217 		break;
218 	case PXENV_UNDI_SHUTDOWN:
219 		pxenv_call.undi_shutdown = pxenv_undi_shutdown;
220 		param_len = sizeof ( pxenv_any.undi_shutdown );
221 		break;
222 	case PXENV_UNDI_OPEN:
223 		pxenv_call.undi_open = pxenv_undi_open;
224 		param_len = sizeof ( pxenv_any.undi_open );
225 		break;
226 	case PXENV_UNDI_CLOSE:
227 		pxenv_call.undi_close = pxenv_undi_close;
228 		param_len = sizeof ( pxenv_any.undi_close );
229 		break;
230 	case PXENV_UNDI_TRANSMIT:
231 		pxenv_call.undi_transmit = pxenv_undi_transmit;
232 		param_len = sizeof ( pxenv_any.undi_transmit );
233 		break;
234 	case PXENV_UNDI_SET_MCAST_ADDRESS:
235 		pxenv_call.undi_set_mcast_address =
236 			pxenv_undi_set_mcast_address;
237 		param_len = sizeof ( pxenv_any.undi_set_mcast_address );
238 		break;
239 	case PXENV_UNDI_SET_STATION_ADDRESS:
240 		pxenv_call.undi_set_station_address =
241 			pxenv_undi_set_station_address;
242 		param_len = sizeof ( pxenv_any.undi_set_station_address );
243 		break;
244 	case PXENV_UNDI_SET_PACKET_FILTER:
245 		pxenv_call.undi_set_packet_filter =
246 			pxenv_undi_set_packet_filter;
247 		param_len = sizeof ( pxenv_any.undi_set_packet_filter );
248 		break;
249 	case PXENV_UNDI_GET_INFORMATION:
250 		pxenv_call.undi_get_information = pxenv_undi_get_information;
251 		param_len = sizeof ( pxenv_any.undi_get_information );
252 		break;
253 	case PXENV_UNDI_GET_STATISTICS:
254 		pxenv_call.undi_get_statistics = pxenv_undi_get_statistics;
255 		param_len = sizeof ( pxenv_any.undi_get_statistics );
256 		break;
257 	case PXENV_UNDI_CLEAR_STATISTICS:
258 		pxenv_call.undi_clear_statistics = pxenv_undi_clear_statistics;
259 		param_len = sizeof ( pxenv_any.undi_clear_statistics );
260 		break;
261 	case PXENV_UNDI_INITIATE_DIAGS:
262 		pxenv_call.undi_initiate_diags = pxenv_undi_initiate_diags;
263 		param_len = sizeof ( pxenv_any.undi_initiate_diags );
264 		break;
265 	case PXENV_UNDI_FORCE_INTERRUPT:
266 		pxenv_call.undi_force_interrupt = pxenv_undi_force_interrupt;
267 		param_len = sizeof ( pxenv_any.undi_force_interrupt );
268 		break;
269 	case PXENV_UNDI_GET_MCAST_ADDRESS:
270 		pxenv_call.undi_get_mcast_address =
271 			pxenv_undi_get_mcast_address;
272 		param_len = sizeof ( pxenv_any.undi_get_mcast_address );
273 		break;
274 	case PXENV_UNDI_GET_NIC_TYPE:
275 		pxenv_call.undi_get_nic_type = pxenv_undi_get_nic_type;
276 		param_len = sizeof ( pxenv_any.undi_get_nic_type );
277 		break;
278 	case PXENV_UNDI_GET_IFACE_INFO:
279 		pxenv_call.undi_get_iface_info = pxenv_undi_get_iface_info;
280 		param_len = sizeof ( pxenv_any.undi_get_iface_info );
281 		break;
282 	case PXENV_UNDI_ISR:
283 		pxenv_call.undi_isr = pxenv_undi_isr;
284 		param_len = sizeof ( pxenv_any.undi_isr );
285 		break;
286 	case PXENV_FILE_OPEN:
287 		pxenv_call.file_open = pxenv_file_open;
288 		param_len = sizeof ( pxenv_any.file_open );
289 		break;
290 	case PXENV_FILE_CLOSE:
291 		pxenv_call.file_close = pxenv_file_close;
292 		param_len = sizeof ( pxenv_any.file_close );
293 		break;
294 	case PXENV_FILE_SELECT:
295 		pxenv_call.file_select = pxenv_file_select;
296 		param_len = sizeof ( pxenv_any.file_select );
297 		break;
298 	case PXENV_FILE_READ:
299 		pxenv_call.file_read = pxenv_file_read;
300 		param_len = sizeof ( pxenv_any.file_read );
301 		break;
302 	case PXENV_GET_FILE_SIZE:
303 		pxenv_call.get_file_size = pxenv_get_file_size;
304 		param_len = sizeof ( pxenv_any.get_file_size );
305 		break;
306 	case PXENV_FILE_EXEC:
307 		pxenv_call.file_exec = pxenv_file_exec;
308 		param_len = sizeof ( pxenv_any.file_exec );
309 		break;
310 	case PXENV_FILE_API_CHECK:
311 		pxenv_call.file_api_check = pxenv_file_api_check;
312 		param_len = sizeof ( pxenv_any.file_api_check );
313 		break;
314 	case PXENV_FILE_EXIT_HOOK:
315 		pxenv_call.file_exit_hook = pxenv_file_exit_hook;
316 		param_len = sizeof ( pxenv_any.file_exit_hook );
317 		break;
318 	default:
319 		DBG ( "PXENV_UNKNOWN_%hx", opcode );
320 		pxenv_call.unknown = pxenv_unknown;
321 		param_len = sizeof ( pxenv_any.unknown );
322 		break;
323 	}
324 
325 	/* Copy parameter block from caller */
326 	copy_from_user ( &pxenv_any, parameters, 0, param_len );
327 
328 	/* Set default status in case child routine fails to do so */
329 	pxenv_any.Status = PXENV_STATUS_FAILURE;
330 
331 	/* Hand off to relevant API routine */
332 	DBG ( "[" );
333 	ret = pxenv_call.any ( &pxenv_any );
334 	if ( pxenv_any.Status != PXENV_STATUS_SUCCESS ) {
335 		DBG ( " %02x", pxenv_any.Status );
336 	}
337 	if ( ret != PXENV_EXIT_SUCCESS ) {
338 		DBG ( ret == PXENV_EXIT_FAILURE ? " err" : " ??" );
339 	}
340 	DBG ( "]" );
341 
342 	/* Copy modified parameter block back to caller and return */
343 	copy_to_user ( parameters, 0, &pxenv_any, param_len );
344 	ix86->regs.ax = ret;
345 }
346 
347 /**
348  * Dispatch weak PXE API call with PXE stack available
349  *
350  * @v ix86		Registers for PXE call
351  * @ret present		Zero (PXE stack present)
352  */
_pxe_api_call_weak(struct i386_all_regs * ix86)353 int _pxe_api_call_weak ( struct i386_all_regs *ix86 )
354 {
355 	pxe_api_call ( ix86 );
356 	return 0;
357 }
358 
359 /**
360  * Dispatch PXE loader call
361  *
362  * @v es:di		Address of PXE parameter block
363  * @ret ax		PXE exit code
364  */
pxe_loader_call(struct i386_all_regs * ix86)365 __asmcall void pxe_loader_call ( struct i386_all_regs *ix86 ) {
366 	userptr_t uparams = real_to_user ( ix86->segs.es, ix86->regs.di );
367 	struct s_UNDI_LOADER params;
368 	PXENV_EXIT_t ret;
369 
370 	/* Copy parameter block from caller */
371 	copy_from_user ( &params, uparams, 0, sizeof ( params ) );
372 
373 	/* Fill in ROM segment address */
374 	ppxe.UNDIROMID.segment = ix86->segs.ds;
375 
376 	/* Set default status in case child routine fails to do so */
377 	params.Status = PXENV_STATUS_FAILURE;
378 
379 	/* Call UNDI loader */
380 	ret = undi_loader ( &params );
381 
382 	/* Copy modified parameter block back to caller and return */
383 	copy_to_user ( uparams, 0, &params, sizeof ( params ) );
384 	ix86->regs.ax = ret;
385 }
386 
387 /**
388  * Calculate byte checksum as used by PXE
389  *
390  * @v data		Data
391  * @v size		Length of data
392  * @ret sum		Checksum
393  */
pxe_checksum(void * data,size_t size)394 static uint8_t pxe_checksum ( void *data, size_t size ) {
395 	uint8_t *bytes = data;
396 	uint8_t sum = 0;
397 
398 	while ( size-- ) {
399 		sum += *bytes++;
400 	}
401 	return sum;
402 }
403 
404 /**
405  * Initialise !PXE and PXENV+ structures
406  *
407  */
pxe_init_structures(void)408 static void pxe_init_structures ( void ) {
409 	uint32_t rm_cs_phys = ( rm_cs << 4 );
410 	uint32_t rm_ds_phys = ( rm_ds << 4 );
411 
412 	/* Fill in missing segment fields */
413 	ppxe.EntryPointSP.segment = rm_cs;
414 	ppxe.EntryPointESP.segment = rm_cs;
415 	ppxe.Stack.segment_address = rm_ds;
416 	ppxe.Stack.Physical_address = rm_ds_phys;
417 	ppxe.UNDIData.segment_address = rm_ds;
418 	ppxe.UNDIData.Physical_address = rm_ds_phys;
419 	ppxe.UNDICode.segment_address = rm_cs;
420 	ppxe.UNDICode.Physical_address = rm_cs_phys;
421 	ppxe.UNDICodeWrite.segment_address = rm_cs;
422 	ppxe.UNDICodeWrite.Physical_address = rm_cs_phys;
423 	pxenv.RMEntry.segment = rm_cs;
424 	pxenv.StackSeg = rm_ds;
425 	pxenv.UNDIDataSeg = rm_ds;
426 	pxenv.UNDICodeSeg = rm_cs;
427 	pxenv.PXEPtr.segment = rm_cs;
428 
429 	/* Update checksums */
430 	ppxe.StructCksum -= pxe_checksum ( &ppxe, sizeof ( ppxe ) );
431 	pxenv.Checksum -= pxe_checksum ( &pxenv, sizeof ( pxenv ) );
432 }
433 
434 /** PXE structure initialiser */
435 struct init_fn pxe_init_fn __init_fn ( INIT_NORMAL ) = {
436 	.initialise = pxe_init_structures,
437 };
438 
439 /**
440  * Activate PXE stack
441  *
442  * @v netdev		Net device to use as PXE net device
443  */
pxe_activate(struct net_device * netdev)444 void pxe_activate ( struct net_device *netdev ) {
445 
446 	/* Ensure INT 1A is hooked */
447 	if ( ! int_1a_hooked ) {
448 		hook_bios_interrupt ( 0x1a, ( unsigned int ) pxe_int_1a,
449 				      &pxe_int_1a_vector );
450 		int_1a_hooked = 1;
451 	}
452 
453 	/* Set PXE network device */
454 	pxe_set_netdev ( netdev );
455 }
456 
457 /**
458  * Deactivate PXE stack
459  *
460  * @ret rc		Return status code
461  */
pxe_deactivate(void)462 int pxe_deactivate ( void ) {
463 	int rc;
464 
465 	/* Clear PXE network device */
466 	pxe_set_netdev ( NULL );
467 
468 	/* Ensure INT 1A is unhooked, if possible */
469 	if ( int_1a_hooked ) {
470 		if ( ( rc = unhook_bios_interrupt ( 0x1a,
471 						    (unsigned int) pxe_int_1a,
472 						    &pxe_int_1a_vector ))!= 0){
473 			DBG ( "Could not unhook INT 1A: %s\n",
474 			      strerror ( rc ) );
475 			return rc;
476 		}
477 		int_1a_hooked = 0;
478 	}
479 
480 	return 0;
481 }
482 
483 /**
484  * Start PXE NBP at 0000:7c00
485  *
486  * @ret rc		Return status code
487  */
pxe_start_nbp(void)488 int pxe_start_nbp ( void ) {
489 	int discard_b, discard_c, discard_d, discard_D;
490 	uint16_t rc;
491 
492 	/* Far call to PXE NBP */
493 	__asm__ __volatile__ ( REAL_CODE ( "movw %%cx, %%es\n\t"
494 					   "pushw %%es\n\t"
495 					   "pushw %%di\n\t"
496 					   "sti\n\t"
497 					   "lcall $0, $0x7c00\n\t"
498 					   "addw $4, %%sp\n\t" )
499 			       : "=a" ( rc ), "=b" ( discard_b ),
500 				 "=c" ( discard_c ), "=d" ( discard_d ),
501 				 "=D" ( discard_D )
502 			       : "a" ( 0 ), "b" ( __from_text16 ( &pxenv ) ),
503 			         "c" ( rm_cs ),
504 			         "d" ( virt_to_phys ( &pxenv ) ),
505 				 "D" ( __from_text16 ( &ppxe ) )
506 			       : "esi", "ebp", "memory" );
507 
508 	return rc;
509 }
510