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 ( ¶ms, 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 ( ¶ms ); 381 382 /* Copy modified parameter block back to caller and return */ 383 copy_to_user ( uparams, 0, ¶ms, 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