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(®s, 0, sizeof regs); 165 regs.eax.w[0] = 0x5650; 166 call16(pxe_int1a, ®s, ®s); 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