iPXE
pxe_call.c
Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
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.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 #include <ipxe/uaccess.h>
27 #include <ipxe/init.h>
28 #include <ipxe/profile.h>
29 #include <ipxe/netdevice.h>
30 #include <rmsetjmp.h>
31 #include <registers.h>
32 #include <biosint.h>
33 #include <pxe.h>
34 #include <pxe_call.h>
35 
36 /** @file
37  *
38  * PXE API entry point
39  */
40 
41 /* Disambiguate the various error causes */
42 #define EINFO_EPXENBP \
43  __einfo_uniqify ( EINFO_EPLATFORM, 0x01, \
44  "External PXE NBP error" )
45 #define EPXENBP( status ) EPLATFORM ( EINFO_EPXENBP, status )
46 
47 /** Vector for chaining INT 1A */
48 extern struct segoff __text16 ( pxe_int_1a_vector );
49 #define pxe_int_1a_vector __use_text16 ( pxe_int_1a_vector )
50 
51 /** INT 1A handler */
52 extern void pxe_int_1a ( void );
53 
54 /** INT 1A hooked flag */
55 static int int_1a_hooked = 0;
56 
57 /** Real-mode code segment size */
58 extern size_t ABS_SYMBOL ( _text16_memsz );
59 #define _text16_memsz ABS_VALUE ( _text16_memsz )
60 
61 /** Real-mode data segment size */
62 extern size_t ABS_SYMBOL (_data16_memsz );
63 #define _data16_memsz ABS_VALUE ( _data16_memsz )
64 
65 /** PXENV_UNDI_TRANSMIT API call profiler */
66 static struct profiler pxe_api_tx_profiler __profiler =
67  { .name = "pxeapi.tx" };
68 
69 /** PXENV_UNDI_ISR API call profiler */
70 static struct profiler pxe_api_isr_profiler __profiler =
71  { .name = "pxeapi.isr" };
72 
73 /** PXE unknown API call profiler
74  *
75  * This profiler can be used to measure the overhead of a dummy PXE
76  * API call.
77  */
78 static struct profiler pxe_api_unknown_profiler __profiler =
79  { .name = "pxeapi.unknown" };
80 
81 /** Miscellaneous PXE API call profiler */
82 static struct profiler pxe_api_misc_profiler __profiler =
83  { .name = "pxeapi.misc" };
84 
85 /**
86  * Handle an unknown PXE API call
87  *
88  * @v pxenv_unknown Pointer to a struct s_PXENV_UNKNOWN
89  * @ret #PXENV_EXIT_FAILURE Always
90  * @err #PXENV_STATUS_UNSUPPORTED Always
91  */
94  return PXENV_EXIT_FAILURE;
95 }
96 
97 /** Unknown PXE API call list */
98 struct pxe_api_call pxenv_unknown_api __pxe_api_call =
100 
101 /**
102  * Locate PXE API call
103  *
104  * @v opcode Opcode
105  * @ret call PXE API call, or NULL
106  */
108  struct pxe_api_call *call;
109 
111  if ( call->opcode == opcode )
112  return call;
113  }
114  return NULL;
115 }
116 
117 /**
118  * Determine applicable profiler (for debugging)
119  *
120  * @v opcode PXE opcode
121  * @ret profiler Profiler
122  */
123 static struct profiler * pxe_api_profiler ( unsigned int opcode ) {
124 
125  /* Determine applicable profiler */
126  switch ( opcode ) {
127  case PXENV_UNDI_TRANSMIT:
128  return &pxe_api_tx_profiler;
129  case PXENV_UNDI_ISR:
130  return &pxe_api_isr_profiler;
131  case PXENV_UNKNOWN:
132  return &pxe_api_unknown_profiler;
133  default:
134  return &pxe_api_misc_profiler;
135  }
136 }
137 
138 /**
139  * Dispatch PXE API call
140  *
141  * @v bx PXE opcode
142  * @v es:di Address of PXE parameter block
143  * @ret ax PXE exit code
144  */
145 __asmcall void pxe_api_call ( struct i386_all_regs *ix86 ) {
146  uint16_t opcode = ix86->regs.bx;
148  union u_PXENV_ANY *params =
149  real_to_virt ( ix86->segs.es, ix86->regs.di );
150  struct pxe_api_call *call;
151  PXENV_EXIT_t ret;
152 
153  /* Start profiling */
155 
156  /* Locate API call */
157  call = find_pxe_api_call ( opcode );
158  if ( ! call ) {
159  DBGC ( &pxe_netdev, "PXENV_UNKNOWN_%04x\n", opcode );
160  call = &pxenv_unknown_api;
161  }
162 
163  /* Set default status in case child routine fails to do so */
164  params->Status = PXENV_STATUS_FAILURE;
165 
166  /* Hand off to relevant API routine */
167  ret = call->entry ( params );
168 
169  /* Return exit code in %ax */
170  ix86->regs.ax = ret;
171 
172  /* Stop profiling, if applicable */
174 }
175 
176 /**
177  * Dispatch weak PXE API call with PXE stack available
178  *
179  * @v ix86 Registers for PXE call
180  * @ret present Zero (PXE stack present)
181  */
182 int pxe_api_call_weak ( struct i386_all_regs *ix86 ) {
183  pxe_api_call ( ix86 );
184  return 0;
185 }
186 
187 /**
188  * Dispatch PXE loader call
189  *
190  * @v es:di Address of PXE parameter block
191  * @ret ax PXE exit code
192  */
193 __asmcall void pxe_loader_call ( struct i386_all_regs *ix86 ) {
194  struct s_UNDI_LOADER *params =
195  real_to_virt ( ix86->segs.es, ix86->regs.di );
196  PXENV_EXIT_t ret;
197 
198  /* Fill in ROM segment address */
199  ppxe.UNDIROMID.segment = ix86->segs.ds;
200 
201  /* Set default status in case child routine fails to do so */
202  params->Status = PXENV_STATUS_FAILURE;
203 
204  /* Call UNDI loader */
205  ret = undi_loader ( params );
206 
207  /* Return exit code in %ax */
208  ix86->regs.ax = ret;
209 }
210 
211 /**
212  * Calculate byte checksum as used by PXE
213  *
214  * @v data Data
215  * @v size Length of data
216  * @ret sum Checksum
217  */
218 static uint8_t pxe_checksum ( void *data, size_t size ) {
219  uint8_t *bytes = data;
220  uint8_t sum = 0;
221 
222  while ( size-- ) {
223  sum += *bytes++;
224  }
225  return sum;
226 }
227 
228 /**
229  * Initialise !PXE and PXENV+ structures
230  *
231  */
232 static void pxe_init_structures ( void ) {
233  uint32_t rm_cs_phys = ( rm_cs << 4 );
234  uint32_t rm_ds_phys = ( rm_ds << 4 );
235 
236  /* Fill in missing segment fields */
237  ppxe.EntryPointSP.segment = rm_cs;
238  ppxe.EntryPointESP.segment = rm_cs;
239  ppxe.Stack.segment_address = rm_ds;
240  ppxe.Stack.Physical_address = rm_ds_phys;
241  ppxe.UNDIData.segment_address = rm_ds;
242  ppxe.UNDIData.Physical_address = rm_ds_phys;
243  ppxe.UNDICode.segment_address = rm_cs;
244  ppxe.UNDICode.Physical_address = rm_cs_phys;
245  ppxe.UNDICodeWrite.segment_address = rm_cs;
246  ppxe.UNDICodeWrite.Physical_address = rm_cs_phys;
247  pxenv.RMEntry.segment = rm_cs;
248  pxenv.StackSeg = rm_ds;
249  pxenv.UNDIDataSeg = rm_ds;
250  pxenv.UNDICodeSeg = rm_cs;
251  pxenv.PXEPtr.segment = rm_cs;
252 
253  /* Update checksums */
254  ppxe.StructCksum -= pxe_checksum ( &ppxe, sizeof ( ppxe ) );
255  pxenv.Checksum -= pxe_checksum ( &pxenv, sizeof ( pxenv ) );
256 }
257 
258 /** PXE structure initialiser */
259 struct init_fn pxe_init_fn __init_fn ( INIT_NORMAL ) = {
260  .name = "pxe",
261  .initialise = pxe_init_structures,
262 };
263 
264 /**
265  * Activate PXE stack
266  *
267  * @v netdev Net device to use as PXE net device
268  */
269 void pxe_activate ( struct net_device *netdev ) {
270  uint32_t discard_a;
271  uint32_t discard_b;
272  uint32_t discard_d;
273 
274  /* Ensure INT 1A is hooked */
275  if ( ! int_1a_hooked ) {
278  devices_get();
279  int_1a_hooked = 1;
280  }
281 
282  /* Set PXE network device */
284 
285  /* Notify BIOS of installation */
286  __asm__ __volatile__ ( REAL_CODE ( "pushw %%cs\n\t"
287  "popw %%es\n\t"
288  "int $0x1a\n\t" )
289  : "=a" ( discard_a ), "=b" ( discard_b ),
290  "=d" ( discard_d )
291  : "0" ( 0x564e ),
292  "1" ( __from_text16 ( &pxenv ) ) );
293 }
294 
295 /**
296  * Deactivate PXE stack
297  *
298  * @ret rc Return status code
299  */
300 int pxe_deactivate ( void ) {
301  int rc;
302 
303  /* Clear PXE network device */
304  pxe_set_netdev ( NULL );
305 
306  /* Ensure INT 1A is unhooked, if possible */
307  if ( int_1a_hooked ) {
308  if ( ( rc = unhook_bios_interrupt ( 0x1a,
309  ( intptr_t ) pxe_int_1a,
310  &pxe_int_1a_vector ))!= 0){
311  DBGC ( &pxe_netdev, "PXE could not unhook INT 1A: %s\n",
312  strerror ( rc ) );
313  return rc;
314  }
315  devices_put();
316  int_1a_hooked = 0;
317  }
318 
319  return 0;
320 }
321 
322 /** Jump buffer for PXENV_RESTART_TFTP */
324 
325 /**
326  * Start PXE NBP at 0000:7c00
327  *
328  * @ret rc Return status code
329  */
330 int pxe_start_nbp ( void ) {
331  int jmp;
332  int discard_b, discard_c, discard_d, discard_D;
334 
335  DBGC ( &pxe_netdev, "PXE NBP starting with netdev %s, code %04x:%04zx, "
336  "data %04x:%04zx\n", ( pxe_netdev ? pxe_netdev->name : "<none>"),
338 
339  /* Allow restarting NBP via PXENV_RESTART_TFTP */
341  if ( jmp )
342  DBGC ( &pxe_netdev, "PXE NBP restarting (%x)\n", jmp );
343 
344  /* Far call to PXE NBP */
345  __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
346  "movw %%cx, %%es\n\t"
347  "pushw %%es\n\t"
348  "pushw %%di\n\t"
349  "sti\n\t"
350  "lcall $0, $0x7c00\n\t"
351  "popl %%ebp\n\t" /* discard */
352  "popl %%ebp\n\t" /* gcc bug */ )
353  : "=a" ( status ), "=b" ( discard_b ),
354  "=c" ( discard_c ), "=d" ( discard_d ),
355  "=D" ( discard_D )
356  : "a" ( 0 ), "b" ( __from_text16 ( &pxenv ) ),
357  "c" ( rm_cs ),
358  "d" ( virt_to_phys ( &pxenv ) ),
359  "D" ( __from_text16 ( &ppxe ) )
360  : "esi", "memory" );
361  if ( status )
362  return -EPXENBP ( status );
363 
364  return 0;
365 }
366 
367 /**
368  * Notify BIOS of existence of network device
369  *
370  * @v netdev Network device
371  * @v priv Private data
372  * @ret rc Return status code
373  */
374 static int pxe_notify ( struct net_device *netdev, void *priv __unused ) {
375 
376  /* Do nothing if we already have a network device */
377  if ( pxe_netdev )
378  return 0;
379 
380  /* Activate (and deactivate) PXE stack to notify BIOS */
381  pxe_activate ( netdev );
382  pxe_deactivate();
383 
384  return 0;
385 }
386 
387 /** PXE BIOS notification driver */
388 struct net_driver pxe_driver __net_driver = {
389  .name = "PXE",
390  .probe = pxe_notify,
391 };
392 
394 REQUIRE_OBJECT ( pxe_preboot );
395 REQUIRE_OBJECT ( pxe_undi );
398 REQUIRE_OBJECT ( pxe_file );
void pxe_activate(struct net_device *netdev)
Activate PXE stack.
Definition: pxe_call.c:269
static PXENV_EXIT_t pxenv_unknown(struct s_PXENV_UNKNOWN *pxenv_unknown)
Handle an unknown PXE API call.
Definition: pxe_call.c:92
struct init_fn pxe_init_fn __init_fn(INIT_NORMAL)
PXE structure initialiser.
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
unsigned short uint16_t
Definition: stdint.h:11
uint8_t jmp
"jmp" instruction
Definition: librm.h:142
struct i386_seg_regs segs
Definition: registers.h:175
static void devices_put(void)
Allow devices to be removed on shutdown.
Definition: device.h:172
#define PXENV_EXIT_FAILURE
An error occurred.
Definition: pxe_types.h:46
size_t ABS_SYMBOL(_text16_memsz)
Real-mode code segment size.
PXENV_STATUS_t Status
Definition: pxe.h:29
uint8_t opcode
Opcode.
Definition: ena.h:16
uint16_t es
Definition: registers.h:142
#define ppxe
Definition: pxe_call.h:28
#define __from_text16(pointer)
Definition: libkir.h:23
void pxe_int_1a(void)
INT 1A handler.
struct net_driver pxe_driver __net_driver
PXE BIOS notification driver.
Definition: pxe_call.c:388
static struct profiler pxe_api_tx_profiler __profiler
PXENV_UNDI_TRANSMIT API call profiler.
Definition: pxe_call.c:66
uint16_t size
Buffer size.
Definition: dwmac.h:14
#define DBGC(...)
Definition: compiler.h:505
i386 registers.
PXE API entry point.
REQUIRING_SYMBOL(pxe_api_call)
A data structure for storing profiling information.
Definition: profile.h:26
#define rm_ds
Definition: libkir.h:39
unsigned long intptr_t
Definition: stdint.h:21
static void profile_stop(struct profiler *profiler)
Stop profiling.
Definition: profile.h:173
A full register dump.
Definition: registers.h:174
struct segoff __text16(pxe_int_1a_vector)
Vector for chaining INT 1A.
A network upper-layer driver.
Definition: netdevice.h:476
#define PXENV_UNKNOWN
PXE API invalid function code.
Definition: pxe.h:13
#define _data16_memsz
Definition: pxe_call.c:63
static __always_inline void * real_to_virt(unsigned int segment, unsigned int offset)
Convert segment:offset address to virtual address.
Definition: realmode.h:77
#define PXE_API_CALL(_opcode, _entry, _params_type)
Define a PXE API call.
Definition: pxe.h:106
void hook_bios_interrupt(unsigned int interrupt, unsigned int handler, struct segoff *chain_vector)
Hook INT vector.
Definition: biosint.c:25
int unhook_bios_interrupt(unsigned int interrupt, unsigned int handler, struct segoff *chain_vector)
Unhook INT vector.
Definition: biosint.c:70
static struct profiler * pxe_api_profiler(unsigned int opcode)
Determine applicable profiler (for debugging)
Definition: pxe_call.c:123
#define INIT_NORMAL
Normal initialisation.
Definition: init.h:31
const char * name
Name.
Definition: netdevice.h:478
UINT16_t PXENV_EXIT_t
A PXE exit code.
Definition: pxe_types.h:44
const char * name
Name.
Definition: profile.h:28
#define __asmcall
Declare a function with standard calling conventions.
Definition: compiler.h:15
An initialisation function.
Definition: init.h:14
static int int_1a_hooked
INT 1A hooked flag.
Definition: pxe_call.c:55
int pxe_deactivate(void)
Deactivate PXE stack.
Definition: pxe_call.c:300
Access to external ("user") memory.
struct i386_regs regs
Definition: registers.h:176
#define __unused
Declare a variable or data structure as unused.
Definition: compiler.h:573
static void devices_get(void)
Prevent devices from being removed on shutdown.
Definition: device.h:164
#define PXE_API_CALLS
PXE API call table.
Definition: pxe.h:93
const char * name
Definition: init.h:15
#define pxe_int_1a_vector
Definition: pxe_call.c:49
static struct net_device * netdev
Definition: gdbudp.c:52
void pxe_set_netdev(struct net_device *netdev)
Set network device as current PXE network device.
Definition: pxe_undi.c:69
static struct pxe_tftp_connection pxe_tftp
The PXE TFTP connection.
Definition: pxe_tftp.c:156
static void profile_start(struct profiler *profiler)
Start profiling.
Definition: profile.h:160
Profiling.
uint16_t bx
Definition: registers.h:84
#define PXENV_UNDI_TRANSMIT
PXE API function code for pxenv_undi_transmit()
Definition: pxe_api.h:1009
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:78
#define _text16_memsz
Definition: pxe_call.c:59
struct net_device * pxe_netdev
Definition: pxe_undi.c:59
int pxe_start_nbp(void)
Start PXE NBP at 0000:7c00.
Definition: pxe_call.c:330
#define for_each_table_entry(pointer, table)
Iterate through all entries within a linker table.
Definition: tables.h:385
A network device.
Definition: netdevice.h:352
__asm__ __volatile__("call *%9" :"=a"(result), "=c"(discard_ecx), "=d"(discard_edx) :"d"(0), "a"(code), "b"(0), "c"(in_phys), "D"(0), "S"(out_phys), "m"(hypercall))
uint16_t ds
Definition: registers.h:141
unsigned char uint8_t
Definition: stdint.h:10
#define PXENV_STATUS_UNSUPPORTED
Definition: pxe_error.h:22
void * discard_D
Definition: bigint.h:31
uint16_t opcode
Opcode.
Definition: pxe.h:89
unsigned int uint32_t
Definition: stdint.h:12
REQUIRE_OBJECT(pxe_preboot)
uint8_t status
Status.
Definition: ena.h:16
__asmcall void pxe_loader_call(struct i386_all_regs *ix86)
Dispatch PXE loader call.
Definition: pxe_call.c:193
#define undi_loader
Definition: undiload.c:53
Network device management.
static uint8_t pxe_checksum(void *data, size_t size)
Calculate byte checksum as used by PXE.
Definition: pxe_call.c:218
__asm__(".section \".rodata\", \"a\", " PROGBITS "\n\t" "\nprivate_key_data:\n\t" ".size private_key_data, ( . - private_key_data )\n\t" ".equ private_key_len, ( . - private_key_data )\n\t" ".previous\n\t")
static void pxe_init_structures(void)
Initialise !PXE and PXENV+ structures.
Definition: pxe_call.c:232
char name[NETDEV_NAME_LEN]
Name of this network device.
Definition: netdevice.h:362
int pxe_api_call_weak(struct i386_all_regs *ix86)
Dispatch weak PXE API call with PXE stack available.
Definition: pxe_call.c:182
A PXE API call.
Definition: pxe.h:81
uint16_t di
Definition: registers.h:64
static struct tlan_private * priv
Definition: tlan.c:225
uint16_t ax
Definition: registers.h:108
Parameter block for undi_loader()
Definition: pxe_api.h:1729
#define PXENV_UNDI_ISR
PXE API function code for pxenv_undi_isr()
Definition: pxe_api.h:1450
static struct pxe_api_call * find_pxe_api_call(uint16_t opcode)
Locate PXE API call.
Definition: pxe_call.c:107
#define rmsetjmp(_env)
Definition: rmsetjmp.h:17
PXENV_STATUS_t Status
PXE status code.
Definition: pxe_api.h:1731
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
uint8_t data[48]
Additional event data.
Definition: ena.h:22
#define rm_cs
Definition: libkir.h:38
static struct pxe_udp_connection pxe_udp
The PXE UDP connection.
Definition: pxe_udp.c:125
long discard_c
Definition: bigint.h:32
PXENV_EXIT_t(* entry)(union u_PXENV_ANY *params)
Entry point.
Definition: pxe.h:87
Parameter block for pxenv_unknown()
Definition: pxe.h:16
struct pxe_api_call pxenv_unknown_api __pxe_api_call
Unknown PXE API call list.
Definition: pxe_call.c:98
#define pxenv
Definition: pxe_call.h:32
#define EPXENBP(status)
Definition: pxe_call.c:45
static int pxe_notify(struct net_device *netdev, void *priv __unused)
Notify BIOS of existence of network device.
Definition: pxe_call.c:374
uint8_t bytes[64]
Definition: ib_mad.h:16
#define REAL_CODE(asm_code_str)
Definition: libkir.h:226
#define NULL
NULL pointer (VOID *)
Definition: Base.h:321
rmjmp_buf pxe_restart_nbp
Jump buffer for PXENV_RESTART_TFTP.
Definition: pxe_call.c:323
A real-mode-extended jump buffer.
Definition: rmsetjmp.h:10
#define PXENV_STATUS_FAILURE
Definition: pxe_error.h:20
__asmcall void pxe_api_call(struct i386_all_regs *ix86)
Dispatch PXE API call.
Definition: pxe_call.c:145