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
24FILE_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 */
49#define pxe_int_1a_vector __use_text16 ( pxe_int_1a_vector )
50
51/** INT 1A handler */
52extern void pxe_int_1a ( void );
53
54/** INT 1A hooked flag */
55static int int_1a_hooked = 0;
56
57/** Real-mode code segment size */
58extern size_t ABS_SYMBOL ( _text16_memsz );
59#define _text16_memsz ABS_VALUE ( _text16_memsz )
60
61/** Real-mode data segment size */
62extern size_t ABS_SYMBOL (_data16_memsz );
63#define _data16_memsz ABS_VALUE ( _data16_memsz )
64
65/** PXENV_UNDI_TRANSMIT API call profiler */
66static struct profiler pxe_api_tx_profiler __profiler =
67 { .name = "pxeapi.tx" };
68
69/** PXENV_UNDI_ISR API call profiler */
70static 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 */
78static struct profiler pxe_api_unknown_profiler __profiler =
79 { .name = "pxeapi.unknown" };
80
81/** Miscellaneous PXE API call profiler */
82static 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 */
96
97/** Unknown PXE API call list */
98struct 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 */
123static struct profiler * pxe_api_profiler ( unsigned int opcode ) {
124
125 /* Determine applicable profiler */
126 switch ( opcode ) {
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 */
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 */
182int 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 */
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 */
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 */
218static 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 */
232static 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 */
259struct 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 */
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 */
300int pxe_deactivate ( void ) {
301 int rc;
302
303 /* Clear PXE network device */
305
306 /* Ensure INT 1A is unhooked, if possible */
307 if ( int_1a_hooked ) {
308 if ( ( rc = unhook_bios_interrupt ( 0x1a,
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 */
330int 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 */
374static 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 */
383
384 return 0;
385}
386
387/** PXE BIOS notification driver */
388struct net_driver pxe_driver __net_driver = {
389 .name = "PXE",
390 .probe = pxe_notify,
391};
392
394REQUIRE_OBJECT ( pxe_preboot );
395REQUIRE_OBJECT ( pxe_undi );
398REQUIRE_OBJECT ( pxe_file );
#define NULL
NULL pointer (VOID *)
Definition Base.h:322
struct arbelprm_rc_send_wqe rc
Definition arbel.h:3
#define __asmcall
Declare a function with standard calling conventions.
Definition compiler.h:15
__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))
unsigned short uint16_t
Definition stdint.h:11
unsigned long intptr_t
Definition stdint.h:21
unsigned int uint32_t
Definition stdint.h:12
unsigned char uint8_t
Definition stdint.h:10
void * discard_D
Definition bigint.h:32
long discard_c
Definition bigint.h:33
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 void devices_get(void)
Prevent devices from being removed on shutdown.
Definition device.h:165
static void devices_put(void)
Allow devices to be removed on shutdown.
Definition device.h:173
uint8_t data[48]
Additional event data.
Definition ena.h:11
uint8_t status
Status.
Definition ena.h:5
uint8_t opcode
Opcode.
Definition ena.h:5
static struct net_device * netdev
Definition gdbudp.c:53
#define ABS_SYMBOL(name)
Declare an absolute symbol (e.g.
Definition compiler.h:655
#define __unused
Declare a variable or data structure as unused.
Definition compiler.h:573
#define DBGC(...)
Definition compiler.h:505
#define INIT_NORMAL
Normal initialisation.
Definition init.h:32
uint16_t size
Buffer size.
Definition dwmac.h:3
#define FILE_LICENCE(_licence)
Declare a particular licence as applying to a file.
Definition compiler.h:896
#define REQUIRE_OBJECT(object)
Require an object.
Definition compiler.h:202
#define PXENV_EXIT_FAILURE
An error occurred.
Definition pxe_types.h:46
UINT16_t PXENV_EXIT_t
A PXE exit code.
Definition pxe_types.h:44
#define PXENV_STATUS_UNSUPPORTED
Definition pxe_error.h:22
#define PXENV_STATUS_FAILURE
Definition pxe_error.h:20
#define PXENV_UNDI_ISR
PXE API function code for pxenv_undi_isr()
Definition pxe_api.h:1450
#define PXENV_UNDI_TRANSMIT
PXE API function code for pxenv_undi_transmit()
Definition pxe_api.h:1009
#define REQUIRING_SYMBOL(symbol)
Specify the file's requiring symbol.
Definition compiler.h:140
#define _text16_memsz
Definition hidemem.c:85
#define _data16_memsz
Definition hidemem.c:87
uint8_t bytes[64]
Definition ib_mad.h:5
Profiling.
#define __profiler
Declare a profiler.
Definition profile.h:61
static void profile_stop(struct profiler *profiler)
Stop profiling.
Definition profile.h:174
static void profile_start(struct profiler *profiler)
Start profiling.
Definition profile.h:161
#define __init_fn(init_order)
Declare an initialisation functon.
Definition init.h:24
Access to external ("user") memory.
#define rm_ds
Definition libkir.h:39
#define __from_text16(pointer)
Definition libkir.h:23
#define REAL_CODE(asm_code_str)
Definition libkir.h:226
#define rm_cs
Definition libkir.h:38
#define __text16(variable)
Definition libkir.h:18
uint8_t jmp
"jmp" instruction
Definition librm.h:7
Network device management.
#define __net_driver
Declare a network driver.
Definition netdevice.h:507
__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")
#define PXE_API_CALL(_opcode, _entry, _params_type)
Define a PXE API call.
Definition pxe.h:106
#define __pxe_api_call
Declare a PXE API call.
Definition pxe.h:96
#define PXE_API_CALLS
PXE API call table.
Definition pxe.h:93
#define PXENV_UNKNOWN
PXE API invalid function code.
Definition pxe.h:13
static uint8_t pxe_checksum(void *data, size_t size)
Calculate byte checksum as used by PXE.
Definition pxe_call.c:218
static struct pxe_api_call * find_pxe_api_call(uint16_t opcode)
Locate PXE API call.
Definition pxe_call.c:107
static struct profiler * pxe_api_profiler(unsigned int opcode)
Determine applicable profiler (for debugging)
Definition pxe_call.c:123
void pxe_int_1a(void)
INT 1A handler.
static void pxe_init_structures(void)
Initialise !PXE and PXENV+ structures.
Definition pxe_call.c:232
#define EPXENBP(status)
Definition pxe_call.c:45
__asmcall void pxe_api_call(struct i386_all_regs *ix86)
Dispatch PXE API call.
Definition pxe_call.c:145
rmjmp_buf pxe_restart_nbp
Jump buffer for PXENV_RESTART_TFTP.
Definition pxe_call.c:323
static int int_1a_hooked
INT 1A hooked flag.
Definition pxe_call.c:55
void pxe_activate(struct net_device *netdev)
Activate PXE stack.
Definition pxe_call.c:269
int pxe_start_nbp(void)
Start PXE NBP at 0000:7c00.
Definition pxe_call.c:330
int pxe_deactivate(void)
Deactivate PXE stack.
Definition pxe_call.c:300
int pxe_api_call_weak(struct i386_all_regs *ix86)
Dispatch weak PXE API call with PXE stack available.
Definition pxe_call.c:182
#define pxe_int_1a_vector
Definition pxe_call.c:49
__asmcall void pxe_loader_call(struct i386_all_regs *ix86)
Dispatch PXE loader call.
Definition pxe_call.c:193
static int pxe_notify(struct net_device *netdev, void *priv __unused)
Notify BIOS of existence of network device.
Definition pxe_call.c:374
static PXENV_EXIT_t pxenv_unknown(struct s_PXENV_UNKNOWN *pxenv_unknown)
Handle an unknown PXE API call.
Definition pxe_call.c:92
PXE API entry point.
#define ppxe
Definition pxe_call.h:28
#define pxenv
Definition pxe_call.h:32
static struct pxe_tftp_connection pxe_tftp
The PXE TFTP connection.
Definition pxe_tftp.c:156
static struct pxe_udp_connection pxe_udp
The PXE UDP connection.
Definition pxe_udp.c:125
void pxe_set_netdev(struct net_device *netdev)
Set network device as current PXE network device.
Definition pxe_undi.c:69
struct net_device * pxe_netdev
Definition pxe_undi.c:59
static __always_inline void * real_to_virt(unsigned int segment, unsigned int offset)
Convert segment:offset address to virtual address.
Definition realmode.h:77
i386 registers.
#define rmsetjmp(_env)
Definition rmsetjmp.h:17
char * strerror(int errno)
Retrieve string representation of error number.
Definition strerror.c:79
A full register dump.
Definition registers.h:174
struct i386_seg_regs segs
Definition registers.h:175
struct i386_regs regs
Definition registers.h:176
uint16_t ax
Definition registers.h:108
uint16_t bx
Definition registers.h:84
uint16_t di
Definition registers.h:64
uint16_t ds
Definition registers.h:141
uint16_t es
Definition registers.h:142
An initialisation function.
Definition init.h:15
A network device.
Definition netdevice.h:353
A network upper-layer driver.
Definition netdevice.h:477
A data structure for storing profiling information.
Definition profile.h:27
A PXE API call.
Definition pxe.h:81
uint16_t opcode
Opcode.
Definition pxe.h:89
PXENV_EXIT_t(* entry)(union u_PXENV_ANY *params)
Entry point.
Definition pxe.h:87
A real-mode-extended jump buffer.
Definition rmsetjmp.h:10
Parameter block for pxenv_unknown()
Definition pxe.h:16
Parameter block for undi_loader()
Definition pxe_api.h:1729
PXENV_STATUS_t Status
PXE status code.
Definition pxe_api.h:1731
#define for_each_table_entry(pointer, table)
Iterate through all entries within a linker table.
Definition tables.h:386
static struct tlan_private * priv
Definition tlan.c:225
#define undi_loader
Definition undiload.c:53
PXENV_STATUS_t Status
Definition pxe.h:29