Home | History | Annotate | Download | only in pxe
      1 /*
      2  * Copyright (C) 2006 Michael Brown <mbrown (at) 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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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