11 #include <lwip/tcpip.h>
18 __lowmem t_PXENV_UNDI_GET_INFORMATION pxe_undi_info
;
19 __lowmem t_PXENV_UNDI_GET_IFACE_INFO pxe_undi_iface
;
21 static uint16_t real_base_mem
; /* Amount of DOS memory after freeing */
23 uint8_t MAC
[MAC_MAX
]; /* Actual MAC address */
24 uint8_t MAC_len
; /* MAC address len */
25 uint8_t MAC_type
; /* MAC address type */
27 char boot_file
[256]; /* From DHCP */
28 char path_prefix
[256]; /* From DHCP */
31 static uint32_t gpxe_funcs
;
32 bool have_uuid
= false;
34 static struct url_scheme
{
36 void (*open
)(struct url_info
*, int, struct inode
*, const char **);
39 { "tftp", tftp_open
, 0 },
40 { "http", http_open
, O_DIRECTORY
},
41 { "ftp", ftp_open
, O_DIRECTORY
},
44 #define OK_FLAGS_MASK (O_DIRECTORY|O_WRONLY)
47 * Allocate a local UDP port structure and assign it a local port number.
48 * Return the inode pointer if success, or null if failure
50 static struct inode
*allocate_socket(struct fs_info
*fs
)
52 struct inode
*inode
= alloc_inode(fs
, 0, sizeof(struct pxe_pvt_inode
));
55 malloc_error("socket structure");
57 inode
->mode
= DT_REG
; /* No other types relevant for PXE */
63 void free_socket(struct inode
*inode
)
65 struct pxe_pvt_inode
*socket
= PVT(inode
);
67 free(socket
->tftp_pktbuf
); /* If we allocated a buffer, free it now */
71 static void pxe_close_file(struct file
*file
)
73 struct inode
*inode
= file
->inode
;
74 struct pxe_pvt_inode
*socket
= PVT(inode
);
76 if (!socket
->tftp_goteof
) {
77 socket
->ops
->close(inode
);
84 * Tests an IP address in _ip_ for validity; return with 0 for bad, 1 for good.
85 * We used to refuse class E, but class E addresses are likely to become
86 * assignable unicast addresses in the near future.
89 bool ip_ok(uint32_t ip
)
91 uint8_t ip_hi
= (uint8_t)ip
; /* First octet of the ip address */
93 if (ip
== 0xffffffff || /* Refuse the all-ones address */
94 ip_hi
== 0 || /* Refuse network zero */
95 ip_hi
== 127 || /* Refuse the loopback network */
96 (ip_hi
& 240) == 224) /* Refuse class D */
104 * Take an IP address (in network byte order) in _ip_ and
105 * output a dotted quad string to _dst_, returns the length
106 * of the dotted quad ip string.
109 static int gendotquad(char *dst
, uint32_t ip
)
111 return sprintf(dst
, "%u.%u.%u.%u",
112 ((const uint8_t *)&ip
)[0],
113 ((const uint8_t *)&ip
)[1],
114 ((const uint8_t *)&ip
)[2],
115 ((const uint8_t *)&ip
)[3]);
119 * the ASM pxenv function wrapper, return 1 if error, or 0
122 int pxe_call(int opcode
, void *data
)
124 static DECLARE_INIT_SEMAPHORE(pxe_sem
, 1);
125 extern void pxenv(void);
128 sem_down(&pxe_sem
, 0);
131 dprintf("pxe_call op %04x data %p\n", opcode
, data
);
134 memset(®s
, 0, sizeof regs
);
135 regs
.ebx
.w
[0] = opcode
;
137 regs
.edi
.w
[0] = OFFS(data
);
138 call16(pxenv
, ®s
, ®s
);
142 return regs
.eflags
.l
& EFLAGS_CF
; /* CF SET if fail */
146 * Get a DHCP packet from the PXE stack into the trackbuf
148 * @param: type, packet type
149 * @return: buffer size
152 static int pxe_get_cached_info(int type
)
155 static __lowmem
struct s_PXENV_GET_CACHED_INFO get_cached_info
;
156 printf(" %02x", type
);
158 memset(&get_cached_info
, 0, sizeof get_cached_info
);
159 get_cached_info
.PacketType
= type
;
160 get_cached_info
.BufferSize
= 8192;
161 get_cached_info
.Buffer
= FAR_PTR(trackbuf
);
162 err
= pxe_call(PXENV_GET_CACHED_INFO
, &get_cached_info
);
164 printf("PXE API call failed, error %04x\n", err
);
168 return get_cached_info
.BufferSize
;
172 * mangle a filename pointed to by _src_ into a buffer pointed
173 * to by _dst_; ends on encountering any whitespace.
175 * This deliberately does not attempt to do any conversion of
176 * pathname separators.
179 static void pxe_mangle_name(char *dst
, const char *src
)
181 size_t len
= FILENAME_MAX
-1;
183 while (len
-- && not_whitespace(*src
))
190 * Read a single character from the specified pxe inode.
191 * Very useful for stepping through http streams and
192 * parsing their headers.
194 int pxe_getc(struct inode
*inode
)
196 struct pxe_pvt_inode
*socket
= PVT(inode
);
199 while (!socket
->tftp_bytesleft
) {
200 if (socket
->tftp_goteof
)
203 socket
->ops
->fill_buffer(inode
);
206 byte
= *socket
->tftp_dataptr
;
207 socket
->tftp_bytesleft
-= 1;
208 socket
->tftp_dataptr
+= 1;
214 * Get a fresh packet if the buffer is drained, and we haven't hit
215 * EOF yet. The buffer should be filled immediately after draining!
217 static void fill_buffer(struct inode
*inode
)
219 struct pxe_pvt_inode
*socket
= PVT(inode
);
220 if (socket
->tftp_bytesleft
|| socket
->tftp_goteof
)
223 return socket
->ops
->fill_buffer(inode
);
228 * getfssec: Get multiple clusters from a file, given the starting cluster.
229 * In this case, get multiple blocks from a specific TCP connection.
231 * @param: fs, the fs_info structure address, in pxe, we don't use this.
232 * @param: buf, buffer to store the read data
233 * @param: openfile, TFTP socket pointer
234 * @param: blocks, 512-byte block count; 0FFFFh = until end of file
236 * @return: the bytes read
239 static uint32_t pxe_getfssec(struct file
*file
, char *buf
,
240 int blocks
, bool *have_more
)
242 struct inode
*inode
= file
->inode
;
243 struct pxe_pvt_inode
*socket
= PVT(inode
);
248 count
<<= TFTP_BLOCKSIZE_LG2
;
250 fill_buffer(inode
); /* If we have no 'fresh' buffer, get it */
251 if (!socket
->tftp_bytesleft
)
255 if (chunk
> socket
->tftp_bytesleft
)
256 chunk
= socket
->tftp_bytesleft
;
257 socket
->tftp_bytesleft
-= chunk
;
258 memcpy(buf
, socket
->tftp_dataptr
, chunk
);
259 socket
->tftp_dataptr
+= chunk
;
266 if (socket
->tftp_bytesleft
|| (socket
->tftp_filepos
< inode
->size
)) {
269 } else if (socket
->tftp_goteof
) {
271 * The socket is closed and the buffer drained; the caller will
272 * call close_file and therefore free the socket.
281 * Assign an IP address to a URL
283 static void url_set_ip(struct url_info
*url
)
287 url
->ip
= dns_resolv(url
->host
);
289 url
->ip
= IPInfo
.serverip
;
293 * Open the specified connection
295 * @param:filename, the file we wanna open
297 * @out: open_file_t structure, stores in file->open_file
298 * @out: the lenght of this file, stores in file->file_len
301 static void __pxe_searchdir(const char *filename
, int flags
, struct file
*file
);
302 extern uint16_t PXERetry
;
304 static void pxe_searchdir(const char *filename
, int flags
, struct file
*file
)
309 dprintf("PXE: file = %p, retries left = %d: ", file
, i
);
310 __pxe_searchdir(filename
, flags
, file
);
311 dprintf("%s\n", file
->inode
? "ok" : "failed");
312 } while (!file
->inode
&& i
--);
314 static void __pxe_searchdir(const char *filename
, int flags
, struct file
*file
)
316 struct fs_info
*fs
= file
->fs
;
318 char fullpath
[2*FILENAME_MAX
];
320 char urlsave
[2*FILENAME_MAX
];
323 const struct url_scheme
*us
= NULL
;
324 int redirect_count
= 0;
325 bool found_scheme
= false;
327 inode
= file
->inode
= NULL
;
330 if (redirect_count
++ > 5)
333 strlcpy(fullpath
, filename
, sizeof fullpath
);
335 strcpy(urlsave
, fullpath
);
337 parse_url(&url
, fullpath
);
338 if (url
.type
== URL_SUFFIX
) {
339 snprintf(fullpath
, sizeof fullpath
, "%s%s", fs
->cwd_name
, filename
);
341 strcpy(urlsave
, fullpath
);
343 parse_url(&url
, fullpath
);
346 inode
= allocate_socket(fs
);
348 return; /* Allocation failure */
353 found_scheme
= false;
354 for (us
= url_schemes
; us
->name
; us
++) {
355 if (!strcmp(us
->name
, url
.scheme
)) {
356 if ((flags
& ~us
->ok_flags
& OK_FLAGS_MASK
) == 0)
357 us
->open(&url
, flags
, inode
, &filename
);
363 /* filename here is set on a redirect */
368 /* No URL scheme found, hand it to GPXE */
369 gpxe_open(inode
, urlsave
);
375 file
->inode
->mode
= (flags
& O_DIRECTORY
) ? DT_DIR
: DT_REG
;
385 * Store standard filename prefix
387 static void get_prefix(void)
393 if (!(DHCPMagic
& 0x04)) {
394 /* No path prefix option, derive from boot file */
396 strlcpy(path_prefix
, boot_file
, sizeof path_prefix
);
397 len
= strlen(path_prefix
);
398 p
= &path_prefix
[len
- 1];
404 c
= (c
>= '0' && c
<= '9') ||
405 (c
>= 'a' && c
<= 'z') ||
406 (c
== '.' || c
== '-');
414 *(p
+ 2) = 0; /* Zero-terminate after delimiter */
417 printf("TFTP prefix: %s\n", path_prefix
);
424 static size_t pxe_realpath(struct fs_info
*fs
, char *dst
, const char *src
,
427 return snprintf(dst
, bufsize
, "%s%s",
428 url_type(src
) == URL_SUFFIX
? fs
->cwd_name
: "", src
);
434 static int pxe_chdir(struct fs_info
*fs
, const char *src
)
436 /* The cwd for PXE is just a text prefix */
437 if (url_type(src
) == URL_SUFFIX
)
438 strlcat(fs
->cwd_name
, src
, sizeof fs
->cwd_name
);
440 strlcpy(fs
->cwd_name
, src
, sizeof fs
->cwd_name
);
442 dprintf("cwd = \"%s\"\n", fs
->cwd_name
);
447 * try to load a config file, if found, return 1, or return 0
450 static int try_load(char *config_name
)
454 printf("Trying to load: %-50s ", config_name
);
455 pxe_mangle_name(KernelName
, config_name
);
457 memset(®s
, 0, sizeof regs
);
458 regs
.edi
.w
[0] = OFFS_WRT(KernelName
, 0);
459 call16(core_open
, ®s
, ®s
);
460 if (regs
.eflags
.l
& EFLAGS_ZF
) {
461 strcpy(ConfigName
, KernelName
);
471 /* Load the config file, return 1 if failed, or 0 */
472 static int pxe_load_config(void)
474 const char *cfgprefix
= "pxelinux.cfg/";
475 const char *default_str
= "default";
481 if (DHCPMagic
& 0x02) {
482 /* We got a DHCP option, try it first */
483 if (try_load(ConfigName
))
488 * Have to guess config file name ...
490 config_file
= stpcpy(ConfigName
, cfgprefix
);
492 /* Try loading by UUID */
493 if (sysappend_strings
[SYSAPPEND_SYSUUID
]) {
494 strcpy(config_file
, sysappend_strings
[SYSAPPEND_SYSUUID
]+8);
495 if (try_load(ConfigName
))
499 /* Try loading by MAC address */
500 strcpy(config_file
, sysappend_strings
[SYSAPPEND_BOOTIF
]+7);
501 if (try_load(ConfigName
))
504 /* Nope, try hexadecimal IP prefixes... */
505 sprintf(config_file
, "%08X", ntohl(IPInfo
.myip
));
506 last
= &config_file
[8];
508 *last
= '\0'; /* Zero-terminate string */
509 if (try_load(ConfigName
))
511 last
--; /* Drop one character */
515 /* Final attempt: "default" string */
516 strcpy(config_file
, default_str
);
517 if (try_load(ConfigName
))
520 printf("%-68s\n", "Unable to locate configuration file");
525 * Generate the bootif string.
527 static void make_bootif_string(void)
529 static char bootif_str
[7+3*(MAC_MAX
+1)];
531 char *dst
= bootif_str
;
534 dst
+= sprintf(dst
, "BOOTIF=%02x", MAC_type
);
536 for (i
= MAC_len
; i
; i
--)
537 dst
+= sprintf(dst
, "-%02x", *src
++);
539 sysappend_strings
[SYSAPPEND_BOOTIF
] = bootif_str
;
543 * Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
544 * option into IPOption based on a DHCP packet in trackbuf.
547 static void genipopt(void)
549 static char ip_option
[3+4*16];
550 const uint32_t *v
= &IPInfo
.myip
;
554 p
= stpcpy(ip_option
, "ip=");
556 for (i
= 0; i
< 4; i
++) {
557 p
+= gendotquad(p
, *v
++);
562 sysappend_strings
[SYSAPPEND_IP
] = ip_option
;
566 /* Generate ip= option and print the ip adress */
567 static void ip_init(void)
569 uint32_t ip
= IPInfo
.myip
;
570 char dot_quad_buf
[16];
573 gendotquad(dot_quad_buf
, ip
);
576 printf("My IP address seems to be %08X %s\n", ip
, dot_quad_buf
);
580 * Validity check on possible !PXE structure in buf
581 * return 1 for success, 0 for failure.
584 static int is_pxe(const void *buf
)
586 const struct pxe_t
*pxe
= buf
;
587 const uint8_t *p
= buf
;
588 int i
= pxe
->structlength
;
591 if (i
< sizeof(struct pxe_t
) ||
592 memcmp(pxe
->signature
, "!PXE", 4))
602 * Just like is_pxe, it checks PXENV+ structure
605 static int is_pxenv(const void *buf
)
607 const struct pxenv_t
*pxenv
= buf
;
608 const uint8_t *p
= buf
;
609 int i
= pxenv
->length
;
612 /* The pxeptr field isn't present in old versions */
613 if (i
< offsetof(struct pxenv_t
, pxeptr
) ||
614 memcmp(pxenv
->signature
, "PXENV+", 6))
626 * memory_scan_for_pxe_struct:
627 * memory_scan_for_pxenv_struct:
629 * If none of the standard methods find the !PXE/PXENV+ structure,
630 * look for it by scanning memory.
632 * return the corresponding pxe structure if found, or NULL;
634 static const void *memory_scan(uintptr_t start
, int (*func
)(const void *))
638 /* Scan each 16 bytes of conventional memory before the VGA region */
639 for (ptr
= (const char *)start
; ptr
< (const char *)0xA0000; ptr
+= 16) {
641 return ptr
; /* found it! */
647 static const struct pxe_t
*memory_scan_for_pxe_struct(void)
649 extern uint16_t BIOS_fbm
; /* Starting segment */
651 return memory_scan(BIOS_fbm
<< 10, is_pxe
);
654 static const struct pxenv_t
*memory_scan_for_pxenv_struct(void)
656 return memory_scan(0x10000, is_pxenv
);
660 * Find the !PXE structure; we search for the following, in order:
662 * a. !PXE structure as SS:[SP + 4]
663 * b. PXENV+ structure at [ES:BX]
664 * c. INT 1Ah AX=0x5650 -> PXENV+
665 * d. Search memory for !PXE
666 * e. Search memory for PXENV+
668 * If we find a PXENV+ structure, we try to find a !PXE structure from
669 * if if the API version is 2.1 or later
672 static int pxe_init(bool quiet
)
674 extern void pxe_int1a(void);
677 uint16_t code_seg
, code_len
;
678 uint16_t data_seg
, data_len
;
679 const char *base
= GET_PTR(InitStack
);
682 const struct pxenv_t
*pxenv
;
683 const struct pxe_t
*pxe
;
685 /* Assume API version 2.1 */
688 /* Plan A: !PXE structure as SS:[SP + 4] */
689 off
= *(const uint16_t *)(base
+ 48);
690 seg
= *(const uint16_t *)(base
+ 50);
691 pxe
= MK_PTR(seg
, off
);
695 /* Plan B: PXENV+ structure at [ES:BX] */
697 off
= *(const uint16_t *)(base
+ 24); /* Original BX */
698 seg
= *(const uint16_t *)(base
+ 4); /* Original ES */
699 pxenv
= MK_PTR(seg
, off
);
703 /* Plan C: PXENV+ structure via INT 1Ah AX=5650h */
705 memset(®s
, 0, sizeof regs
);
706 regs
.eax
.w
[0] = 0x5650;
707 call16(pxe_int1a
, ®s
, ®s
);
708 if (!(regs
.eflags
.l
& EFLAGS_CF
) && (regs
.eax
.w
[0] == 0x564e)) {
709 pxenv
= MK_PTR(regs
.es
, regs
.ebx
.w
[0]);
714 /* Plan D: !PXE memory scan */
716 if ((pxe
= memory_scan_for_pxe_struct()))
719 /* Plan E: PXENV+ memory scan */
721 if ((pxenv
= memory_scan_for_pxenv_struct()))
724 /* Found nothing at all !! */
726 printf("No !PXE or PXENV+ API found; we're dead...\n");
730 APIVer
= pxenv
->version
;
732 printf("Found PXENV+ structure\nPXE API version is %04x\n", APIVer
);
734 /* if the API version number is 0x0201 or higher, use the !PXE structure */
735 if (APIVer
>= 0x201) {
736 if (pxenv
->length
>= sizeof(struct pxenv_t
)) {
737 pxe
= GET_PTR(pxenv
->pxeptr
);
741 * Nope, !PXE structure missing despite API 2.1+, or at least
742 * the pointer is missing. Do a last-ditch attempt to find it
744 if ((pxe
= memory_scan_for_pxe_struct()))
747 APIVer
= 0x200; /* PXENV+ only, assume version 2.00 */
750 /* Otherwise, no dice, use PXENV+ structure */
751 data_len
= pxenv
->undidatasize
;
752 data_seg
= pxenv
->undidataseg
;
753 code_len
= pxenv
->undicodesize
;
754 code_seg
= pxenv
->undicodeseg
;
755 PXEEntry
= pxenv
->rmentry
;
757 goto have_entrypoint
;
760 data_len
= pxe
->seg
[PXE_Seg_UNDIData
].size
;
761 data_seg
= pxe
->seg
[PXE_Seg_UNDIData
].sel
;
762 code_len
= pxe
->seg
[PXE_Seg_UNDICode
].size
;
763 code_seg
= pxe
->seg
[PXE_Seg_UNDICode
].sel
;
764 PXEEntry
= pxe
->entrypointsp
;
769 printf("%s entry point found (we hope) at %04X:%04X via plan %c\n",
770 type
, PXEEntry
.seg
, PXEEntry
.offs
, plan
);
771 printf("UNDI code segment at %04X len %04X\n", code_seg
, code_len
);
772 printf("UNDI data segment at %04X len %04X\n", data_seg
, data_len
);
775 code_seg
= code_seg
+ ((code_len
+ 15) >> 4);
776 data_seg
= data_seg
+ ((data_len
+ 15) >> 4);
778 real_base_mem
= max(code_seg
, data_seg
) >> 6; /* Convert to kilobytes */
780 /* Probe UNDI information */
781 pxe_call(PXENV_UNDI_GET_INFORMATION
, &pxe_undi_info
);
782 pxe_call(PXENV_UNDI_GET_IFACE_INFO
, &pxe_undi_iface
);
784 printf("UNDI: baseio %04x int %d MTU %d type %d \"%s\" flags 0x%x\n",
785 pxe_undi_info
.BaseIo
, pxe_undi_info
.IntNumber
,
786 pxe_undi_info
.MaxTranUnit
, pxe_undi_info
.HwType
,
787 pxe_undi_iface
.IfaceType
, pxe_undi_iface
.ServiceFlags
);
793 * See if we have gPXE
795 static void gpxe_init(void)
798 static __lowmem
struct s_PXENV_FILE_API_CHECK api_check
;
800 if (APIVer
>= 0x201) {
801 api_check
.Size
= sizeof api_check
;
802 api_check
.Magic
= 0x91d447b2;
803 err
= pxe_call(PXENV_FILE_API_CHECK
, &api_check
);
804 if (!err
&& api_check
.Magic
== 0xe9c17b20)
805 gpxe_funcs
= api_check
.APIMask
;
808 /* Necessary functions for us to use the gPXE file API */
809 has_gpxe
= (~gpxe_funcs
& 0x4b) == 0;
813 * Network-specific initialization
815 static void network_init(void)
817 struct bootp_t
*bp
= (struct bootp_t
*)trackbuf
;
822 *LocalDomain
= 0; /* No LocalDomain received */
825 * Get the DHCP client identifiers (query info 1)
827 printf("Getting cached packet ");
828 pkt_len
= pxe_get_cached_info(1);
831 * We don't use flags from the request packet, so
832 * this is a good time to initialize DHCPMagic...
833 * Initialize it to 1 meaning we will accept options found;
834 * in earlier versions of PXELINUX bit 0 was used to indicate
835 * we have found option 208 with the appropriate magic number;
836 * we no longer require that, but MAY want to re-introduce
837 * it in the future for vendor encapsulated options.
839 *(char *)&DHCPMagic
= 1;
842 * Get the BOOTP/DHCP packet that brought us file (and an IP
843 * address). This lives in the DHCPACK packet (query info 2)
845 pkt_len
= pxe_get_cached_info(2);
848 * Save away MAC address (assume this is in query info 2. If this
849 * turns out to be problematic it might be better getting it from
850 * the query info 1 packet
852 MAC_len
= bp
->hardlen
> 16 ? 0 : bp
->hardlen
;
853 MAC_type
= bp
->hardware
;
854 memcpy(MAC
, bp
->macaddr
, MAC_len
);
857 * Get the boot file and other info. This lives in the CACHED_REPLY
858 * packet (query info 3)
860 pkt_len
= pxe_get_cached_info(3);
864 make_bootif_string();
865 /* If DMI and DHCP disagree, which one should we set? */
867 sysappend_set_uuid(uuid
);
869 /* print_sysappend(); */
873 * Check to see if we got any PXELINUX-specific DHCP options; in particular,
874 * if we didn't get the magic enable, do not recognize any other options.
876 if ((DHCPMagic
& 1) == 0)
879 /* Initialize lwip */
880 tcpip_init(NULL
, NULL
);
882 /* Start up the undi driver for lwip */
883 err
= undiif_start(IPInfo
.myip
, IPInfo
.netmask
, IPInfo
.gateway
);
885 printf("undiif driver failed to start: %d\n", err
);
889 for (i
= 0; i
< DNS_MAX_SERVERS
; i
++) {
890 /* Transfer the DNS information to lwip */
891 dns_setserver(i
, (struct ip_addr
*)&dns_server
[i
]);
899 static int pxe_fs_init(struct fs_info
*fs
)
901 (void)fs
; /* drop the compile warning message */
903 /* Prepare for handling pxe interrupts */
906 /* This block size is actually arbitrary... */
907 fs
->sector_shift
= fs
->block_shift
= TFTP_BLOCKSIZE_LG2
;
908 fs
->sector_size
= fs
->block_size
= 1 << TFTP_BLOCKSIZE_LG2
;
910 /* Find the PXE stack */
914 /* See if we also have a gPXE stack */
917 /* Network-specific initialization */
920 /* Initialize network-card-specific idle handling */
923 /* Our name for the root */
924 strcpy(fs
->cwd_name
, "::");
930 * Look to see if we are on an EFI CSM system. Some EFI
931 * CSM systems put the BEV stack in low memory, which means
932 * a return to the PXE stack will crash the system. However,
933 * INT 18h works reliably, so in that case hack the stack and
934 * point the "return address" to an INT 18h instruction.
936 * Hack the stack instead of the much simpler "just invoke INT 18h
937 * if we want to reset", so that chainloading other NBPs will work.
939 * This manipulates the real-mode InitStack directly. It relies on this
940 * *not* being a currently active stack, i.e. the former
941 * USE_PXE_PROVIDED_STACK no longer works.
943 * XXX: Disable this until we can find a better way to discriminate
944 * between BIOSes that are broken on BEV return and BIOSes which are
945 * broken on INT 18h. Keying on the EFI CSM turns out to cause more
946 * problems than it solves.
948 extern far_ptr_t InitStack
;
954 } __attribute__((packed
));
955 #define EFI_MAGIC (('$' << 24)+('E' << 16)+('F' << 8)+'I')
957 static inline bool is_efi(const struct efi_struct
*efi
)
960 * We don't verify the checksum, because it seems some CSMs leave
961 * it at zero, sigh...
963 return (efi
->magic
== EFI_MAGIC
) && (efi
->len
>= 83);
967 static void install_int18_hack(void)
969 static const uint8_t int18_hack
[] =
971 0xcd, 0x18, /* int $0x18 */
972 0xea, 0xf0, 0xff, 0x00, 0xf0, /* ljmpw $0xf000,$0xfff0 */
977 retcode
= GET_PTR(*(far_ptr_t
*)((char *)GET_PTR(InitStack
) + 44));
979 /* Don't do this if the return already points to int $0x18 */
980 if (*retcode
!= 0x18cd) {
984 for (efi_ptr
= 0xe0000 ; efi_ptr
< 0x100000 ; efi_ptr
+= 16) {
985 if (is_efi((const struct efi_struct
*)efi_ptr
)) {
992 uint8_t *src
= GET_PTR(InitStack
);
993 uint8_t *dst
= src
- sizeof int18_hack
;
995 memmove(dst
, src
, 52);
996 memcpy(dst
+52, int18_hack
, sizeof int18_hack
);
997 InitStack
.offs
-= sizeof int18_hack
;
999 /* Clobber the return address */
1000 *(uint16_t *)(dst
+44) = OFFS_WRT(dst
+52, InitStack
.seg
);
1001 *(uint16_t *)(dst
+46) = InitStack
.seg
;
1008 * This function unloads the PXE and UNDI stacks and
1009 * unclaims the memory.
1011 void unload_pxe(void)
1013 /* PXE unload sequences */
1016 * UNDI_SHUTDOWN, UNDI_CLEANUP, STOP_UNDI
1017 * Older Syslinux did:
1018 * UDP_CLOSE, UNDI_SHUTDOWN, UNLOAD_STACK, STOP_UNDI/UNDI_CLEANUP
1020 static const uint8_t new_api_unload
[] = {
1021 PXENV_UNDI_SHUTDOWN
, PXENV_UNLOAD_STACK
, PXENV_STOP_UNDI
, 0
1023 static const uint8_t old_api_unload
[] = {
1024 PXENV_UNDI_SHUTDOWN
, PXENV_UNLOAD_STACK
, PXENV_UNDI_CLEANUP
, 0
1028 const uint8_t *api_ptr
;
1031 static __lowmem
union {
1032 struct s_PXENV_UNDI_SHUTDOWN undi_shutdown
;
1033 struct s_PXENV_UNLOAD_STACK unload_stack
;
1034 struct s_PXENV_STOP_UNDI stop_undi
;
1035 struct s_PXENV_UNDI_CLEANUP undi_cleanup
;
1036 uint16_t Status
; /* All calls have this as the first member */
1039 dprintf("Called unload_pxe()...\n");
1040 dprintf("FBM before unload = %d\n", BIOS_fbm
);
1044 /* If we want to keep PXE around, we still need to reset it */
1048 dprintf("APIVer = %04x\n", APIVer
);
1050 api_ptr
= APIVer
>= 0x0200 ? new_api_unload
: old_api_unload
;
1051 while((api
= *api_ptr
++)) {
1052 dprintf("PXE call %04x\n", api
);
1053 memset(&unload_call
, 0, sizeof unload_call
);
1054 err
= pxe_call(api
, &unload_call
);
1055 if (err
|| unload_call
.Status
!= PXENV_STATUS_SUCCESS
) {
1056 printf("PXE unload API call %04x failed: 0x%x\n",
1057 api
, unload_call
.Status
);
1063 if (real_base_mem
<= BIOS_fbm
) { /* Sanity check */
1064 dprintf("FBM %d < real_base_mem %d\n", BIOS_fbm
, real_base_mem
);
1069 /* Check that PXE actually unhooked the INT 0x1A chain */
1070 int_addr
= (size_t)GET_PTR(*(far_ptr_t
*)(4 * 0x1a));
1072 if (int_addr
>= real_base_mem
|| int_addr
< BIOS_fbm
) {
1073 BIOS_fbm
= real_base_mem
;
1074 dprintf("FBM after unload_pxe = %d\n", BIOS_fbm
);
1078 dprintf("Can't free FBM, real_base_mem = %d, "
1079 "FBM = %d, INT 1A = %08x (%d)\n",
1080 real_base_mem
, BIOS_fbm
,
1081 *(uint32_t *)(4 * 0x1a), int_addr
);
1084 printf("Failed to free base memory error %04x-%08x (%d/%dK)\n",
1085 api
, *(uint32_t *)(4 * 0x1a), BIOS_fbm
, real_base_mem
);
1089 static int pxe_readdir(struct file
*file
, struct dirent
*dirent
)
1091 struct inode
*inode
= file
->inode
;
1092 struct pxe_pvt_inode
*socket
= PVT(inode
);
1094 if (socket
->ops
->readdir
)
1095 return socket
->ops
->readdir(inode
, dirent
);
1097 return -1; /* No such operation */
1100 const struct fs_ops pxe_fs_ops
= {
1102 .fs_flags
= FS_NODEV
,
1103 .fs_init
= pxe_fs_init
,
1104 .searchdir
= pxe_searchdir
,
1106 .realpath
= pxe_realpath
,
1107 .getfssec
= pxe_getfssec
,
1108 .close_file
= pxe_close_file
,
1109 .mangle_name
= pxe_mangle_name
,
1110 .load_config
= pxe_load_config
,
1111 .readdir
= pxe_readdir
,