1 /* 2 * Copyright (C) 2007 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/dhcp.h> 22 #include <pxeparent.h> 23 #include <pxe_api.h> 24 #include <pxe_types.h> 25 #include <pxe.h> 26 27 /** @file 28 * 29 * Call interface to parent PXE stack 30 * 31 */ 32 33 /** 34 * Name PXE API call 35 * 36 * @v function API call number 37 * @ret name API call name 38 */ 39 static inline __attribute__ (( always_inline )) const char * 40 pxeparent_function_name ( unsigned int function ) { 41 switch ( function ) { 42 case PXENV_START_UNDI: 43 return "PXENV_START_UNDI"; 44 case PXENV_STOP_UNDI: 45 return "PXENV_STOP_UNDI"; 46 case PXENV_UNDI_STARTUP: 47 return "PXENV_UNDI_STARTUP"; 48 case PXENV_UNDI_CLEANUP: 49 return "PXENV_UNDI_CLEANUP"; 50 case PXENV_UNDI_INITIALIZE: 51 return "PXENV_UNDI_INITIALIZE"; 52 case PXENV_UNDI_RESET_ADAPTER: 53 return "PXENV_UNDI_RESET_ADAPTER"; 54 case PXENV_UNDI_SHUTDOWN: 55 return "PXENV_UNDI_SHUTDOWN"; 56 case PXENV_UNDI_OPEN: 57 return "PXENV_UNDI_OPEN"; 58 case PXENV_UNDI_CLOSE: 59 return "PXENV_UNDI_CLOSE"; 60 case PXENV_UNDI_TRANSMIT: 61 return "PXENV_UNDI_TRANSMIT"; 62 case PXENV_UNDI_SET_MCAST_ADDRESS: 63 return "PXENV_UNDI_SET_MCAST_ADDRESS"; 64 case PXENV_UNDI_SET_STATION_ADDRESS: 65 return "PXENV_UNDI_SET_STATION_ADDRESS"; 66 case PXENV_UNDI_SET_PACKET_FILTER: 67 return "PXENV_UNDI_SET_PACKET_FILTER"; 68 case PXENV_UNDI_GET_INFORMATION: 69 return "PXENV_UNDI_GET_INFORMATION"; 70 case PXENV_UNDI_GET_STATISTICS: 71 return "PXENV_UNDI_GET_STATISTICS"; 72 case PXENV_UNDI_CLEAR_STATISTICS: 73 return "PXENV_UNDI_CLEAR_STATISTICS"; 74 case PXENV_UNDI_INITIATE_DIAGS: 75 return "PXENV_UNDI_INITIATE_DIAGS"; 76 case PXENV_UNDI_FORCE_INTERRUPT: 77 return "PXENV_UNDI_FORCE_INTERRUPT"; 78 case PXENV_UNDI_GET_MCAST_ADDRESS: 79 return "PXENV_UNDI_GET_MCAST_ADDRESS"; 80 case PXENV_UNDI_GET_NIC_TYPE: 81 return "PXENV_UNDI_GET_NIC_TYPE"; 82 case PXENV_UNDI_GET_IFACE_INFO: 83 return "PXENV_UNDI_GET_IFACE_INFO"; 84 /* 85 * Duplicate case value; this is a bug in the PXE specification. 86 * 87 * case PXENV_UNDI_GET_STATE: 88 * return "PXENV_UNDI_GET_STATE"; 89 */ 90 case PXENV_UNDI_ISR: 91 return "PXENV_UNDI_ISR"; 92 case PXENV_GET_CACHED_INFO: 93 return "PXENV_GET_CACHED_INFO"; 94 default: 95 return "UNKNOWN API CALL"; 96 } 97 } 98 99 /** 100 * PXE parent parameter block 101 * 102 * Used as the paramter block for all parent PXE API calls. Resides in base 103 * memory. 104 */ 105 static union u_PXENV_ANY __bss16 ( pxeparent_params ); 106 #define pxeparent_params __use_data16 ( pxeparent_params ) 107 108 /** PXE parent entry point 109 * 110 * Used as the indirection vector for all parent PXE API calls. Resides in 111 * base memory. 112 */ 113 SEGOFF16_t __bss16 ( pxeparent_entry_point ); 114 #define pxeparent_entry_point __use_data16 ( pxeparent_entry_point ) 115 116 /** 117 * Issue parent PXE API call 118 * 119 * @v entry Parent PXE stack entry point 120 * @v function API call number 121 * @v params PXE parameter block 122 * @v params_len Length of PXE parameter block 123 * @ret rc Return status code 124 */ 125 int pxeparent_call ( SEGOFF16_t entry, unsigned int function, 126 void *params, size_t params_len ) { 127 PXENV_EXIT_t exit; 128 int discard_b, discard_D; 129 int rc; 130 131 /* Copy parameter block and entry point */ 132 assert ( params_len <= sizeof ( pxeparent_params ) ); 133 memcpy ( &pxeparent_params, params, params_len ); 134 memcpy ( &pxeparent_entry_point, &entry, sizeof ( entry ) ); 135 136 /* Call real-mode entry point. This calling convention will 137 * work with both the !PXE and the PXENV+ entry points. 138 */ 139 __asm__ __volatile__ ( REAL_CODE ( "pushw %%es\n\t" 140 "pushw %%di\n\t" 141 "pushw %%bx\n\t" 142 "lcall *pxeparent_entry_point\n\t" 143 "addw $6, %%sp\n\t" ) 144 : "=a" ( exit ), "=b" ( discard_b ), 145 "=D" ( discard_D ) 146 : "b" ( function ), 147 "D" ( __from_data16 ( &pxeparent_params ) ) 148 : "ecx", "edx", "esi", "ebp" ); 149 150 /* PXE API calls may rudely change the status of A20 and not 151 * bother to restore it afterwards. Intel is known to be 152 * guilty of this. 153 * 154 * Note that we will return to this point even if A20 gets 155 * screwed up by the parent PXE stack, because Etherboot always 156 * resides in an even megabyte of RAM. 157 */ 158 gateA20_set(); 159 160 /* Determine return status code based on PXENV_EXIT and 161 * PXENV_STATUS 162 */ 163 if ( exit == PXENV_EXIT_SUCCESS ) { 164 rc = 0; 165 } else { 166 rc = -pxeparent_params.Status; 167 /* Paranoia; don't return success for the combination 168 * of PXENV_EXIT_FAILURE but PXENV_STATUS_SUCCESS 169 */ 170 if ( rc == 0 ) 171 rc = -EIO; 172 } 173 174 /* If anything goes wrong, print as much debug information as 175 * it's possible to give. 176 */ 177 if ( rc != 0 ) { 178 SEGOFF16_t rm_params = { 179 .segment = rm_ds, 180 .offset = __from_data16 ( &pxeparent_params ), 181 }; 182 183 DBG ( "PXEPARENT %s failed: %s\n", 184 pxeparent_function_name ( function ), strerror ( rc ) ); 185 DBG ( "PXEPARENT parameters at %04x:%04x length " 186 "%#02zx, entry point at %04x:%04x\n", 187 rm_params.segment, rm_params.offset, params_len, 188 pxeparent_entry_point.segment, 189 pxeparent_entry_point.offset ); 190 DBG ( "PXEPARENT parameters provided:\n" ); 191 DBG_HDA ( rm_params, params, params_len ); 192 DBG ( "PXEPARENT parameters returned:\n" ); 193 DBG_HDA ( rm_params, &pxeparent_params, params_len ); 194 } 195 196 /* Copy parameter block back */ 197 memcpy ( params, &pxeparent_params, params_len ); 198 199 return rc; 200 } 201 202