Home | History | Annotate | Download | only in pxe
      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  */
     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  */
     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  */
     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 
     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 
     90 static const struct pxenv_t *memory_scan_for_pxenv_struct(void)
     91 {
     92     return memory_scan(0x10000, is_pxenv);
     93 }
     94 
     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  */
    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  */
    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  */
    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  */
    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 
    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