iPXE
pxe_preboot.c
Go to the documentation of this file.
1 /** @file
2  *
3  * PXE Preboot API
4  *
5  */
6 
7 /* PXE API interface for Etherboot.
8  *
9  * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License as
13  * published by the Free Software Foundation; either version 2 of the
14  * License, or any later version.
15  *
16  * This program is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24  * 02110-1301, USA.
25  *
26  * You can also choose to distribute this program under the terms of
27  * the Unmodified Binary Distribution Licence (as given in the file
28  * COPYING.UBDL), provided that you have satisfied its requirements.
29  */
30 
31 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
32 
33 #include <stdint.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <ipxe/dhcp.h>
37 #include <ipxe/fakedhcp.h>
38 #include <ipxe/device.h>
39 #include <ipxe/netdevice.h>
40 #include <ipxe/isapnp.h>
41 #include <ipxe/init.h>
42 #include <ipxe/if_ether.h>
43 #include <basemem_packet.h>
44 #include <biosint.h>
45 #include <rmsetjmp.h>
46 #include "pxe.h"
47 #include "pxe_call.h"
48 
49 /* Avoid dragging in isapnp.o unnecessarily */
51 
52 /** Zero-based versions of PXENV_GET_CACHED_INFO::PacketType */
58 };
59 
60 /** A cached DHCP packet */
62  struct dhcphdr dhcphdr;
63  /* This buffer must be *exactly* the size of a BOOTPLAYER_t
64  * structure, otherwise WinPE will die horribly. It takes the
65  * size of *our* buffer and feeds it in to us as the size of
66  * one of *its* buffers. If our buffer is larger than it
67  * expects, we therefore end up overwriting part of its data
68  * segment, since it tells us to do so. (D'oh!)
69  *
70  * Note that a BOOTPLAYER_t is not necessarily large enough to
71  * hold a DHCP packet; this is a flaw in the PXE spec.
72  */
74 } __attribute__ (( packed ));
75 
76 /** A PXE DHCP packet creator */
78  /** Create DHCP packet
79  *
80  * @v netdev Network device
81  * @v data Buffer for DHCP packet
82  * @v max_len Size of DHCP packet buffer
83  * @ret rc Return status code
84  */
85  int ( * create ) ( struct net_device *netdev, void *data,
86  size_t max_len );
87 };
88 
89 /** PXE DHCP packet creators */
94 };
95 
96 /**
97  * Name PXENV_GET_CACHED_INFO packet type
98  *
99  * @v packet_type Packet type
100  * @ret name Name of packet type
101  */
102 static inline __attribute__ (( always_inline )) const char *
103 pxenv_get_cached_info_name ( int packet_type ) {
104  switch ( packet_type ) {
106  return "DHCPDISCOVER";
108  return "DHCPACK";
110  return "BINL";
111  default:
112  return "<INVALID>";
113  }
114 }
115 
116 /* The case in which the caller doesn't supply a buffer is really
117  * awkward to support given that we have multiple sources of options,
118  * and that we don't actually store the DHCP packets. (We may not
119  * even have performed DHCP; we may have obtained all configuration
120  * from non-volatile stored options or from the command line.)
121  *
122  * Some NBPs rely on the buffers we provide being persistent, so we
123  * can't just use the temporary packet buffer. 4.5kB of base memory
124  * always wasted just because some clients are too lazy to provide
125  * their own buffers...
126  */
128 #define cached_info __use_data16 ( cached_info )
129 
130 /**
131  * Construct cached DHCP packets
132  *
133  */
134 void pxe_fake_cached_info ( void ) {
135  struct pxe_dhcp_packet_creator *creator;
136  union pxe_cached_info *info;
137  unsigned int i;
138  int rc;
139 
140  /* Sanity check */
141  assert ( pxe_netdev != NULL );
142 
143  /* Erase any stale packets */
144  memset ( cached_info, 0, sizeof ( cached_info ) );
145 
146  /* Construct all DHCP packets */
147  for ( i = 0 ; i < ( sizeof ( pxe_dhcp_packet_creators ) /
148  sizeof ( pxe_dhcp_packet_creators[0] ) ) ; i++ ) {
149 
150  /* Construct DHCP packet */
151  creator = &pxe_dhcp_packet_creators[i];
152  info = &cached_info[i];
153  if ( ( rc = creator->create ( pxe_netdev, info,
154  sizeof ( *info ) ) ) != 0 ) {
155  DBGC ( &pxe_netdev, " failed to build packet: %s\n",
156  strerror ( rc ) );
157  /* Continue constructing remaining packets */
158  }
159  }
160 }
161 
162 /**
163  * UNLOAD BASE CODE STACK
164  *
165  * @v None -
166  * @ret ...
167  *
168  */
169 static PXENV_EXIT_t
170 pxenv_unload_stack ( struct s_PXENV_UNLOAD_STACK *unload_stack ) {
171  DBGC ( &pxe_netdev, "PXENV_UNLOAD_STACK\n" );
172 
173  unload_stack->Status = PXENV_STATUS_SUCCESS;
174  return PXENV_EXIT_SUCCESS;
175 }
176 
177 /* PXENV_GET_CACHED_INFO
178  *
179  * Status: working
180  */
181 static PXENV_EXIT_t
182 pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO *get_cached_info ) {
183  union pxe_cached_info *info;
184  unsigned int idx;
185  size_t len;
186  void *buffer;
187 
188  DBGC ( &pxe_netdev, "PXENV_GET_CACHED_INFO %s to %04x:%04x+%x",
189  pxenv_get_cached_info_name ( get_cached_info->PacketType ),
190  get_cached_info->Buffer.segment,
191  get_cached_info->Buffer.offset, get_cached_info->BufferSize );
192 
193  /* Sanity check */
194  idx = ( get_cached_info->PacketType - 1 );
195  if ( idx >= NUM_CACHED_INFOS ) {
196  DBGC ( &pxe_netdev, " bad PacketType %d\n",
197  get_cached_info->PacketType );
198  get_cached_info->Status = PXENV_STATUS_UNSUPPORTED;
199  return PXENV_EXIT_FAILURE;
200  }
201  info = &cached_info[idx];
202 
203  /* Copy packet (if applicable) */
204  len = get_cached_info->BufferSize;
205  if ( len == 0 ) {
206  /* Point client at our cached buffer.
207  *
208  * To add to the fun, Intel decided at some point in
209  * the evolution of the PXE specification to add the
210  * BufferLimit field, which we are meant to fill in
211  * with the length of our packet buffer, so that the
212  * caller can safely modify the boot server reply
213  * packet stored therein. However, this field was not
214  * present in earlier versions of the PXE spec, and
215  * there is at least one PXE NBP (Altiris) which
216  * allocates only exactly enough space for this
217  * earlier, shorter version of the structure. If we
218  * actually fill in the BufferLimit field, we
219  * therefore risk trashing random areas of the
220  * caller's memory. If we *don't* fill it in, then
221  * the caller is at liberty to assume that whatever
222  * random value happened to be in that location
223  * represents the length of the buffer we've just
224  * passed back to it.
225  *
226  * Since older PXE stacks won't fill this field in
227  * anyway, it's probably safe to assume that no
228  * callers actually rely on it, so we choose to not
229  * fill it in.
230  */
231  get_cached_info->Buffer.segment = rm_ds;
232  get_cached_info->Buffer.offset = __from_data16 ( info );
233  get_cached_info->BufferSize = sizeof ( *info );
234  DBGC ( &pxe_netdev, " using %04x:%04x+%04x['%x']",
235  get_cached_info->Buffer.segment,
236  get_cached_info->Buffer.offset,
237  get_cached_info->BufferSize,
238  get_cached_info->BufferLimit );
239  } else {
240  /* Copy packet to client buffer */
241  if ( len > sizeof ( *info ) )
242  len = sizeof ( *info );
243  if ( len < sizeof ( *info ) )
244  DBGC ( &pxe_netdev, " buffer may be too short" );
245  buffer = real_to_virt ( get_cached_info->Buffer.segment,
246  get_cached_info->Buffer.offset );
247  memcpy ( buffer, info, len );
248  get_cached_info->BufferSize = len;
249  }
250 
251  DBGC ( &pxe_netdev, "\n" );
252  get_cached_info->Status = PXENV_STATUS_SUCCESS;
253  return PXENV_EXIT_SUCCESS;
254 }
255 
256 /* PXENV_RESTART_TFTP
257  *
258  * Status: working
259  */
260 static PXENV_EXIT_t
261 pxenv_restart_tftp ( struct s_PXENV_TFTP_READ_FILE *restart_tftp ) {
262  PXENV_EXIT_t tftp_exit;
263 
264  DBGC ( &pxe_netdev, "PXENV_RESTART_TFTP\n" );
265 
266  /* Words cannot describe the complete mismatch between the PXE
267  * specification and any possible version of reality...
268  */
269  restart_tftp->Buffer = PXE_LOAD_PHYS; /* Fixed by spec, apparently */
270  restart_tftp->BufferSize = ( 0xa0000 - PXE_LOAD_PHYS ); /* Near enough */
271  tftp_exit = pxenv_tftp_read_file ( restart_tftp );
272  if ( tftp_exit != PXENV_EXIT_SUCCESS )
273  return tftp_exit;
274 
275  /* Restart NBP */
277 }
278 
279 /* PXENV_START_UNDI
280  *
281  * Status: working
282  */
283 static PXENV_EXIT_t pxenv_start_undi ( struct s_PXENV_START_UNDI *start_undi ) {
284  unsigned int bus_type;
285  unsigned int location;
286  struct net_device *netdev;
287 
288  DBGC ( &pxe_netdev, "PXENV_START_UNDI %04x:%04x:%04x\n",
289  start_undi->AX, start_undi->BX, start_undi->DX );
290 
291  /* Determine bus type and location. Use a heuristic to decide
292  * whether we are PCI or ISAPnP
293  */
294  if ( ( start_undi->DX >= ISAPNP_READ_PORT_MIN ) &&
295  ( start_undi->DX <= ISAPNP_READ_PORT_MAX ) &&
296  ( start_undi->BX >= ISAPNP_CSN_MIN ) &&
297  ( start_undi->BX <= ISAPNP_CSN_MAX ) ) {
298  bus_type = BUS_TYPE_ISAPNP;
299  location = start_undi->BX;
300  /* Record ISAPnP read port for use by isapnp.c */
301  isapnp_read_port = start_undi->DX;
302  } else {
303  bus_type = BUS_TYPE_PCI;
304  location = start_undi->AX;
305  }
306 
307  /* Probe for devices, etc. */
308  startup();
309 
310  /* Look for a matching net device */
311  netdev = find_netdev_by_location ( bus_type, location );
312  if ( ! netdev ) {
313  DBGC ( &pxe_netdev, "PXENV_START_UNDI could not find matching "
314  "net device\n" );
316  return PXENV_EXIT_FAILURE;
317  }
318  DBGC ( &pxe_netdev, "PXENV_START_UNDI found net device %s\n",
319  netdev->name );
320 
321  /* Activate PXE */
322  pxe_activate ( netdev );
323 
324  start_undi->Status = PXENV_STATUS_SUCCESS;
325  return PXENV_EXIT_SUCCESS;
326 }
327 
328 /* PXENV_STOP_UNDI
329  *
330  * Status: working
331  */
332 static PXENV_EXIT_t pxenv_stop_undi ( struct s_PXENV_STOP_UNDI *stop_undi ) {
333  DBGC ( &pxe_netdev, "PXENV_STOP_UNDI\n" );
334 
335  /* Deactivate PXE */
336  pxe_deactivate();
337 
338  /* Prepare for unload */
339  shutdown_boot();
340 
341  /* Check to see if we still have any hooked interrupts */
342  if ( hooked_bios_interrupts != 0 ) {
343  DBGC ( &pxe_netdev, "PXENV_STOP_UNDI failed: %d interrupts "
344  "still hooked\n", hooked_bios_interrupts );
345  stop_undi->Status = PXENV_STATUS_KEEP_UNDI;
346  return PXENV_EXIT_FAILURE;
347  }
348 
349  stop_undi->Status = PXENV_STATUS_SUCCESS;
350  return PXENV_EXIT_SUCCESS;
351 }
352 
353 /* PXENV_START_BASE
354  *
355  * Status: won't implement (requires major structural changes)
356  */
357 static PXENV_EXIT_t pxenv_start_base ( struct s_PXENV_START_BASE *start_base ) {
358  DBGC ( &pxe_netdev, "PXENV_START_BASE\n" );
359 
360  start_base->Status = PXENV_STATUS_UNSUPPORTED;
361  return PXENV_EXIT_FAILURE;
362 }
363 
364 /* PXENV_STOP_BASE
365  *
366  * Status: working
367  */
368 static PXENV_EXIT_t pxenv_stop_base ( struct s_PXENV_STOP_BASE *stop_base ) {
369  DBGC ( &pxe_netdev, "PXENV_STOP_BASE\n" );
370 
371  /* The only time we will be called is when the NBP is trying
372  * to shut down the PXE stack. There's nothing we need to do
373  * in this call.
374  */
375 
376  stop_base->Status = PXENV_STATUS_SUCCESS;
377  return PXENV_EXIT_SUCCESS;
378 }
379 
380 /** PXE preboot API */
381 struct pxe_api_call pxe_preboot_api[] __pxe_api_call = {
383  struct s_PXENV_UNLOAD_STACK ),
385  struct s_PXENV_GET_CACHED_INFO ),
387  struct s_PXENV_TFTP_READ_FILE ),
389  struct s_PXENV_START_UNDI ),
391  struct s_PXENV_STOP_UNDI ),
393  struct s_PXENV_START_BASE ),
395  struct s_PXENV_STOP_BASE ),
396 };
void pxe_activate(struct net_device *netdev)
Activate PXE stack.
Definition: pxe_call.c:269
#define __attribute__(x)
Definition: compiler.h:10
Parameter block for pxenv_stop_base()
Definition: pxe_api.h:546
static const char * pxenv_get_cached_info_name(int packet_type)
Name PXENV_GET_CACHED_INFO packet type.
Definition: pxe_preboot.c:103
UINT16_t BufferLimit
Maximum buffer size.
Definition: pxe_api.h:296
PXENV_STATUS_t Status
PXE status code.
Definition: pxe_api.h:509
static PXENV_EXIT_t pxenv_restart_tftp(struct s_PXENV_TFTP_READ_FILE *restart_tftp)
Definition: pxe_preboot.c:261
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
unsigned short uint16_t
Definition: stdint.h:11
Dynamic Host Configuration Protocol.
u32 info
Definition: ar9003_mac.h:67
#define PXENV_EXIT_FAILURE
An error occurred.
Definition: pxe_types.h:46
#define PXENV_START_UNDI
PXE API function code for pxenv_start_undi()
Definition: pxe_api.h:435
#define PXENV_STOP_UNDI
PXE API function code for pxenv_stop_undi()
Definition: pxe_api.h:505
Parameter block for pxenv_unload_stack()
Definition: pxe_api.h:253
int create_fakedhcpack(struct net_device *netdev, void *data, size_t max_len)
Create fake DHCPACK packet.
Definition: fakedhcp.c:136
#define DBGC(...)
Definition: compiler.h:505
UINT16_t PacketType
Packet type.
Definition: pxe_api.h:293
static struct pxe_dhcp_packet_creator pxe_dhcp_packet_creators[]
PXE DHCP packet creators.
Definition: pxe_preboot.c:90
static PXENV_EXIT_t pxenv_stop_undi(struct s_PXENV_STOP_UNDI *stop_undi)
Definition: pxe_preboot.c:332
Parameter block for pxenv_get_cached_info()
Definition: pxe_api.h:286
#define PXENV_PACKET_TYPE_DHCP_ACK
The DHCP server's DHCPACK packet.
Definition: pxe_api.h:276
PXE API entry point.
PXENV_EXIT_t pxenv_tftp_read_file(struct s_PXENV_TFTP_READ_FILE *tftp_read_file)
TFTP/MTFTP read file.
Definition: pxe_tftp.c:478
#define PXENV_START_BASE
PXE API function code for pxenv_start_base()
Definition: pxe_api.h:524
uint32_t buffer
Buffer index (or NETVSC_RNDIS_NO_BUFFER)
Definition: netvsc.h:16
#define rm_ds
Definition: libkir.h:39
#define ISAPNP_CSN_MAX
Definition: isapnp.h:76
UINT32_t BufferSize
Size of data buffer.
Definition: pxe_api.h:648
int create_fakedhcpdiscover(struct net_device *netdev, void *data, size_t max_len)
Create fake DHCPDISCOVER packet.
Definition: fakedhcp.c:109
#define ISAPNP_READ_PORT_MAX
Definition: isapnp.h:65
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
BOOTPLAYER_t packet
Definition: pxe_preboot.c:73
#define PXENV_STATUS_UNDI_CANNOT_INITIALIZE_NIC
Definition: pxe_error.h:74
PXENV_STATUS_t Status
PXE status code.
Definition: pxe_api.h:254
void pxe_fake_cached_info(void)
Construct cached DHCP packets.
Definition: pxe_preboot.c:134
pxe_cached_info_indices
Zero-based versions of PXENV_GET_CACHED_INFO::PacketType.
Definition: pxe_preboot.c:53
void * memcpy(void *dest, const void *src, size_t len) __nonnull
UINT16_t PXENV_EXIT_t
A PXE exit code.
Definition: pxe_types.h:44
#define BUS_TYPE_PCI
PCI bus type.
Definition: device.h:43
int pxe_deactivate(void)
Deactivate PXE stack.
Definition: pxe_call.c:300
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
PXENV_STATUS_t Status
PXE status code.
Definition: pxe_api.h:287
#define PXENV_STATUS_KEEP_UNDI
Definition: pxe_error.h:23
static PXENV_EXIT_t pxenv_start_base(struct s_PXENV_START_BASE *start_base)
Definition: pxe_preboot.c:357
ring len
Length.
Definition: dwmac.h:231
#define ISAPNP_CSN_MIN
Definition: isapnp.h:75
A cached DHCP packet.
Definition: pxe_preboot.c:61
static struct net_device * netdev
Definition: gdbudp.c:52
UINT16_t BufferSize
Buffer size.
Definition: pxe_api.h:294
UINT16_t BX
bx register as passed to the Option ROM initialisation routine.
Definition: pxe_api.h:453
#define hooked_bios_interrupts
Definition: biosint.h:25
struct pxe_api_call pxe_preboot_api [] __pxe_api_call
PXE preboot API.
Definition: pxe_preboot.c:381
PXENV_STATUS_t Status
PXE status code.
Definition: pxe_api.h:547
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:78
struct net_device * pxe_netdev
Definition: pxe_undi.c:59
A network device.
Definition: netdevice.h:352
#define PXENV_PACKET_TYPE_DHCP_DISCOVER
The client's DHCPDISCOVER packet.
Definition: pxe_api.h:273
#define PXENV_STATUS_UNSUPPORTED
Definition: pxe_error.h:22
static PXENV_EXIT_t pxenv_unload_stack(struct s_PXENV_UNLOAD_STACK *unload_stack)
UNLOAD BASE CODE STACK.
Definition: pxe_preboot.c:170
PXENV_STATUS_t Status
PXE status code.
Definition: pxe_api.h:528
SEGOFF16_t Buffer
Buffer address.
Definition: pxe_api.h:295
#define PXENV_RESTART_TFTP
PXE API function code for pxenv_restart_tftp()
Definition: pxe_api.h:418
#define ISAPNP_READ_PORT_MIN
Definition: isapnp.h:59
#define PXENV_UNLOAD_STACK
PXE API function code for pxenv_unload_stack()
Definition: pxe_api.h:250
#define PXENV_EXIT_SUCCESS
No error occurred.
Definition: pxe_types.h:45
A DHCP header.
Definition: dhcp.h:615
Network device management.
static PXENV_EXIT_t pxenv_get_cached_info(struct s_PXENV_GET_CACHED_INFO *get_cached_info)
Definition: pxe_preboot.c:182
char name[NETDEV_NAME_LEN]
Name of this network device.
Definition: netdevice.h:362
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
Parameter block for pxenv_start_base()
Definition: pxe_api.h:527
A PXE API call.
Definition: pxe.h:81
#define PXENV_PACKET_TYPE_CACHED_REPLY
The Boot Server's Discover Reply packet.
Definition: pxe_api.h:283
#define __from_data16(pointer)
Definition: libkir.h:22
uint16_t isapnp_read_port
ISAPnP Read Port address.
Definition: pxe_preboot.c:50
Parameter block for pxenv_tftp_read_file()
Definition: pxe_api.h:645
ADDR32_t Buffer
Address of data buffer.
Definition: pxe_api.h:649
uint8_t data[48]
Additional event data.
Definition: ena.h:22
Format of buffer filled in by pxenv_get_cached_info()
Definition: pxe_api.h:322
#define PXENV_GET_CACHED_INFO
PXE API function code for pxenv_get_cached_info()
Definition: pxe_api.h:270
#define PXE_LOAD_PHYS
PXE physical load address.
Definition: pxe_call.h:24
Device model.
#define PXENV_STOP_BASE
PXE API function code for pxenv_stop_base()
Definition: pxe_api.h:543
Parameter block for pxenv_start_undi()
Definition: pxe_api.h:438
#define PXENV_STATUS_SUCCESS
Definition: pxe_error.h:19
#define rmlongjmp(_env, _val)
Definition: rmsetjmp.h:22
UINT16_t AX
ax register as passed to the Option ROM initialisation routine.
Definition: pxe_api.h:446
struct net_device * find_netdev_by_location(unsigned int bus_type, unsigned int location)
Get network device by PCI bus:dev.fn address.
Definition: netdevice.c:1029
PXENV_STATUS_t Status
PXE status code.
Definition: pxe_api.h:439
#define cached_info
Definition: pxe_preboot.c:128
static void shutdown_boot(void)
Shut down system for OS boot.
Definition: init.h:77
#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
String functions.
UINT16_t DX
dx register as passed to the Option ROM initialisation routine.
Definition: pxe_api.h:462
int create_fakepxebsack(struct net_device *netdev, void *data, size_t max_len)
Create fake PXE Boot Server ACK packet.
Definition: fakedhcp.c:178
Parameter block for pxenv_stop_undi()
Definition: pxe_api.h:508
A PXE DHCP packet creator.
Definition: pxe_preboot.c:77
Fake DHCP packets.
void startup(void)
Start up iPXE.
Definition: init.c:69
static union pxe_cached_info __bss16_array(cached_info, [NUM_CACHED_INFOS])
static PXENV_EXIT_t pxenv_stop_base(struct s_PXENV_STOP_BASE *stop_base)
Definition: pxe_preboot.c:368
int(* create)(struct net_device *netdev, void *data, size_t max_len)
Create DHCP packet.
Definition: pxe_preboot.c:85
#define BUS_TYPE_ISAPNP
ISAPnP bus type.
Definition: device.h:46
void * memset(void *dest, int character, size_t len) __nonnull
static PXENV_EXIT_t pxenv_start_undi(struct s_PXENV_START_UNDI *start_undi)
Definition: pxe_preboot.c:283