1 #include <syslinux/firmware.h>
2 #include <syslinux/memscan.h>
9 static uint16_t real_base_mem
; /* Amount of DOS memory after freeing */
12 static uint32_t gpxe_funcs
;
17 * Validity check on possible !PXE structure in buf
18 * return 1 for success, 0 for failure.
21 static int is_pxe(const void *buf
)
23 const struct pxe_t
*pxe
= buf
;
24 const uint8_t *p
= buf
;
25 int i
= pxe
->structlength
;
28 if (i
< sizeof(struct pxe_t
) ||
29 memcmp(pxe
->signature
, "!PXE", 4))
39 * Just like is_pxe, it checks PXENV+ structure
42 static int is_pxenv(const void *buf
)
44 const struct pxenv_t
*pxenv
= buf
;
45 const uint8_t *p
= buf
;
46 int i
= pxenv
->length
;
49 /* The pxeptr field isn't present in old versions */
50 if (i
< offsetof(struct pxenv_t
, pxeptr
) ||
51 memcmp(pxenv
->signature
, "PXENV+", 6))
61 * memory_scan_for_pxe_struct:
62 * memory_scan_for_pxenv_struct:
64 * If none of the standard methods find the !PXE/PXENV+ structure,
65 * look for it by scanning memory.
67 * return the corresponding pxe structure if found, or NULL;
69 static const void *memory_scan(uintptr_t start
, int (*func
)(const void *))
73 /* Scan each 16 bytes of conventional memory before the VGA region */
74 for (ptr
= (const char *)start
; ptr
< (const char *)0xA0000; ptr
+= 16) {
76 return ptr
; /* found it! */
82 static const struct pxe_t
*memory_scan_for_pxe_struct(void)
84 uint16_t start
= bios_fbm(); /* Starting segment */
86 return memory_scan(start
<< 10, is_pxe
);
89 static const struct pxenv_t
*memory_scan_for_pxenv_struct(void)
91 return memory_scan(0x10000, is_pxenv
);
94 static int pxelinux_scan_memory(scan_memory_callback_t callback
, void *data
)
103 * If we are planning on calling unload_pxe() and unmapping the PXE
104 * region before we transfer control away from PXELINUX we can mark
105 * that region as SMT_TERMINAL to indicate that the region will
106 * become free at some point in the future.
108 start
= bios_fbm() << 10;
109 size
= (real_base_mem
- bios_fbm()) << 10;
110 dprintf("Marking PXE region 0x%x - 0x%x as SMT_TERMINAL\n",
111 start
, start
+ size
);
113 callback(data
, start
, size
, SMT_TERMINAL
);
118 * Find the !PXE structure; we search for the following, in order:
120 * a. !PXE structure as SS:[SP + 4]
121 * b. PXENV+ structure at [ES:BX]
122 * c. INT 1Ah AX=0x5650 -> PXENV+
123 * d. Search memory for !PXE
124 * e. Search memory for PXENV+
126 * If we find a PXENV+ structure, we try to find a !PXE structure from
127 * if if the API version is 2.1 or later
130 int pxe_init(bool quiet
)
132 extern void pxe_int1a(void);
135 uint16_t code_seg
, code_len
;
136 uint16_t data_seg
, data_len
;
137 const char *base
= GET_PTR(InitStack
);
140 const struct pxenv_t
*pxenv
;
141 const struct pxe_t
*pxe
;
143 /* Assume API version 2.1 */
146 /* Plan A: !PXE structure as SS:[SP + 4] */
147 off
= *(const uint16_t *)(base
+ 48);
148 seg
= *(const uint16_t *)(base
+ 50);
149 pxe
= MK_PTR(seg
, off
);
153 /* Plan B: PXENV+ structure at [ES:BX] */
155 off
= *(const uint16_t *)(base
+ 24); /* Original BX */
156 seg
= *(const uint16_t *)(base
+ 4); /* Original ES */
157 pxenv
= MK_PTR(seg
, off
);
161 /* Plan C: PXENV+ structure via INT 1Ah AX=5650h */
163 memset(®s
, 0, sizeof regs
);
164 regs
.eax
.w
[0] = 0x5650;
165 call16(pxe_int1a
, ®s
, ®s
);
166 if (!(regs
.eflags
.l
& EFLAGS_CF
) && (regs
.eax
.w
[0] == 0x564e)) {
169 pxenv
= MK_PTR(seg
, off
);
174 /* Plan D: !PXE memory scan */
176 if ((pxe
= memory_scan_for_pxe_struct())) {
182 /* Plan E: PXENV+ memory scan */
184 if ((pxenv
= memory_scan_for_pxenv_struct())) {
190 /* Found nothing at all !! */
192 ddprintf("No !PXE or PXENV+ API found; we're dead...\n");
196 APIVer
= pxenv
->version
;
198 ddprintf("Found PXENV+ structure\nPXE API version is %04x\n", APIVer
);
200 /* if the API version number is 0x0201 or higher, use the !PXE structure */
201 if (APIVer
>= 0x201) {
202 if (pxenv
->length
>= sizeof(struct pxenv_t
)) {
203 pxe
= GET_PTR(pxenv
->pxeptr
);
207 * Nope, !PXE structure missing despite API 2.1+, or at least
208 * the pointer is missing. Do a last-ditch attempt to find it
210 if ((pxe
= memory_scan_for_pxe_struct()))
213 APIVer
= 0x200; /* PXENV+ only, assume version 2.00 */
216 /* Otherwise, no dice, use PXENV+ structure */
217 data_len
= pxenv
->undidatasize
;
218 data_seg
= pxenv
->undidataseg
;
219 code_len
= pxenv
->undicodesize
;
220 code_seg
= pxenv
->undicodeseg
;
221 PXEEntry
= pxenv
->rmentry
;
224 goto have_entrypoint
;
227 data_len
= pxe
->seg
[PXE_Seg_UNDIData
].size
;
228 data_seg
= pxe
->seg
[PXE_Seg_UNDIData
].sel
;
229 code_len
= pxe
->seg
[PXE_Seg_UNDICode
].size
;
230 code_seg
= pxe
->seg
[PXE_Seg_UNDICode
].sel
;
231 PXEEntry
= pxe
->entrypointsp
;
239 ddprintf("%s entry point found (we hope) at %04X:%04X via plan %c\n",
240 type
, PXEEntry
.seg
, PXEEntry
.offs
, plan
);
241 ddprintf("UNDI code segment at %04X len %04X\n", code_seg
, code_len
);
242 ddprintf("UNDI data segment at %04X len %04X\n", data_seg
, data_len
);
245 syslinux_memscan_new(pxelinux_scan_memory
);
247 code_seg
= code_seg
+ ((code_len
+ 15) >> 4);
248 data_seg
= data_seg
+ ((data_len
+ 15) >> 4);
250 real_base_mem
= max(code_seg
, data_seg
) >> 6; /* Convert to kilobytes */
258 * See if we have gPXE
263 static __lowmem
struct s_PXENV_FILE_API_CHECK api_check
;
265 if (APIVer
>= 0x201) {
266 api_check
.Size
= sizeof api_check
;
267 api_check
.Magic
= 0x91d447b2;
268 err
= pxe_call(PXENV_FILE_API_CHECK
, &api_check
);
269 if (!err
&& api_check
.Magic
== 0xe9c17b20)
270 gpxe_funcs
= api_check
.APIMask
;
273 /* Necessary functions for us to use the gPXE file API */
274 has_gpxe
= (~gpxe_funcs
& 0x4b) == 0;
279 * Get a DHCP packet from the PXE stack into a lowmem buffer
281 * @param: type, packet type
282 * @return: buffer size
285 static int pxe_get_cached_info(int type
, void *buf
, size_t bufsiz
)
288 static __lowmem
struct s_PXENV_GET_CACHED_INFO get_cached_info
;
289 ddprintf(" %02x", type
);
291 memset(&get_cached_info
, 0, sizeof get_cached_info
);
292 get_cached_info
.PacketType
= type
;
293 get_cached_info
.BufferSize
= bufsiz
;
294 get_cached_info
.Buffer
= FAR_PTR(buf
);
295 err
= pxe_call(PXENV_GET_CACHED_INFO
, &get_cached_info
);
297 ddprintf("PXE API call failed, error %04x\n", err
);
301 return get_cached_info
.BufferSize
;
305 * This function unloads the PXE and UNDI stacks and
306 * unclaims the memory.
308 __export
void unload_pxe(uint16_t flags
)
310 /* PXE unload sequences */
313 * UNDI_SHUTDOWN, UNDI_CLEANUP, STOP_UNDI
314 * Older Syslinux did:
315 * UDP_CLOSE, UNDI_SHUTDOWN, UNLOAD_STACK, STOP_UNDI/UNDI_CLEANUP
317 static const uint8_t new_api_unload
[] = {
318 PXENV_UNDI_SHUTDOWN
, PXENV_UNLOAD_STACK
, PXENV_STOP_UNDI
, 0
320 static const uint8_t old_api_unload
[] = {
321 PXENV_UNDI_SHUTDOWN
, PXENV_UNLOAD_STACK
, PXENV_UNDI_CLEANUP
, 0
325 const uint8_t *api_ptr
;
328 static __lowmem
union {
329 struct s_PXENV_UNDI_SHUTDOWN undi_shutdown
;
330 struct s_PXENV_UNLOAD_STACK unload_stack
;
331 struct s_PXENV_STOP_UNDI stop_undi
;
332 struct s_PXENV_UNDI_CLEANUP undi_cleanup
;
333 uint16_t Status
; /* All calls have this as the first member */
336 dprintf("Called unload_pxe()...\n");
337 dprintf("FBM before unload = %d\n", bios_fbm());
341 dprintf("FBM after reset_pxe = %d, err = %d\n", bios_fbm(), err
);
343 /* If we want to keep PXE around, we still need to reset it */
347 dprintf("APIVer = %04x\n", APIVer
);
349 api_ptr
= APIVer
>= 0x0200 ? new_api_unload
: old_api_unload
;
350 while((api
= *api_ptr
++)) {
351 dprintf("PXE call %04x\n", api
);
352 memset(&unload_call
, 0, sizeof unload_call
);
353 err
= pxe_call(api
, &unload_call
);
354 if (err
|| unload_call
.Status
!= PXENV_STATUS_SUCCESS
) {
355 ddprintf("PXE unload API call %04x failed: 0x%x\n",
356 api
, unload_call
.Status
);
362 if (real_base_mem
<= bios_fbm()) { /* Sanity check */
363 dprintf("FBM %d < real_base_mem %d\n", bios_fbm(), real_base_mem
);
368 /* Check that PXE actually unhooked the INT 0x1A chain */
369 int_addr
= (size_t)GET_PTR(*(far_ptr_t
*)(4 * 0x1a));
371 if (int_addr
>= real_base_mem
|| int_addr
< bios_fbm()) {
372 set_bios_fbm(real_base_mem
);
373 dprintf("FBM after unload_pxe = %d\n", bios_fbm());
377 dprintf("Can't free FBM, real_base_mem = %d, "
378 "FBM = %d, INT 1A = %08x (%d)\n",
379 real_base_mem
, bios_fbm(),
380 *(uint32_t *)(4 * 0x1a), int_addr
);
383 ddprintf("Failed to free base memory error %04x-%08x (%d/%dK)\n",
384 api
, *(uint32_t *)(4 * 0x1a), bios_fbm(), real_base_mem
);
388 void net_parse_dhcp(void)
392 const size_t dhcp_max_packet
= 4096;
394 bp
= lmalloc(dhcp_max_packet
);
396 ddprintf("Out of low memory\n");
400 *LocalDomain
= 0; /* No LocalDomain received */
403 * Get the DHCP client identifiers (query info 1)
405 ddprintf("Getting cached packet ");
406 pkt_len
= pxe_get_cached_info(1, bp
, dhcp_max_packet
);
407 parse_dhcp(bp
, pkt_len
);
409 * We don't use flags from the request packet, so
410 * this is a good time to initialize DHCPMagic...
411 * Initialize it to 1 meaning we will accept options found;
412 * in earlier versions of PXELINUX bit 0 was used to indicate
413 * we have found option 208 with the appropriate magic number;
414 * we no longer require that, but MAY want to re-introduce
415 * it in the future for vendor encapsulated options.
417 *(char *)&DHCPMagic
= 1;
420 * Get the BOOTP/DHCP packet that brought us file (and an IP
421 * address). This lives in the DHCPACK packet (query info 2)
423 pkt_len
= pxe_get_cached_info(2, bp
, dhcp_max_packet
);
424 parse_dhcp(bp
, pkt_len
);
426 * Save away MAC address (assume this is in query info 2. If this
427 * turns out to be problematic it might be better getting it from
428 * the query info 1 packet
430 MAC_len
= bp
->hardlen
> 16 ? 0 : bp
->hardlen
;
431 MAC_type
= bp
->hardware
;
432 memcpy(MAC
, bp
->macaddr
, MAC_len
);
435 * Get the boot file and other info. This lives in the CACHED_REPLY
436 * packet (query info 3)
438 pkt_len
= pxe_get_cached_info(3, bp
, dhcp_max_packet
);
439 parse_dhcp(bp
, pkt_len
);