• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <syslinux/firmware.h>
2 #include <syslinux/memscan.h>
3 #include <core.h>
4 #include "pxe.h"
5 #include <net.h>
6 #include <minmax.h>
7 #include <bios.h>
8 #include <dprintf.h>
9 
10 static uint16_t real_base_mem;	   /* Amount of DOS memory after freeing */
11 
12 static bool has_gpxe;
13 static uint32_t gpxe_funcs;
14 
15 far_ptr_t StrucPtr;
16 
17 /*
18  * Validity check on possible !PXE structure in buf
19  * return 1 for success, 0 for failure.
20  *
21  */
is_pxe(const void * buf)22 static int is_pxe(const void *buf)
23 {
24     const struct pxe_t *pxe = buf;
25     const uint8_t *p = buf;
26     int i = pxe->structlength;
27     uint8_t sum = 0;
28 
29     if (i < sizeof(struct pxe_t) ||
30 	memcmp(pxe->signature, "!PXE", 4))
31         return 0;
32 
33     while (i--)
34         sum += *p++;
35 
36     return sum == 0;
37 }
38 
39 /*
40  * Just like is_pxe, it checks PXENV+ structure
41  *
42  */
is_pxenv(const void * buf)43 static int is_pxenv(const void *buf)
44 {
45     const struct pxenv_t *pxenv = buf;
46     const uint8_t *p = buf;
47     int i = pxenv->length;
48     uint8_t sum = 0;
49 
50     /* The pxeptr field isn't present in old versions */
51     if (i < offsetof(struct pxenv_t, pxeptr) ||
52 	memcmp(pxenv->signature, "PXENV+", 6))
53         return 0;
54 
55     while (i--)
56         sum += *p++;
57 
58     return sum == 0;
59 }
60 
61 /*
62  * memory_scan_for_pxe_struct:
63  * memory_scan_for_pxenv_struct:
64  *
65  *	If none of the standard methods find the !PXE/PXENV+ structure,
66  *	look for it by scanning memory.
67  *
68  *	return the corresponding pxe structure if found, or NULL;
69  */
memory_scan(uintptr_t start,int (* func)(const void *))70 static const void *memory_scan(uintptr_t start, int (*func)(const void *))
71 {
72     const char *ptr;
73 
74     /* Scan each 16 bytes of conventional memory before the VGA region */
75     for (ptr = (const char *)start; ptr < (const char *)0xA0000; ptr += 16) {
76         if (func(ptr))
77             return ptr;		/* found it! */
78 	ptr += 16;
79     }
80     return NULL;
81 }
82 
memory_scan_for_pxe_struct(void)83 static const struct pxe_t *memory_scan_for_pxe_struct(void)
84 {
85     uint16_t start = bios_fbm(); /* Starting segment */
86 
87     return memory_scan(start << 10, is_pxe);
88 }
89 
memory_scan_for_pxenv_struct(void)90 static const struct pxenv_t *memory_scan_for_pxenv_struct(void)
91 {
92     return memory_scan(0x10000, is_pxenv);
93 }
94 
pxelinux_scan_memory(scan_memory_callback_t callback,void * data)95 static int pxelinux_scan_memory(scan_memory_callback_t callback, void *data)
96 {
97     addr_t start, size;
98     int rv = 0;
99 
100     if (KeepPXE)
101 	return 0;
102 
103     /*
104      * If we are planning on calling unload_pxe() and unmapping the PXE
105      * region before we transfer control away from PXELINUX we can mark
106      * that region as SMT_TERMINAL to indicate that the region will
107      * become free at some point in the future.
108      */
109     start = bios_fbm() << 10;
110     size = (real_base_mem - bios_fbm()) << 10;
111     dprintf("Marking PXE region 0x%x - 0x%x as SMT_TERMINAL\n",
112 	start, start + size);
113 
114     callback(data, start, size, SMT_TERMINAL);
115     return rv;
116 }
117 
118 /*
119  * Find the !PXE structure; we search for the following, in order:
120  *
121  * a. !PXE structure as SS:[SP + 4]
122  * b. PXENV+ structure at [ES:BX]
123  * c. INT 1Ah AX=0x5650 -> PXENV+
124  * d. Search memory for !PXE
125  * e. Search memory for PXENV+
126  *
127  * If we find a PXENV+ structure, we try to find a !PXE structure from
128  * if if the API version is 2.1 or later
129  *
130  */
pxe_init(bool quiet)131 int pxe_init(bool quiet)
132 {
133     extern void pxe_int1a(void);
134     char plan = 'A';
135     uint16_t seg, off;
136     uint16_t code_seg, code_len;
137     uint16_t data_seg, data_len;
138     const char *base = GET_PTR(InitStack);
139     com32sys_t regs;
140     const char *type;
141     const struct pxenv_t *pxenv;
142     const struct pxe_t *pxe;
143 
144     /* Assume API version 2.1 */
145     APIVer = 0x201;
146 
147     /* Plan A: !PXE structure as SS:[SP + 4] */
148     off = *(const uint16_t *)(base + 48);
149     seg = *(const uint16_t *)(base + 50);
150     pxe = MK_PTR(seg, off);
151     if (is_pxe(pxe))
152         goto have_pxe;
153 
154     /* Plan B: PXENV+ structure at [ES:BX] */
155     plan++;
156     off = *(const uint16_t *)(base + 24);  /* Original BX */
157     seg = *(const uint16_t *)(base + 4);   /* Original ES */
158     pxenv = MK_PTR(seg, off);
159     if (is_pxenv(pxenv))
160         goto have_pxenv;
161 
162     /* Plan C: PXENV+ structure via INT 1Ah AX=5650h  */
163     plan++;
164     memset(&regs, 0, sizeof regs);
165     regs.eax.w[0] = 0x5650;
166     call16(pxe_int1a, &regs, &regs);
167     if (!(regs.eflags.l & EFLAGS_CF) && (regs.eax.w[0] == 0x564e)) {
168 	off = regs.ebx.w[0];
169 	seg = regs.es;
170 	pxenv = MK_PTR(seg, off);
171         if (is_pxenv(pxenv))
172             goto have_pxenv;
173     }
174 
175     /* Plan D: !PXE memory scan */
176     plan++;
177     if ((pxe = memory_scan_for_pxe_struct())) {
178 	off = OFFS(pxe);
179 	seg = SEG(pxe);
180         goto have_pxe;
181     }
182 
183     /* Plan E: PXENV+ memory scan */
184     plan++;
185     if ((pxenv = memory_scan_for_pxenv_struct())) {
186 	off = OFFS(pxenv);
187 	seg = SEG(pxenv);
188         goto have_pxenv;
189     }
190 
191     /* Found nothing at all !! */
192     if (!quiet)
193 	ddprintf("No !PXE or PXENV+ API found; we're dead...\n");
194     return -1;
195 
196  have_pxenv:
197     APIVer = pxenv->version;
198     if (!quiet)
199 	ddprintf("Found PXENV+ structure\nPXE API version is %04x\n", APIVer);
200 
201     /* if the API version number is 0x0201 or higher, use the !PXE structure */
202     if (APIVer >= 0x201) {
203 	if (pxenv->length >= sizeof(struct pxenv_t)) {
204 	    pxe = GET_PTR(pxenv->pxeptr);
205 	    if (is_pxe(pxe))
206 		goto have_pxe;
207 	    /*
208 	     * Nope, !PXE structure missing despite API 2.1+, or at least
209 	     * the pointer is missing. Do a last-ditch attempt to find it
210 	     */
211 	    if ((pxe = memory_scan_for_pxe_struct()))
212 		goto have_pxe;
213 	}
214 	APIVer = 0x200;		/* PXENV+ only, assume version 2.00 */
215     }
216 
217     /* Otherwise, no dice, use PXENV+ structure */
218     data_len = pxenv->undidatasize;
219     data_seg = pxenv->undidataseg;
220     code_len = pxenv->undicodesize;
221     code_seg = pxenv->undicodeseg;
222     PXEEntry = pxenv->rmentry;
223     type = "PXENV+";
224 
225     goto have_entrypoint;
226 
227  have_pxe:
228     data_len = pxe->seg[PXE_Seg_UNDIData].size;
229     data_seg = pxe->seg[PXE_Seg_UNDIData].sel;
230     code_len = pxe->seg[PXE_Seg_UNDICode].size;
231     code_seg = pxe->seg[PXE_Seg_UNDICode].sel;
232     PXEEntry = pxe->entrypointsp;
233     type = "!PXE";
234 
235  have_entrypoint:
236     StrucPtr.offs = off;
237     StrucPtr.seg  = seg;
238 
239     if (!quiet) {
240 	ddprintf("%s entry point found (we hope) at %04X:%04X via plan %c\n",
241 	       type, PXEEntry.seg, PXEEntry.offs, plan);
242 	ddprintf("UNDI code segment at %04X len %04X\n", code_seg, code_len);
243 	ddprintf("UNDI data segment at %04X len %04X\n", data_seg, data_len);
244     }
245 
246     syslinux_memscan_new(pxelinux_scan_memory);
247 
248     code_seg = code_seg + ((code_len + 15) >> 4);
249     data_seg = data_seg + ((data_len + 15) >> 4);
250 
251     real_base_mem = max(code_seg, data_seg) >> 6; /* Convert to kilobytes */
252 
253     probe_undi();
254 
255     return 0;
256 }
257 
258 /*
259  * See if we have gPXE
260  */
gpxe_init(void)261 void gpxe_init(void)
262 {
263     int err;
264     static __lowmem struct s_PXENV_FILE_API_CHECK api_check;
265 
266     if (APIVer >= 0x201) {
267 	api_check.Size = sizeof api_check;
268 	api_check.Magic = 0x91d447b2;
269 	err = pxe_call(PXENV_FILE_API_CHECK, &api_check);
270 	if (!err && api_check.Magic == 0xe9c17b20)
271 	    gpxe_funcs = api_check.APIMask;
272     }
273 
274     /* Necessary functions for us to use the gPXE file API */
275     has_gpxe = (~gpxe_funcs & 0x4b) == 0;
276 }
277 
278 
279 /**
280  * Get a DHCP packet from the PXE stack into a lowmem buffer
281  *
282  * @param:  type,  packet type
283  * @return: buffer size
284  *
285  */
pxe_get_cached_info(int type,void * buf,size_t bufsiz)286 static int pxe_get_cached_info(int type, void *buf, size_t bufsiz)
287 {
288     int err;
289     static __lowmem struct s_PXENV_GET_CACHED_INFO get_cached_info;
290     ddprintf(" %02x", type);
291 
292     memset(&get_cached_info, 0, sizeof get_cached_info);
293     get_cached_info.PacketType  = type;
294     get_cached_info.BufferSize  = bufsiz;
295     get_cached_info.Buffer      = FAR_PTR(buf);
296     err = pxe_call(PXENV_GET_CACHED_INFO, &get_cached_info);
297     if (err) {
298         ddprintf("PXE API call failed, error  %04x\n", err);
299 	kaboom();
300     }
301 
302     return get_cached_info.BufferSize;
303 }
304 
305 /*
306  * This function unloads the PXE and UNDI stacks and
307  * unclaims the memory.
308  */
unload_pxe(uint16_t flags)309 __export void unload_pxe(uint16_t flags)
310 {
311     /* PXE unload sequences */
312     /*
313      * iPXE does:
314      * UNDI_SHUTDOWN, UNDI_CLEANUP, STOP_UNDI
315      * Older Syslinux did:
316      * UDP_CLOSE, UNDI_SHUTDOWN, UNLOAD_STACK, STOP_UNDI/UNDI_CLEANUP
317      */
318     static const uint8_t new_api_unload[] = {
319 	PXENV_UNDI_SHUTDOWN, PXENV_UNLOAD_STACK, PXENV_STOP_UNDI, 0
320     };
321     static const uint8_t old_api_unload[] = {
322 	PXENV_UNDI_SHUTDOWN, PXENV_UNLOAD_STACK, PXENV_UNDI_CLEANUP, 0
323     };
324 
325     unsigned int api;
326     const uint8_t *api_ptr;
327     int err;
328     size_t int_addr;
329     static __lowmem union {
330 	struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
331 	struct s_PXENV_UNLOAD_STACK unload_stack;
332 	struct s_PXENV_STOP_UNDI stop_undi;
333 	struct s_PXENV_UNDI_CLEANUP undi_cleanup;
334 	uint16_t Status;	/* All calls have this as the first member */
335     } unload_call;
336 
337     dprintf("Called unload_pxe()...\n");
338     dprintf("FBM before unload = %d\n", bios_fbm());
339 
340     err = reset_pxe();
341 
342     dprintf("FBM after reset_pxe = %d, err = %d\n", bios_fbm(), err);
343 
344     /* If we want to keep PXE around, we still need to reset it */
345     if (flags || err)
346 	return;
347 
348     dprintf("APIVer = %04x\n", APIVer);
349 
350     api_ptr = APIVer >= 0x0200 ? new_api_unload : old_api_unload;
351     while((api = *api_ptr++)) {
352 	dprintf("PXE call %04x\n", api);
353 	memset(&unload_call, 0, sizeof unload_call);
354 	err = pxe_call(api, &unload_call);
355 	if (err || unload_call.Status != PXENV_STATUS_SUCCESS) {
356 	    ddprintf("PXE unload API call %04x failed: 0x%x\n",
357 		   api, unload_call.Status);
358 	    goto cant_free;
359 	}
360     }
361 
362     api = 0xff00;
363     if (real_base_mem <= bios_fbm()) {  /* Sanity check */
364 	dprintf("FBM %d < real_base_mem %d\n", bios_fbm(), real_base_mem);
365 	goto cant_free;
366     }
367     api++;
368 
369     /* Check that PXE actually unhooked the INT 0x1A chain */
370     int_addr = (size_t)GET_PTR(*(far_ptr_t *)(4 * 0x1a));
371     int_addr >>= 10;
372     if (int_addr >= real_base_mem || int_addr < bios_fbm()) {
373 	set_bios_fbm(real_base_mem);
374 	dprintf("FBM after unload_pxe = %d\n", bios_fbm());
375 	return;
376     }
377 
378     dprintf("Can't free FBM, real_base_mem = %d, "
379 	    "FBM = %d, INT 1A = %08x (%d)\n",
380 	    real_base_mem, bios_fbm(),
381 	    *(uint32_t *)(4 * 0x1a), int_addr);
382 
383 cant_free:
384     ddprintf("Failed to free base memory error %04x-%08x (%d/%dK)\n",
385 	   api, *(uint32_t *)(4 * 0x1a), bios_fbm(), real_base_mem);
386     return;
387 }
388 
389 extern const char bdhcp_data[], adhcp_data[];
390 extern const uint32_t bdhcp_len, adhcp_len;
391 
net_parse_dhcp(void)392 void net_parse_dhcp(void)
393 {
394     int pkt_len;
395     struct bootp_t *bp;
396     const size_t dhcp_max_packet = 4096;
397 
398     bp = lmalloc(dhcp_max_packet);
399     if (!bp) {
400 	ddprintf("Out of low memory\n");
401 	kaboom();
402     }
403 
404     *LocalDomain = 0;   /* No LocalDomain received */
405 
406     /*
407      * Parse any "before" hardcoded options
408      */
409     dprintf("DHCP: bdhcp_len = %d\n", bdhcp_len);
410     parse_dhcp_options(bdhcp_data, bdhcp_len, 0);
411 
412     /*
413      * Get the DHCP client identifiers (query info 1)
414      */
415     ddprintf("Getting cached packet ");
416     pkt_len = pxe_get_cached_info(1, bp, dhcp_max_packet);
417     parse_dhcp(bp, pkt_len);
418 
419     /*
420      * We don't use flags from the request packet, so
421      * this is a good time to initialize DHCPMagic...
422      * Initialize it to 1 meaning we will accept options found;
423      * in earlier versions of PXELINUX bit 0 was used to indicate
424      * we have found option 208 with the appropriate magic number;
425      * we no longer require that, but MAY want to re-introduce
426      * it in the future for vendor encapsulated options.
427      */
428     *(char *)&DHCPMagic = 1;
429 
430     /*
431      * Get the BOOTP/DHCP packet that brought us file (and an IP
432      * address). This lives in the DHCPACK packet (query info 2)
433      */
434     pkt_len = pxe_get_cached_info(2, bp, dhcp_max_packet);
435     parse_dhcp(bp, pkt_len);
436     /*
437      * Save away MAC address (assume this is in query info 2. If this
438      * turns out to be problematic it might be better getting it from
439      * the query info 1 packet
440      */
441     MAC_len = bp->hardlen > 16 ? 0 : bp->hardlen;
442     MAC_type = bp->hardware;
443     memcpy(MAC, bp->macaddr, MAC_len);
444 
445     /*
446      * Get the boot file and other info. This lives in the CACHED_REPLY
447      * packet (query info 3)
448      */
449     pkt_len = pxe_get_cached_info(3, bp, dhcp_max_packet);
450     parse_dhcp(bp, pkt_len);
451     ddprintf("\n");
452 
453     /*
454      * Parse any "after" hardcoded options
455      */
456     dprintf("DHCP: adhcp_len = %d\n", adhcp_len);
457     parse_dhcp_options(adhcp_data, adhcp_len, 0);
458 
459     lfree(bp);
460 }
461