source: bootcd/isolinux/syslinux-6.03/core/fs/pxe/bios.c

Last change on this file was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 12.0 KB
Line 
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
10static uint16_t real_base_mem;     /* Amount of DOS memory after freeing */
11
12static bool has_gpxe;
13static uint32_t gpxe_funcs;
14
15far_ptr_t StrucPtr;
16
17/*
18 * Validity check on possible !PXE structure in buf
19 * return 1 for success, 0 for failure.
20 *
21 */
22static 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 */
43static 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 */
70static 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
83static 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
90static const struct pxenv_t *memory_scan_for_pxenv_struct(void)
91{
92    return memory_scan(0x10000, is_pxenv);
93}
94
95static 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 */
131int 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 */
261void 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 */
286static 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 */
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
383cant_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
389extern const char bdhcp_data[], adhcp_data[];
390extern const uint32_t bdhcp_len, adhcp_len;
391
392void 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}
Note: See TracBrowser for help on using the repository browser.