14 __lowmem t_PXENV_UNDI_GET_INFORMATION pxe_undi_info
;
15 __lowmem t_PXENV_UNDI_GET_IFACE_INFO pxe_undi_iface
;
17 uint8_t MAC
[MAC_MAX
]; /* Actual MAC address */
18 uint8_t MAC_len
; /* MAC address len */
19 uint8_t MAC_type
; /* MAC address type */
21 char boot_file
[256]; /* From DHCP */
22 char path_prefix
[256]; /* From DHCP */
24 bool have_uuid
= false;
27 * Allocate a local UDP port structure and assign it a local port number.
28 * Return the inode pointer if success, or null if failure
30 static struct inode
*allocate_socket(struct fs_info
*fs
)
32 struct inode
*inode
= alloc_inode(fs
, 0, sizeof(struct pxe_pvt_inode
));
35 malloc_error("socket structure");
37 inode
->mode
= DT_REG
; /* No other types relevant for PXE */
43 void free_socket(struct inode
*inode
)
45 struct pxe_pvt_inode
*socket
= PVT(inode
);
47 free(socket
->tftp_pktbuf
); /* If we allocated a buffer, free it now */
51 static void pxe_close_file(struct file
*file
)
53 struct inode
*inode
= file
->inode
;
54 struct pxe_pvt_inode
*socket
= PVT(inode
);
59 if (!socket
->tftp_goteof
) {
60 socket
->ops
->close(inode
);
67 * Tests an IP address in _ip_ for validity; return with 0 for bad, 1 for good.
68 * We used to refuse class E, but class E addresses are likely to become
69 * assignable unicast addresses in the near future.
72 bool ip_ok(uint32_t ip
)
74 uint8_t ip_hi
= (uint8_t)ip
; /* First octet of the ip address */
76 if (ip
== 0xffffffff || /* Refuse the all-ones address */
77 ip_hi
== 0 || /* Refuse network zero */
78 ip_hi
== 127 || /* Refuse the loopback network */
79 (ip_hi
& 240) == 224) /* Refuse class D */
87 * Take an IP address (in network byte order) in _ip_ and
88 * output a dotted quad string to _dst_, returns the length
89 * of the dotted quad ip string.
92 static int gendotquad(char *dst
, uint32_t ip
)
94 return sprintf(dst
, "%u.%u.%u.%u",
95 ((const uint8_t *)&ip
)[0],
96 ((const uint8_t *)&ip
)[1],
97 ((const uint8_t *)&ip
)[2],
98 ((const uint8_t *)&ip
)[3]);
102 * mangle a filename pointed to by _src_ into a buffer pointed
103 * to by _dst_; ends on encountering any whitespace.
105 * This deliberately does not attempt to do any conversion of
106 * pathname separators.
109 static void pxe_mangle_name(char *dst
, const char *src
)
111 size_t len
= FILENAME_MAX
-1;
113 while (len
-- && not_whitespace(*src
))
120 * Read a single character from the specified pxe inode.
121 * Very useful for stepping through http streams and
122 * parsing their headers.
124 int pxe_getc(struct inode
*inode
)
126 struct pxe_pvt_inode
*socket
= PVT(inode
);
129 while (!socket
->tftp_bytesleft
) {
130 if (socket
->tftp_goteof
)
133 socket
->ops
->fill_buffer(inode
);
136 byte
= *socket
->tftp_dataptr
;
137 socket
->tftp_bytesleft
-= 1;
138 socket
->tftp_dataptr
+= 1;
144 * Get a fresh packet if the buffer is drained, and we haven't hit
145 * EOF yet. The buffer should be filled immediately after draining!
147 static void fill_buffer(struct inode
*inode
)
149 struct pxe_pvt_inode
*socket
= PVT(inode
);
150 if (socket
->tftp_bytesleft
|| socket
->tftp_goteof
)
153 return socket
->ops
->fill_buffer(inode
);
158 * getfssec: Get multiple clusters from a file, given the starting cluster.
159 * In this case, get multiple blocks from a specific TCP connection.
161 * @param: fs, the fs_info structure address, in pxe, we don't use this.
162 * @param: buf, buffer to store the read data
163 * @param: openfile, TFTP socket pointer
164 * @param: blocks, 512-byte block count; 0FFFFh = until end of file
166 * @return: the bytes read
169 static uint32_t pxe_getfssec(struct file
*file
, char *buf
,
170 int blocks
, bool *have_more
)
172 struct inode
*inode
= file
->inode
;
173 struct pxe_pvt_inode
*socket
= PVT(inode
);
178 count
<<= TFTP_BLOCKSIZE_LG2
;
180 fill_buffer(inode
); /* If we have no 'fresh' buffer, get it */
181 if (!socket
->tftp_bytesleft
)
185 if (chunk
> socket
->tftp_bytesleft
)
186 chunk
= socket
->tftp_bytesleft
;
187 socket
->tftp_bytesleft
-= chunk
;
188 memcpy(buf
, socket
->tftp_dataptr
, chunk
);
189 socket
->tftp_dataptr
+= chunk
;
196 if (socket
->tftp_bytesleft
|| (socket
->tftp_filepos
< inode
->size
)) {
199 } else if (socket
->tftp_goteof
) {
201 * The socket is closed and the buffer drained; the caller will
202 * call close_file and therefore free the socket.
211 * Assign an IP address to a URL
213 __export
int url_set_ip(struct url_info
*url
)
215 int err
= -ntohs(TFTP_OK
);
218 if (url
->host
&& url
->host
[0]) {
219 url
->ip
= pxe_dns(url
->host
);
221 err
= -ntohs(TFTP_ERESOLVE
);
224 /* Note: default to the server IP on resolve failure */
226 url
->ip
= IPInfo
.serverip
;
228 err
= -ntohs(TFTP_NONETWORK
);
235 * Open the specified connection
237 * @param:filename, the file we wanna open
239 * @out: open_file_t structure, stores in file->open_file
240 * @out: the lenght of this file, stores in file->file_len
243 static void __pxe_searchdir(const char *filename
, int flags
, struct file
*file
);
244 extern uint16_t PXERetry
;
246 static void pxe_searchdir(const char *filename
, int flags
, struct file
*file
)
251 dprintf("PXE: file = %p, retries left = %d: ", file
, i
);
252 __pxe_searchdir(filename
, flags
, file
);
253 dprintf("%s\n", file
->inode
? "ok" : "failed");
254 } while (!file
->inode
&& i
--);
256 static void __pxe_searchdir(const char *filename
, int flags
, struct file
*file
)
258 struct fs_info
*fs
= file
->fs
;
260 char fullpath
[2*FILENAME_MAX
];
262 char urlsave
[2*FILENAME_MAX
];
265 const struct url_scheme
*us
= NULL
;
266 int redirect_count
= 0;
267 bool found_scheme
= false;
269 inode
= file
->inode
= NULL
;
272 if (redirect_count
++ > 5)
275 strlcpy(fullpath
, filename
, sizeof fullpath
);
277 strcpy(urlsave
, fullpath
);
279 parse_url(&url
, fullpath
);
280 if (url
.type
== URL_SUFFIX
) {
281 snprintf(fullpath
, sizeof fullpath
, "%s%s", fs
->cwd_name
, filename
);
283 strcpy(urlsave
, fullpath
);
285 parse_url(&url
, fullpath
);
288 inode
= allocate_socket(fs
);
290 return; /* Allocation failure */
295 found_scheme
= false;
296 for (us
= url_schemes
; us
->name
; us
++) {
297 if (!strcmp(us
->name
, url
.scheme
)) {
298 if ((flags
& ~us
->ok_flags
& OK_FLAGS_MASK
) == 0)
299 us
->open(&url
, flags
, inode
, &filename
);
305 /* filename here is set on a redirect */
310 /* No URL scheme found, hand it to GPXE */
311 gpxe_open(inode
, urlsave
);
317 file
->inode
->mode
= (flags
& O_DIRECTORY
) ? DT_DIR
: DT_REG
;
327 * Store standard filename prefix
329 static void get_prefix(void)
335 if (!(DHCPMagic
& 0x04)) {
336 /* No path prefix option, derive from boot file */
338 strlcpy(path_prefix
, boot_file
, sizeof path_prefix
);
339 len
= strlen(path_prefix
);
340 p
= &path_prefix
[len
- 1];
346 c
= (c
>= '0' && c
<= '9') ||
347 (c
>= 'a' && c
<= 'z') ||
348 (c
== '.' || c
== '-');
356 *(p
+ 2) = 0; /* Zero-terminate after delimiter */
359 ddprintf("TFTP prefix: %s\n", path_prefix
);
361 if (url_type(path_prefix
) == URL_SUFFIX
) {
363 * Construct a ::-style TFTP path.
365 * We may have moved out of the root directory at the time
366 * this function is invoked, but to maintain compatibility
367 * with versions of Syslinux < 5.00, path_prefix must be
370 p
= strdup(path_prefix
);
374 snprintf(path_prefix
, sizeof path_prefix
, "::%s", p
);
384 static size_t pxe_realpath(struct fs_info
*fs
, char *dst
, const char *src
,
387 return snprintf(dst
, bufsize
, "%s%s",
388 url_type(src
) == URL_SUFFIX
? fs
->cwd_name
: "", src
);
394 static int pxe_chdir(struct fs_info
*fs
, const char *src
)
396 /* The cwd for PXE is just a text prefix */
397 enum url_type path_type
= url_type(src
);
399 if (path_type
== URL_SUFFIX
)
400 strlcat(fs
->cwd_name
, src
, sizeof fs
->cwd_name
);
402 strlcpy(fs
->cwd_name
, src
, sizeof fs
->cwd_name
);
405 dprintf("cwd = \"%s\"\n", fs
->cwd_name
);
409 static int pxe_chdir_start(void)
415 /* Load the config file, return -1 if failed, or 0 */
416 static int pxe_open_config(struct com32_filedata
*filedata
)
418 const char *cfgprefix
= "pxelinux.cfg/";
419 const char *default_str
= "default";
425 if (DHCPMagic
& 0x02) {
426 /* We got a DHCP option, try it first */
427 if (open_file(ConfigName
, O_RDONLY
, filedata
) >= 0)
432 * Have to guess config file name ...
434 config_file
= stpcpy(ConfigName
, cfgprefix
);
436 /* Try loading by UUID */
437 if (sysappend_strings
[SYSAPPEND_SYSUUID
]) {
438 strcpy(config_file
, sysappend_strings
[SYSAPPEND_SYSUUID
]+8);
439 if (open_file(ConfigName
, O_RDONLY
, filedata
) >= 0)
443 /* Try loading by MAC address */
444 strcpy(config_file
, sysappend_strings
[SYSAPPEND_BOOTIF
]+7);
445 if (open_file(ConfigName
, O_RDONLY
, filedata
) >= 0)
448 /* Nope, try hexadecimal IP prefixes... */
449 sprintf(config_file
, "%08X", ntohl(IPInfo
.myip
));
450 last
= &config_file
[8];
452 *last
= '\0'; /* Zero-terminate string */
453 if (open_file(ConfigName
, O_RDONLY
, filedata
) >= 0)
455 last
--; /* Drop one character */
459 /* Final attempt: "default" string */
460 strcpy(config_file
, default_str
);
461 if (open_file(ConfigName
, O_RDONLY
, filedata
) >= 0)
464 ddprintf("%-68s\n", "Unable to locate configuration file");
469 * Generate the bootif string.
471 static void make_bootif_string(void)
473 static char bootif_str
[7+3*(MAC_MAX
+1)];
475 char *dst
= bootif_str
;
478 dst
+= sprintf(dst
, "BOOTIF=%02x", MAC_type
);
480 for (i
= MAC_len
; i
; i
--)
481 dst
+= sprintf(dst
, "-%02x", *src
++);
483 sysappend_strings
[SYSAPPEND_BOOTIF
] = bootif_str
;
487 * Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
488 * option into IPOption based on DHCP information in IPInfo.
491 static void genipopt(void)
493 static char ip_option
[3+4*16];
494 const uint32_t *v
= &IPInfo
.myip
;
498 p
= stpcpy(ip_option
, "ip=");
500 for (i
= 0; i
< 4; i
++) {
501 p
+= gendotquad(p
, *v
++);
506 sysappend_strings
[SYSAPPEND_IP
] = ip_option
;
510 /* Generate ip= option and print the ip adress */
511 static void ip_init(void)
513 uint32_t ip
= IPInfo
.myip
;
514 char dot_quad_buf
[16];
517 gendotquad(dot_quad_buf
, ip
);
520 ddprintf("My IP address seems to be %08X %s\n", ip
, dot_quad_buf
);
524 * Network-specific initialization
526 static void network_init(void)
530 make_bootif_string();
531 /* If DMI and DHCP disagree, which one should we set? */
533 sysappend_set_uuid(uuid
);
536 /* print_sysappend(); */
538 * Check to see if we got any PXELINUX-specific DHCP options; in particular,
539 * if we didn't get the magic enable, do not recognize any other options.
541 if ((DHCPMagic
& 1) == 0)
551 static int pxe_fs_init(struct fs_info
*fs
)
553 (void)fs
; /* drop the compile warning message */
555 /* Prepare for handling pxe interrupts */
558 /* This block size is actually arbitrary... */
559 fs
->sector_shift
= fs
->block_shift
= TFTP_BLOCKSIZE_LG2
;
560 fs
->sector_size
= fs
->block_size
= 1 << TFTP_BLOCKSIZE_LG2
;
562 /* Find the PXE stack */
566 /* See if we also have a gPXE stack */
569 /* Network-specific initialization */
572 /* Initialize network-card-specific idle handling */
575 /* Our name for the root */
576 strcpy(fs
->cwd_name
, "::");
582 * Look to see if we are on an EFI CSM system. Some EFI
583 * CSM systems put the BEV stack in low memory, which means
584 * a return to the PXE stack will crash the system. However,
585 * INT 18h works reliably, so in that case hack the stack and
586 * point the "return address" to an INT 18h instruction.
588 * Hack the stack instead of the much simpler "just invoke INT 18h
589 * if we want to reset", so that chainloading other NBPs will work.
591 * This manipulates the real-mode InitStack directly. It relies on this
592 * *not* being a currently active stack, i.e. the former
593 * USE_PXE_PROVIDED_STACK no longer works.
595 * XXX: Disable this until we can find a better way to discriminate
596 * between BIOSes that are broken on BEV return and BIOSes which are
597 * broken on INT 18h. Keying on the EFI CSM turns out to cause more
598 * problems than it solves.
600 extern far_ptr_t InitStack
;
606 } __attribute__((packed
));
607 #define EFI_MAGIC (('$' << 24)+('E' << 16)+('F' << 8)+'I')
609 static inline bool is_efi(const struct efi_struct
*efi
)
612 * We don't verify the checksum, because it seems some CSMs leave
613 * it at zero, sigh...
615 return (efi
->magic
== EFI_MAGIC
) && (efi
->len
>= 83);
619 static void install_int18_hack(void)
621 static const uint8_t int18_hack
[] =
623 0xcd, 0x18, /* int $0x18 */
624 0xea, 0xf0, 0xff, 0x00, 0xf0, /* ljmpw $0xf000,$0xfff0 */
629 retcode
= GET_PTR(*(far_ptr_t
*)((char *)GET_PTR(InitStack
) + 44));
631 /* Don't do this if the return already points to int $0x18 */
632 if (*retcode
!= 0x18cd) {
636 for (efi_ptr
= 0xe0000 ; efi_ptr
< 0x100000 ; efi_ptr
+= 16) {
637 if (is_efi((const struct efi_struct
*)efi_ptr
)) {
644 uint8_t *src
= GET_PTR(InitStack
);
645 uint8_t *dst
= src
- sizeof int18_hack
;
647 memmove(dst
, src
, 52);
648 memcpy(dst
+52, int18_hack
, sizeof int18_hack
);
649 InitStack
.offs
-= sizeof int18_hack
;
651 /* Clobber the return address */
652 *(uint16_t *)(dst
+44) = OFFS_WRT(dst
+52, InitStack
.seg
);
653 *(uint16_t *)(dst
+46) = InitStack
.seg
;
659 static int pxe_readdir(struct file
*file
, struct dirent
*dirent
)
661 struct inode
*inode
= file
->inode
;
662 struct pxe_pvt_inode
*socket
= PVT(inode
);
664 if (socket
->ops
->readdir
)
665 return socket
->ops
->readdir(inode
, dirent
);
667 return -1; /* No such operation */
670 const struct fs_ops pxe_fs_ops
= {
672 .fs_flags
= FS_NODEV
,
673 .fs_init
= pxe_fs_init
,
674 .searchdir
= pxe_searchdir
,
676 .realpath
= pxe_realpath
,
677 .getfssec
= pxe_getfssec
,
678 .close_file
= pxe_close_file
,
679 .mangle_name
= pxe_mangle_name
,
680 .chdir_start
= pxe_chdir_start
,
681 .open_config
= pxe_open_config
,
682 .readdir
= pxe_readdir
,