13 static uint16_t real_base_mem
; /* Amount of DOS memory after freeing */
15 uint8_t MAC
[MAC_MAX
]; /* Actual MAC address */
16 uint8_t MAC_len
; /* MAC address len */
17 uint8_t MAC_type
; /* MAC address type */
19 char __bss16 BOOTIFStr
[7+3*(MAC_MAX
+1)];
20 #define MAC_str (BOOTIFStr+7) /* The actual hardware address */
21 char __bss16 SYSUUIDStr
[8+32+5];
22 #define UUID_str (SYSUUIDStr+8) /* The actual UUID */
24 char boot_file
[256]; /* From DHCP */
25 char path_prefix
[256]; /* From DHCP */
26 char dot_quad_buf
[16];
29 static uint32_t gpxe_funcs
;
30 bool have_uuid
= false;
32 /* Common receive buffer */
33 static __lowmem
char packet_buf
[PKTBUF_SIZE
] __aligned(16);
35 const uint8_t TimeoutTable
[] = {
36 2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18, 21, 26, 31, 37, 44,
37 53, 64, 77, 92, 110, 132, 159, 191, 229, 255, 255, 255, 255, 0
41 const char *str_ptr
; /* string pointer */
42 size_t offset
; /* offset into socket structre */
45 #define IFIELD(x) offsetof(struct inode, x)
46 #define PFIELD(x) (offsetof(struct inode, pvt) + \
47 offsetof(struct pxe_pvt_inode, x))
49 static const struct tftp_options tftp_options
[] =
51 { "tsize", IFIELD(size
) },
52 { "blksize", PFIELD(tftp_blksize
) },
54 static const int tftp_nopts
= sizeof tftp_options
/ sizeof tftp_options
[0];
56 static void tftp_error(struct inode
*file
, uint16_t errnum
,
60 * Allocate a local UDP port structure and assign it a local port number.
61 * Return the inode pointer if success, or null if failure
63 static struct inode
*allocate_socket(struct fs_info
*fs
)
65 struct inode
*inode
= alloc_inode(fs
, 0, sizeof(struct pxe_pvt_inode
));
68 malloc_error("socket structure");
70 struct pxe_pvt_inode
*socket
= PVT(inode
);
71 socket
->tftp_localport
= get_port();
72 inode
->mode
= DT_REG
; /* No other types relevant for PXE */
78 static void free_socket(struct inode
*inode
)
80 struct pxe_pvt_inode
*socket
= PVT(inode
);
82 free_port(socket
->tftp_localport
);
87 static void gpxe_close_file(struct inode
*inode
)
89 struct pxe_pvt_inode
*socket
= PVT(inode
);
90 static __lowmem
struct s_PXENV_FILE_CLOSE file_close
;
92 file_close
.FileHandle
= socket
->tftp_remoteport
;
93 pxe_call(PXENV_FILE_CLOSE
, &file_close
);
97 static void pxe_close_file(struct file
*file
)
99 struct inode
*inode
= file
->inode
;
100 struct pxe_pvt_inode
*socket
= PVT(inode
);
102 if (!socket
->tftp_goteof
) {
104 if (socket
->tftp_localport
== 0xffff) {
105 gpxe_close_file(inode
);
108 if (socket
->tftp_localport
!= 0) {
109 tftp_error(inode
, 0, "No error, file close");
117 * Take a nubmer of bytes in memory and convert to lower-case hxeadecimal
119 * @param: dst, output buffer
120 * @param: src, input buffer
121 * @param: count, number of bytes
124 static void lchexbytes(char *dst
, const void *src
, int count
)
128 const uint8_t *s
= src
;
130 for(; count
> 0; count
--) {
132 half
= ((c
>> 4) & 0x0f) + '0';
133 *dst
++ = half
> '9' ? (half
+ 'a' - '9' - 1) : half
;
135 half
= (c
& 0x0f) + '0';
136 *dst
++ = half
> '9' ? (half
+ 'a' - '9' - 1) : half
;
141 * just like the lchexbytes, except to upper-case
144 static void uchexbytes(char *dst
, const void *src
, int count
)
148 const uint8_t *s
= src
;
150 for(; count
> 0; count
--) {
152 half
= ((c
>> 4) & 0x0f) + '0';
153 *dst
++ = half
> '9' ? (half
+ 'A' - '9' - 1) : half
;
155 half
= (c
& 0x0f) + '0';
156 *dst
++ = half
> '9' ? (half
+ 'A' - '9' - 1) : half
;
161 * Parse a single hexadecimal byte, which must be complete (two
162 * digits). This is used in URL parsing.
164 static int hexbyte(const char *p
)
166 if (!is_hex(p
[0]) || !is_hex(p
[1]))
169 return (hexval(p
[0]) << 4) + hexval(p
[1]);
173 * Tests an IP address in _ip_ for validity; return with 0 for bad, 1 for good.
174 * We used to refuse class E, but class E addresses are likely to become
175 * assignable unicast addresses in the near future.
178 bool ip_ok(uint32_t ip
)
180 uint8_t ip_hi
= (uint8_t)ip
; /* First octet of the ip address */
182 if (ip
== 0xffffffff || /* Refuse the all-ones address */
183 ip_hi
== 0 || /* Refuse network zero */
184 ip_hi
== 127 || /* Refuse the loopback network */
185 (ip_hi
& 240) == 224) /* Refuse class D */
193 * Take an IP address (in network byte order) in _ip_ and
194 * output a dotted quad string to _dst_, returns the length
195 * of the dotted quad ip string.
198 static int gendotquad(char *dst
, uint32_t ip
)
209 temp
[j
++] = (part
% 10) + '0';
217 /* drop the last dot '.' and zero-terminate string*/
224 * parse the ip_str and return the ip address with *res.
225 * return the the string address after the ip string
228 static const char *parse_dotquad(const char *ip_str
, uint32_t *res
)
230 const char *p
= ip_str
;
235 for (i
= 0; i
< 4; i
++) {
236 while (is_digit(*p
)) {
237 part
= part
* 10 + *p
- '0';
240 if (i
!= 3 && *p
!= '.')
243 ip
= (ip
<< 8) | part
;
254 * the ASM pxenv function wrapper, return 1 if error, or 0
257 __export
int pxe_call(int opcode
, void *data
)
259 extern void pxenv(void);
263 printf("pxe_call op %04x data %p\n", opcode
, data
);
266 memset(®s
, 0, sizeof regs
);
267 regs
.ebx
.w
[0] = opcode
;
269 regs
.edi
.w
[0] = OFFS(data
);
270 call16(pxenv
, ®s
, ®s
);
272 return regs
.eflags
.l
& EFLAGS_CF
; /* CF SET if fail */
276 * Send an ERROR packet. This is used to terminate a connection.
278 * @inode: Inode structure
279 * @errnum: Error number (network byte order)
280 * @errstr: Error string (included in packet)
282 static void tftp_error(struct inode
*inode
, uint16_t errnum
,
285 static __lowmem
struct {
290 static __lowmem
struct s_PXENV_UDP_WRITE udp_write
;
291 int len
= min(strlen(errstr
), sizeof(err_buf
.err_msg
)-1);
292 struct pxe_pvt_inode
*socket
= PVT(inode
);
294 err_buf
.err_op
= TFTP_ERROR
;
295 err_buf
.err_num
= errnum
;
296 memcpy(err_buf
.err_msg
, errstr
, len
);
297 err_buf
.err_msg
[len
] = '\0';
299 udp_write
.src_port
= socket
->tftp_localport
;
300 udp_write
.dst_port
= socket
->tftp_remoteport
;
301 udp_write
.ip
= socket
->tftp_remoteip
;
302 udp_write
.gw
= gateway(udp_write
.ip
);
303 udp_write
.buffer
= FAR_PTR(&err_buf
);
304 udp_write
.buffer_size
= 4 + len
+ 1;
306 /* If something goes wrong, there is nothing we can do, anyway... */
307 pxe_call(PXENV_UDP_WRITE
, &udp_write
);
312 * Send ACK packet. This is a common operation and so is worth canning.
314 * @param: inode, Inode pointer
315 * @param: ack_num, Packet # to ack (network byte order)
318 static void ack_packet(struct inode
*inode
, uint16_t ack_num
)
321 static __lowmem
uint16_t ack_packet_buf
[2];
322 static __lowmem
struct s_PXENV_UDP_WRITE udp_write
;
323 struct pxe_pvt_inode
*socket
= PVT(inode
);
325 /* Packet number to ack */
326 ack_packet_buf
[0] = TFTP_ACK
;
327 ack_packet_buf
[1] = ack_num
;
328 udp_write
.src_port
= socket
->tftp_localport
;
329 udp_write
.dst_port
= socket
->tftp_remoteport
;
330 udp_write
.ip
= socket
->tftp_remoteip
;
331 udp_write
.gw
= gateway(udp_write
.ip
);
332 udp_write
.buffer
= FAR_PTR(ack_packet_buf
);
333 udp_write
.buffer_size
= 4;
335 err
= pxe_call(PXENV_UDP_WRITE
, &udp_write
);
338 printf("sent %s\n", err
? "FAILED" : "OK");
344 * Get a DHCP packet from the PXE stack into a lowmem buffer
346 * @param: type, packet type
347 * @return: buffer size
350 static int pxe_get_cached_info(int type
, void *buf
, size_t bufsiz
)
353 static __lowmem
struct s_PXENV_GET_CACHED_INFO get_cached_info
;
354 printf(" %02x", type
);
356 get_cached_info
.Status
= 0;
357 get_cached_info
.PacketType
= type
;
358 get_cached_info
.BufferSize
= bufsiz
;
359 get_cached_info
.Buffer
= FAR_PTR(buf
);
360 err
= pxe_call(PXENV_GET_CACHED_INFO
, &get_cached_info
);
362 printf("PXE API call failed, error %04x\n", err
);
366 return get_cached_info
.BufferSize
;
371 * Return the type of pathname passed.
374 PXE_RELATIVE
, /* No :: or URL */
375 PXE_HOMESERVER
, /* Starting with :: */
376 PXE_TFTP
, /* host:: */
377 PXE_URL_TFTP
, /* tftp:// */
378 PXE_URL
, /* Absolute URL syntax */
381 static enum pxe_path_type
pxe_path_type(const char *str
)
392 return PXE_HOMESERVER
;
395 } else if (p
> str
&& p
[1] == '/' && p
[2] == '/') {
396 if (!strncasecmp(str
, "tftp://", 7))
402 /* else fall through */
403 case '/': case '!': case '@': case '#': case '%':
404 case '^': case '&': case '*': case '(': case ')':
405 case '[': case ']': case '{': case '}': case '\\':
406 case '|': case '=': case '`': case '~': case '\'':
407 case '\"': case ';': case '>': case '<': case '?':
409 /* Any of these characters terminate the colon search */
421 * Get a fresh packet from a gPXE socket
422 * @param: inode -> Inode pointer
425 static void get_packet_gpxe(struct inode
*inode
)
427 struct pxe_pvt_inode
*socket
= PVT(inode
);
428 static __lowmem
struct s_PXENV_FILE_READ file_read
;
432 file_read
.FileHandle
= socket
->tftp_remoteport
;
433 file_read
.Buffer
= FAR_PTR(packet_buf
);
434 file_read
.BufferSize
= PKTBUF_SIZE
;
435 err
= pxe_call(PXENV_FILE_READ
, &file_read
);
436 if (!err
) /* successed */
439 if (file_read
.Status
!= PXENV_STATUS_TFTP_OPEN
)
443 memcpy(socket
->tftp_pktbuf
, packet_buf
, file_read
.BufferSize
);
445 socket
->tftp_dataptr
= socket
->tftp_pktbuf
;
446 socket
->tftp_bytesleft
= file_read
.BufferSize
;
447 socket
->tftp_filepos
+= file_read
.BufferSize
;
449 if (socket
->tftp_bytesleft
== 0)
450 inode
->size
= socket
->tftp_filepos
;
452 /* if we're done here, close the file */
453 if (inode
->size
> socket
->tftp_filepos
)
456 /* Got EOF, close it */
457 socket
->tftp_goteof
= 1;
458 gpxe_close_file(inode
);
464 * mangle a filename pointed to by _src_ into a buffer pointed
465 * to by _dst_; ends on encountering any whitespace.
468 static void pxe_mangle_name(char *dst
, const char *src
)
470 size_t len
= FILENAME_MAX
-1;
472 while (len
-- && not_whitespace(*src
))
479 * Get a fresh packet if the buffer is drained, and we haven't hit
480 * EOF yet. The buffer should be filled immediately after draining!
482 static void fill_buffer(struct inode
*inode
)
486 const uint8_t *timeout_ptr
;
491 static __lowmem
struct s_PXENV_UDP_READ udp_read
;
492 struct pxe_pvt_inode
*socket
= PVT(inode
);
494 if (socket
->tftp_bytesleft
|| socket
->tftp_goteof
)
498 if (socket
->tftp_localport
== 0xffff) {
499 get_packet_gpxe(inode
);
505 * Start by ACKing the previous packet; this should cause
506 * the next packet to be sent.
508 timeout_ptr
= TimeoutTable
;
509 timeout
= *timeout_ptr
++;
513 ack_packet(inode
, socket
->tftp_lastpkt
);
516 udp_read
.buffer
= FAR_PTR(packet_buf
);
517 udp_read
.buffer_size
= PKTBUF_SIZE
;
518 udp_read
.src_ip
= socket
->tftp_remoteip
;
519 udp_read
.dest_ip
= IPInfo
.myip
;
520 udp_read
.s_port
= socket
->tftp_remoteport
;
521 udp_read
.d_port
= socket
->tftp_localport
;
522 err
= pxe_call(PXENV_UDP_READ
, &udp_read
);
524 uint32_t now
= jiffies();
526 if (now
-oldtime
>= timeout
) {
528 timeout
= *timeout_ptr
++;
535 if (udp_read
.buffer_size
< 4) /* Bad size for a DATA packet */
539 if (*(uint16_t *)data
!= TFTP_DATA
) /* Not a data packet */
542 /* If goes here, recevie OK, break */
550 last_pkt
= socket
->tftp_lastpkt
;
551 last_pkt
= ntohs(last_pkt
); /* Host byte order */
553 last_pkt
= htons(last_pkt
); /* Network byte order */
554 if (*(uint16_t *)(data
+ 2) != last_pkt
) {
556 * Wrong packet, ACK the packet and try again.
557 * This is presumably because the ACK got lost,
558 * so the server just resent the previous packet.
561 printf("Wrong packet, wanted %04x, got %04x\n", \
562 htons(last_pkt
), htons(*(uint16_t *)(data
+2)));
567 /* It's the packet we want. We're also EOF if the size < blocksize */
568 socket
->tftp_lastpkt
= last_pkt
; /* Update last packet number */
569 buffersize
= udp_read
.buffer_size
- 4; /* Skip TFTP header */
570 memcpy(socket
->tftp_pktbuf
, packet_buf
+ 4, buffersize
);
571 socket
->tftp_dataptr
= socket
->tftp_pktbuf
;
572 socket
->tftp_filepos
+= buffersize
;
573 socket
->tftp_bytesleft
= buffersize
;
574 if (buffersize
< socket
->tftp_blksize
) {
575 /* it's the last block, ACK packet immediately */
576 ack_packet(inode
, *(uint16_t *)(data
+ 2));
578 /* Make sure we know we are at end of file */
579 inode
->size
= socket
->tftp_filepos
;
580 socket
->tftp_goteof
= 1;
586 * getfssec: Get multiple clusters from a file, given the starting cluster.
587 * In this case, get multiple blocks from a specific TCP connection.
589 * @param: fs, the fs_info structure address, in pxe, we don't use this.
590 * @param: buf, buffer to store the read data
591 * @param: openfile, TFTP socket pointer
592 * @param: blocks, 512-byte block count; 0FFFFh = until end of file
594 * @return: the bytes read
597 static uint32_t pxe_getfssec(struct file
*file
, char *buf
,
598 int blocks
, bool *have_more
)
600 struct inode
*inode
= file
->inode
;
601 struct pxe_pvt_inode
*socket
= PVT(inode
);
606 count
<<= TFTP_BLOCKSIZE_LG2
;
608 fill_buffer(inode
); /* If we have no 'fresh' buffer, get it */
609 if (!socket
->tftp_bytesleft
)
613 if (chunk
> socket
->tftp_bytesleft
)
614 chunk
= socket
->tftp_bytesleft
;
615 socket
->tftp_bytesleft
-= chunk
;
616 memcpy(buf
, socket
->tftp_dataptr
, chunk
);
617 socket
->tftp_dataptr
+= chunk
;
624 if (socket
->tftp_bytesleft
|| (socket
->tftp_filepos
< inode
->size
)) {
627 } else if (socket
->tftp_goteof
) {
629 * The socket is closed and the buffer drained; the caller will
630 * call close_file and therefore free the socket.
639 * Open a TFTP connection to the server
641 * @param:filename, the file we wanna open
643 * @out: open_file_t structure, stores in file->open_file
644 * @out: the lenght of this file, stores in file->file_len
647 static void __pxe_searchdir(const char *filename
, struct file
*file
);
648 extern uint16_t PXERetry
;
650 static void pxe_searchdir(const char *filename
, struct file
*file
)
655 dprintf("PXE: file = %p, retries left = %d: ", file
, i
);
656 __pxe_searchdir(filename
, file
);
657 dprintf("%s\n", file
->inode
? "ok" : "failed");
658 } while (!file
->inode
&& i
--);
661 static void __pxe_searchdir(const char *filename
, struct file
*file
)
663 struct fs_info
*fs
= file
->fs
;
665 struct pxe_pvt_inode
*socket
;
671 static __lowmem
struct s_PXENV_UDP_WRITE udp_write
;
672 static __lowmem
struct s_PXENV_UDP_READ udp_read
;
673 static __lowmem
struct s_PXENV_FILE_OPEN file_open
;
674 static const char rrq_tail
[] = "octet\0""tsize\0""0\0""blksize\0""1408";
675 static __lowmem
char rrq_packet_buf
[2+2*FILENAME_MAX
+sizeof rrq_tail
];
676 const struct tftp_options
*tftp_opt
;
681 const uint8_t *timeout_ptr
;
688 uint32_t opdata
, *opdata_ptr
;
689 enum pxe_path_type path_type
;
690 char fullpath
[2*FILENAME_MAX
];
691 uint16_t server_port
= TFTP_PORT
; /* TFTP server port */
693 inode
= file
->inode
= NULL
;
695 buf
= rrq_packet_buf
;
696 *(uint16_t *)buf
= TFTP_RRQ
; /* TFTP opcode */
699 path_type
= pxe_path_type(filename
);
700 if (path_type
== PXE_RELATIVE
) {
701 snprintf(fullpath
, sizeof fullpath
, "%s%s", fs
->cwd_name
, filename
);
702 path_type
= pxe_path_type(filename
= fullpath
);
706 case PXE_RELATIVE
: /* Really shouldn't happen... */
708 buf
= stpcpy(buf
, filename
);
709 ip
= IPInfo
.serverip
; /* Default server */
713 buf
= stpcpy(buf
, filename
+2);
714 ip
= IPInfo
.serverip
;
718 np
= strchr(filename
, ':');
719 buf
= stpcpy(buf
, np
+2);
720 if (parse_dotquad(filename
, &ip
) != np
)
721 ip
= dns_resolv(filename
);
726 while (*np
&& *np
!= '/' && *np
!= ':')
728 if (np
> filename
+ 7) {
729 if (parse_dotquad(filename
+ 7, &ip
) != np
)
730 ip
= dns_resolv(filename
+ 7);
735 while (*np
>= '0' && *np
<= '9')
736 server_port
= server_port
* 10 + *np
++ - '0';
737 server_port
= server_port
? htons(server_port
) : TFTP_PORT
;
740 np
++; /* Do *NOT* eat more than one slash here... */
742 * The ; is because of a quirk in the TFTP URI spec (RFC
743 * 3617); it is to be followed by TFTP modes, which we just ignore.
745 while (*np
&& *np
!= ';') {
747 if (*np
== '%' && (v
= hexbyte(np
+1)) > 0) {
758 buf
++; /* Point *past* the final NULL */
759 memcpy(buf
, rrq_tail
, sizeof rrq_tail
);
760 buf
+= sizeof rrq_tail
;
762 rrq_len
= buf
- rrq_packet_buf
;
764 inode
= allocate_socket(fs
);
766 return; /* Allocation failure */
770 if (path_type
== PXE_URL
) {
772 file_open
.Status
= PXENV_STATUS_BAD_FUNC
;
773 file_open
.FileName
= FAR_PTR(rrq_packet_buf
+ 2);
774 err
= pxe_call(PXENV_FILE_OPEN
, &file_open
);
778 socket
->tftp_localport
= -1;
779 socket
->tftp_remoteport
= file_open
.FileHandle
;
783 static bool already
= false;
785 printf("URL syntax, but gPXE extensions not detected, "
786 "trying plain TFTP...\n");
794 goto done
; /* No server */
796 timeout_ptr
= TimeoutTable
; /* Reset timeout */
799 timeout
= *timeout_ptr
++;
802 return; /* No file available... */
806 socket
->tftp_remoteip
= ip
;
807 tid
= socket
->tftp_localport
; /* TID(local port No) */
808 udp_write
.buffer
= FAR_PTR(rrq_packet_buf
);
810 udp_write
.gw
= gateway(udp_write
.ip
);
811 udp_write
.src_port
= tid
;
812 udp_write
.dst_port
= server_port
;
813 udp_write
.buffer_size
= rrq_len
;
814 pxe_call(PXENV_UDP_WRITE
, &udp_write
);
816 /* If the WRITE call fails, we let the timeout take care of it... */
822 udp_read
.buffer
= FAR_PTR(buf
);
823 udp_read
.buffer_size
= PKTBUF_SIZE
;
824 udp_read
.dest_ip
= IPInfo
.myip
;
825 udp_read
.d_port
= tid
;
826 err
= pxe_call(PXENV_UDP_READ
, &udp_read
);
827 if (err
|| udp_read
.status
) {
828 uint32_t now
= jiffies();
829 if (now
- oldtime
>= timeout
)
832 /* Make sure the packet actually came from the server */
833 if (udp_read
.src_ip
== socket
->tftp_remoteip
)
838 socket
->tftp_remoteport
= udp_read
.s_port
;
840 /* filesize <- -1 == unknown */
842 /* Default blksize unless blksize option negotiated */
843 socket
->tftp_blksize
= TFTP_BLOCKSIZE
;
844 buffersize
= udp_read
.buffer_size
- 2; /* bytes after opcode */
846 goto wait_pkt
; /* Garbled reply */
849 * Get the opcode type, and parse it
851 opcode
= *(uint16_t *)packet_buf
;
855 break; /* ERROR reply; don't try again */
859 * If the server doesn't support any options, we'll get a
860 * DATA reply instead of OACK. Stash the data in the file
861 * buffer and go with the default value for all options...
863 * We got a DATA packet, meaning no options are
864 * suported. Save the data away and consider the
865 * length undefined, *unless* this is the only
871 data
= packet_buf
+ 2;
872 blk_num
= *(uint16_t *)data
;
874 if (blk_num
!= htons(1))
876 socket
->tftp_lastpkt
= blk_num
;
877 if (buffersize
> TFTP_BLOCKSIZE
)
878 goto err_reply
; /* Corrupt */
879 else if (buffersize
< TFTP_BLOCKSIZE
) {
881 * This is the final EOF packet, already...
882 * We know the filesize, but we also want to
883 * ack the packet and set the EOF flag.
885 inode
->size
= buffersize
;
886 socket
->tftp_goteof
= 1;
887 ack_packet(inode
, blk_num
);
890 socket
->tftp_bytesleft
= buffersize
;
891 socket
->tftp_dataptr
= socket
->tftp_pktbuf
;
892 memcpy(socket
->tftp_pktbuf
, data
, buffersize
);
897 * Now we need to parse the OACK packet to get the transfer
901 options
= packet_buf
+ 2;
908 * If we find an option which starts with a NUL byte,
909 * (a null option), we're either seeing garbage that some
910 * TFTP servers add to the end of the packet, or we have
911 * no clue how to parse the rest of the packet (what is
912 * an option name and what is a value?) In either case,
920 break; /* Found a final null */
925 break; /* Unterminated option */
927 /* Consume the terminal null */
932 break; /* No option data */
935 * Parse option pointed to by options; guaranteed to be
938 tftp_opt
= tftp_options
;
939 for (i
= 0; i
< tftp_nopts
; i
++) {
940 if (!strcmp(opt
, tftp_opt
->str_ptr
))
945 goto err_reply
; /* Non-negotitated option returned,
946 no idea what it means ...*/
948 /* get the address of the filed that we want to write on */
949 opdata_ptr
= (uint32_t *)((char *)inode
+ tftp_opt
->offset
);
952 /* do convert a number-string to decimal number, just like atoi */
953 while (buffersize
--) {
956 break; /* found a final null */
959 goto err_reply
; /* Not a decimal digit */
960 opdata
= opdata
*10 + d
;
962 *opdata_ptr
= opdata
;
967 printf("TFTP unknown opcode %d\n", ntohs(opcode
));
980 /* Build the TFTP error packet */
981 tftp_error(inode
, TFTP_EOPTNEG
, "TFTP protocol error");
982 printf("TFTP server sent an incomprehesible reply\n");
988 * Store standard filename prefix
990 static void get_prefix(void)
996 if (!(DHCPMagic
& 0x04)) {
997 /* No path prefix option, derive from boot file */
999 strlcpy(path_prefix
, boot_file
, sizeof path_prefix
);
1000 len
= strlen(path_prefix
);
1001 p
= &path_prefix
[len
- 1];
1007 c
= (c
>= '0' && c
<= '9') ||
1008 (c
>= 'a' && c
<= 'z') ||
1009 (c
== '.' || c
== '-');
1017 *(p
+ 2) = 0; /* Zero-terminate after delimiter */
1020 printf("TFTP prefix: %s\n", path_prefix
);
1027 static size_t pxe_realpath(struct fs_info
*fs
, char *dst
, const char *src
,
1030 enum pxe_path_type path_type
= pxe_path_type(src
);
1032 return snprintf(dst
, bufsize
, "%s%s",
1033 path_type
== PXE_RELATIVE
? fs
->cwd_name
: "", src
);
1039 static int pxe_chdir(struct fs_info
*fs
, const char *src
)
1041 /* The cwd for PXE is just a text prefix */
1042 enum pxe_path_type path_type
= pxe_path_type(src
);
1044 if (path_type
== PXE_RELATIVE
)
1045 strlcat(fs
->cwd_name
, src
, sizeof fs
->cwd_name
);
1047 strlcpy(fs
->cwd_name
, src
, sizeof fs
->cwd_name
);
1049 dprintf("cwd = \"%s\"\n", fs
->cwd_name
);
1053 static int pxe_chdir_start(void)
1059 /* Load the config file, return -1 if failed, or 0 */
1060 static int pxe_open_config(struct com32_filedata
*filedata
)
1062 const char *cfgprefix
= "pxelinux.cfg/";
1063 const char *default_str
= "default";
1068 if (DHCPMagic
& 0x02) {
1069 /* We got a DHCP option, try it first */
1070 if (open_file(ConfigName
, filedata
) >= 0)
1075 * Have to guess config file name ...
1077 config_file
= stpcpy(ConfigName
, cfgprefix
);
1079 /* Try loading by UUID */
1081 strcpy(config_file
, UUID_str
);
1082 if (open_file(ConfigName
, filedata
) >= 0)
1086 /* Try loading by MAC address */
1087 strcpy(config_file
, MAC_str
);
1088 if (open_file(ConfigName
, filedata
) >= 0)
1091 /* Nope, try hexadecimal IP prefixes... */
1092 uchexbytes(config_file
, (uint8_t *)&IPInfo
.myip
, 4);
1093 last
= &config_file
[8];
1095 *last
= '\0'; /* Zero-terminate string */
1096 if (open_file(ConfigName
, filedata
) >= 0)
1098 last
--; /* Drop one character */
1102 /* Final attempt: "default" string */
1103 strcpy(config_file
, default_str
);
1104 if (open_file(ConfigName
, filedata
) >= 0)
1107 printf("%-68s\n", "Unable to locate configuration file");
1112 * Generate the bootif string.
1114 static void make_bootif_string(void)
1117 char *dst
= BOOTIFStr
;
1120 dst
+= sprintf(dst
, "BOOTIF=%02x", MAC_type
);
1122 for (i
= MAC_len
; i
; i
--)
1123 dst
+= sprintf(dst
, "-%02x", *src
++);
1126 * Generate the SYSUUID string, if we have one...
1128 static void make_sysuuid_string(void)
1130 static const uint8_t uuid_dashes
[] = {4, 2, 2, 2, 6, 0};
1131 const uint8_t *src
= uuid
;
1132 const uint8_t *uuid_ptr
= uuid_dashes
;
1135 SYSUUIDStr
[0] = '\0'; /* If nothing there... */
1137 /* Try loading by UUID */
1139 dst
= stpcpy(SYSUUIDStr
, "SYSUUID=");
1142 int len
= *uuid_ptr
;
1144 lchexbytes(dst
, src
, len
);
1150 /* Remove last dash and zero-terminate */
1156 * Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
1157 * option into IPOption based on DHCP information in IPInfo.
1160 char __bss16 IPOption
[3+4*16];
1162 static void genipopt(void)
1165 const uint32_t *v
= &IPInfo
.myip
;
1168 p
= stpcpy(p
, "ip=");
1170 for (i
= 0; i
< 4; i
++) {
1171 p
+= gendotquad(p
, *v
++);
1178 /* Generate ip= option and print the ip adress */
1179 static void ip_init(void)
1181 uint32_t ip
= IPInfo
.myip
;
1184 gendotquad(dot_quad_buf
, ip
);
1187 printf("My IP address seems to be %08X %s\n", ip
, dot_quad_buf
);
1191 * Print the IPAPPEND strings, in order
1193 static void print_ipappend(void)
1197 for (i
= 0; i
< (size_t)numIPAppends
; i
++) {
1198 const char *p
= (const char *)(size_t)IPAppends
[i
];
1205 * Validity check on possible !PXE structure in buf
1206 * return 1 for success, 0 for failure.
1209 static int is_pxe(const void *buf
)
1211 const struct pxe_t
*pxe
= buf
;
1212 const uint8_t *p
= buf
;
1213 int i
= pxe
->structlength
;
1216 if (i
< sizeof(struct pxe_t
) ||
1217 memcmp(pxe
->signature
, "!PXE", 4))
1227 * Just like is_pxe, it checks PXENV+ structure
1230 static int is_pxenv(const void *buf
)
1232 const struct pxenv_t
*pxenv
= buf
;
1233 const uint8_t *p
= buf
;
1234 int i
= pxenv
->length
;
1237 /* The pxeptr field isn't present in old versions */
1238 if (i
< offsetof(struct pxenv_t
, pxeptr
) ||
1239 memcmp(pxenv
->signature
, "PXENV+", 6))
1251 * memory_scan_for_pxe_struct:
1252 * memory_scan_for_pxenv_struct:
1254 * If none of the standard methods find the !PXE/PXENV+ structure,
1255 * look for it by scanning memory.
1257 * return the corresponding pxe structure if found, or NULL;
1259 static const void *memory_scan(uintptr_t start
, int (*func
)(const void *))
1263 /* Scan each 16 bytes of conventional memory before the VGA region */
1264 for (ptr
= (const char *)start
; ptr
< (const char *)0xA0000; ptr
+= 16) {
1266 return ptr
; /* found it! */
1272 static const struct pxe_t
*memory_scan_for_pxe_struct(void)
1274 uint16_t start
= bios_fbm(); /* Starting segment */
1276 return memory_scan(start
<< 10, is_pxe
);
1279 static const struct pxenv_t
*memory_scan_for_pxenv_struct(void)
1281 return memory_scan(0x10000, is_pxenv
);
1285 * Find the !PXE structure; we search for the following, in order:
1287 * a. !PXE structure as SS:[SP + 4]
1288 * b. PXENV+ structure at [ES:BX]
1289 * c. INT 1Ah AX=0x5650 -> PXENV+
1290 * d. Search memory for !PXE
1291 * e. Search memory for PXENV+
1293 * If we find a PXENV+ structure, we try to find a !PXE structure from
1294 * if if the API version is 2.1 or later
1297 static int pxe_init(bool quiet
)
1299 extern void pxe_int1a(void);
1302 uint16_t code_seg
, code_len
;
1303 uint16_t data_seg
, data_len
;
1304 const char *base
= GET_PTR(InitStack
);
1307 const struct pxenv_t
*pxenv
;
1308 const struct pxe_t
*pxe
;
1310 /* Assume API version 2.1 */
1313 /* Plan A: !PXE structure as SS:[SP + 4] */
1314 off
= *(const uint16_t *)(base
+ 48);
1315 seg
= *(const uint16_t *)(base
+ 50);
1316 pxe
= MK_PTR(seg
, off
);
1320 /* Plan B: PXENV+ structure at [ES:BX] */
1322 off
= *(const uint16_t *)(base
+ 24); /* Original BX */
1323 seg
= *(const uint16_t *)(base
+ 4); /* Original ES */
1324 pxenv
= MK_PTR(seg
, off
);
1325 if (is_pxenv(pxenv
))
1328 /* Plan C: PXENV+ structure via INT 1Ah AX=5650h */
1330 memset(®s
, 0, sizeof regs
);
1331 regs
.eax
.w
[0] = 0x5650;
1332 call16(pxe_int1a
, ®s
, ®s
);
1333 if (!(regs
.eflags
.l
& EFLAGS_CF
) && (regs
.eax
.w
[0] == 0x564e)) {
1334 pxenv
= MK_PTR(regs
.es
, regs
.ebx
.w
[0]);
1335 if (is_pxenv(pxenv
))
1339 /* Plan D: !PXE memory scan */
1341 if ((pxe
= memory_scan_for_pxe_struct()))
1344 /* Plan E: PXENV+ memory scan */
1346 if ((pxenv
= memory_scan_for_pxenv_struct()))
1349 /* Found nothing at all !! */
1351 printf("No !PXE or PXENV+ API found; we're dead...\n");
1355 APIVer
= pxenv
->version
;
1357 printf("Found PXENV+ structure\nPXE API version is %04x\n", APIVer
);
1359 /* if the API version number is 0x0201 or higher, use the !PXE structure */
1360 if (APIVer
>= 0x201) {
1361 if (pxenv
->length
>= sizeof(struct pxenv_t
)) {
1362 pxe
= GET_PTR(pxenv
->pxeptr
);
1366 * Nope, !PXE structure missing despite API 2.1+, or at least
1367 * the pointer is missing. Do a last-ditch attempt to find it
1369 if ((pxe
= memory_scan_for_pxe_struct()))
1372 APIVer
= 0x200; /* PXENV+ only, assume version 2.00 */
1375 /* Otherwise, no dice, use PXENV+ structure */
1376 data_len
= pxenv
->undidatasize
;
1377 data_seg
= pxenv
->undidataseg
;
1378 code_len
= pxenv
->undicodesize
;
1379 code_seg
= pxenv
->undicodeseg
;
1380 PXEEntry
= pxenv
->rmentry
;
1382 goto have_entrypoint
;
1385 data_len
= pxe
->seg
[PXE_Seg_UNDIData
].size
;
1386 data_seg
= pxe
->seg
[PXE_Seg_UNDIData
].sel
;
1387 code_len
= pxe
->seg
[PXE_Seg_UNDICode
].size
;
1388 code_seg
= pxe
->seg
[PXE_Seg_UNDICode
].sel
;
1389 PXEEntry
= pxe
->entrypointsp
;
1394 printf("%s entry point found (we hope) at %04X:%04X via plan %c\n",
1395 type
, PXEEntry
.seg
, PXEEntry
.offs
, plan
);
1396 printf("UNDI code segment at %04X len %04X\n", code_seg
, code_len
);
1397 printf("UNDI data segment at %04X len %04X\n", data_seg
, data_len
);
1400 code_seg
= code_seg
+ ((code_len
+ 15) >> 4);
1401 data_seg
= data_seg
+ ((data_len
+ 15) >> 4);
1403 real_base_mem
= max(code_seg
, data_seg
) >> 6; /* Convert to kilobytes */
1409 * See if we have gPXE
1411 static void gpxe_init(void)
1414 static __lowmem
struct s_PXENV_FILE_API_CHECK api_check
;
1416 if (APIVer
>= 0x201) {
1417 api_check
.Size
= sizeof api_check
;
1418 api_check
.Magic
= 0x91d447b2;
1419 err
= pxe_call(PXENV_FILE_API_CHECK
, &api_check
);
1420 if (!err
&& api_check
.Magic
== 0xe9c17b20)
1421 gpxe_funcs
= api_check
.APIMask
;
1424 /* Necessary functions for us to use the gPXE file API */
1425 has_gpxe
= (~gpxe_funcs
& 0x4b) == 0;
1429 * Initialize UDP stack
1432 static void udp_init(void)
1435 static __lowmem
struct s_PXENV_UDP_OPEN udp_open
;
1436 udp_open
.src_ip
= IPInfo
.myip
;
1437 err
= pxe_call(PXENV_UDP_OPEN
, &udp_open
);
1438 if (err
|| udp_open
.status
) {
1439 printf("Failed to initialize UDP stack ");
1440 printf("%d\n", udp_open
.status
);
1447 * Network-specific initialization
1449 static void network_init(void)
1453 const size_t dhcp_max_packet
= 4096;
1455 bp
= lmalloc(dhcp_max_packet
);
1457 printf("Out of low memory\n");
1461 *LocalDomain
= 0; /* No LocalDomain received */
1464 * Get the DHCP client identifiers (query info 1)
1466 printf("Getting cached packet ");
1467 pkt_len
= pxe_get_cached_info(1, bp
, dhcp_max_packet
);
1468 parse_dhcp(bp
, pkt_len
);
1470 * We don't use flags from the request packet, so
1471 * this is a good time to initialize DHCPMagic...
1472 * Initialize it to 1 meaning we will accept options found;
1473 * in earlier versions of PXELINUX bit 0 was used to indicate
1474 * we have found option 208 with the appropriate magic number;
1475 * we no longer require that, but MAY want to re-introduce
1476 * it in the future for vendor encapsulated options.
1478 *(char *)&DHCPMagic
= 1;
1481 * Get the BOOTP/DHCP packet that brought us file (and an IP
1482 * address). This lives in the DHCPACK packet (query info 2)
1484 pkt_len
= pxe_get_cached_info(2, bp
, dhcp_max_packet
);
1485 parse_dhcp(bp
, pkt_len
);
1487 * Save away MAC address (assume this is in query info 2. If this
1488 * turns out to be problematic it might be better getting it from
1489 * the query info 1 packet
1491 MAC_len
= bp
->hardlen
> 16 ? 0 : bp
->hardlen
;
1492 MAC_type
= bp
->hardware
;
1493 memcpy(MAC
, bp
->macaddr
, MAC_len
);
1496 * Get the boot file and other info. This lives in the CACHED_REPLY
1497 * packet (query info 3)
1499 pkt_len
= pxe_get_cached_info(3, bp
, dhcp_max_packet
);
1500 parse_dhcp(bp
, pkt_len
);
1505 make_bootif_string();
1506 make_sysuuid_string();
1511 * Check to see if we got any PXELINUX-specific DHCP options; in particular,
1512 * if we didn't get the magic enable, do not recognize any other options.
1514 if ((DHCPMagic
& 1) == 0)
1524 static int pxe_fs_init(struct fs_info
*fs
)
1526 (void)fs
; /* drop the compile warning message */
1528 /* This block size is actually arbitrary... */
1529 fs
->sector_shift
= fs
->block_shift
= TFTP_BLOCKSIZE_LG2
;
1530 fs
->sector_size
= fs
->block_size
= 1 << TFTP_BLOCKSIZE_LG2
;
1532 /* This block size is actually arbitrary... */
1533 fs
->sector_shift
= fs
->block_shift
= TFTP_BLOCKSIZE_LG2
;
1534 fs
->sector_size
= fs
->block_size
= 1 << TFTP_BLOCKSIZE_LG2
;
1536 /* Find the PXE stack */
1537 if (pxe_init(false))
1540 /* See if we also have a gPXE stack */
1543 /* Network-specific initialization */
1546 /* Initialize network-card-specific idle handling */
1549 /* Our name for the root */
1550 strcpy(fs
->cwd_name
, "::");
1556 * Look to see if we are on an EFI CSM system. Some EFI
1557 * CSM systems put the BEV stack in low memory, which means
1558 * a return to the PXE stack will crash the system. However,
1559 * INT 18h works reliably, so in that case hack the stack and
1560 * point the "return address" to an INT 18h instruction.
1562 * Hack the stack instead of the much simpler "just invoke INT 18h
1563 * if we want to reset", so that chainloading other NBPs will work.
1565 * This manipulates the real-mode InitStack directly. It relies on this
1566 * *not* being a currently active stack, i.e. the former
1567 * USE_PXE_PROVIDED_STACK no longer works.
1569 * XXX: Disable this until we can find a better way to discriminate
1570 * between BIOSes that are broken on BEV return and BIOSes which are
1571 * broken on INT 18h. Keying on the EFI CSM turns out to cause more
1572 * problems than it solves.
1574 extern far_ptr_t InitStack
;
1580 } __attribute__((packed
));
1581 #define EFI_MAGIC (('$' << 24)+('E' << 16)+('F' << 8)+'I')
1583 static inline bool is_efi(const struct efi_struct
*efi
)
1586 * We don't verify the checksum, because it seems some CSMs leave
1587 * it at zero, sigh...
1589 return (efi
->magic
== EFI_MAGIC
) && (efi
->len
>= 83);
1592 static void install_int18_hack(void)
1595 static const uint8_t int18_hack
[] =
1597 0xcd, 0x18, /* int $0x18 */
1598 0xea, 0xf0, 0xff, 0x00, 0xf0, /* ljmpw $0xf000,$0xfff0 */
1603 retcode
= GET_PTR(*(far_ptr_t
*)((char *)GET_PTR(InitStack
) + 44));
1605 /* Don't do this if the return already points to int $0x18 */
1606 if (*retcode
!= 0x18cd) {
1610 for (efi_ptr
= 0xe0000 ; efi_ptr
< 0x100000 ; efi_ptr
+= 16) {
1611 if (is_efi((const struct efi_struct
*)efi_ptr
)) {
1618 uint8_t *src
= GET_PTR(InitStack
);
1619 uint8_t *dst
= src
- sizeof int18_hack
;
1621 memmove(dst
, src
, 52);
1622 memcpy(dst
+52, int18_hack
, sizeof int18_hack
);
1623 InitStack
.offs
-= sizeof int18_hack
;
1625 /* Clobber the return address */
1626 *(uint16_t *)(dst
+44) = OFFS_WRT(dst
+52, InitStack
.seg
);
1627 *(uint16_t *)(dst
+46) = InitStack
.seg
;
1635 static __lowmem
struct s_PXENV_UDP_CLOSE udp_close
;
1636 extern void gpxe_unload(void);
1641 pxe_call(PXENV_UDP_CLOSE
, &udp_close
);
1643 if (gpxe_funcs
& 0x80) {
1644 /* gPXE special unload implemented */
1645 call16(gpxe_unload
, &zero_regs
, NULL
);
1647 /* Locate the actual vendor stack... */
1648 err
= pxe_init(true);
1651 install_int18_hack();
1656 * This function unloads the PXE and UNDI stacks and
1657 * unclaims the memory.
1659 __export
void unload_pxe(uint16_t flags
)
1661 /* PXE unload sequences */
1662 static const uint8_t new_api_unload
[] = {
1663 PXENV_UNDI_SHUTDOWN
, PXENV_UNLOAD_STACK
, PXENV_STOP_UNDI
, 0
1665 static const uint8_t old_api_unload
[] = {
1666 PXENV_UNDI_SHUTDOWN
, PXENV_UNLOAD_STACK
, PXENV_UNDI_CLEANUP
, 0
1670 const uint8_t *api_ptr
;
1673 static __lowmem
union {
1674 struct s_PXENV_UNDI_SHUTDOWN undi_shutdown
;
1675 struct s_PXENV_UNLOAD_STACK unload_stack
;
1676 struct s_PXENV_STOP_UNDI stop_undi
;
1677 struct s_PXENV_UNDI_CLEANUP undi_cleanup
;
1678 uint16_t Status
; /* All calls have this as the first member */
1681 dprintf("FBM before unload = %d\n", bios_fbm());
1685 dprintf("FBM after reset_pxe = %d, err = %d\n", bios_fbm(), err
);
1687 /* If we want to keep PXE around, we still need to reset it */
1691 dprintf("APIVer = %04x\n", APIVer
);
1693 api_ptr
= APIVer
>= 0x0200 ? new_api_unload
: old_api_unload
;
1694 while((api
= *api_ptr
++)) {
1695 dprintf("PXE call %04x\n", api
);
1696 memset(&unload_call
, 0, sizeof unload_call
);
1697 err
= pxe_call(api
, &unload_call
);
1698 if (err
|| unload_call
.Status
!= PXENV_STATUS_SUCCESS
) {
1699 dprintf("PXE unload API call %04x failed\n", api
);
1705 if (real_base_mem
<= bios_fbm()) { /* Sanity check */
1706 dprintf("FBM %d < real_base_mem %d\n", bios_fbm(), real_base_mem
);
1711 /* Check that PXE actually unhooked the INT 0x1A chain */
1712 int_addr
= (size_t)GET_PTR(*(far_ptr_t
*)(4 * 0x1a));
1714 if (int_addr
>= real_base_mem
|| int_addr
< bios_fbm()) {
1715 set_bios_fbm(real_base_mem
);
1716 dprintf("FBM after unload_pxe = %d\n", bios_fbm());
1720 dprintf("Can't free FBM, real_base_mem = %d, "
1721 "FBM = %d, INT 1A = %08x (%d)\n",
1722 real_base_mem
, bios_fbm(),
1723 *(uint32_t *)(4 * 0x1a), int_addr
);
1726 printf("Failed to free base memory error %04x-%08x (%d/%dK)\n",
1727 api
, *(uint32_t *)(4 * 0x1a), bios_fbm(), real_base_mem
);
1731 const struct fs_ops pxe_fs_ops
= {
1733 .fs_flags
= FS_NODEV
,
1734 .fs_init
= pxe_fs_init
,
1735 .searchdir
= pxe_searchdir
,
1737 .realpath
= pxe_realpath
,
1738 .getfssec
= pxe_getfssec
,
1739 .close_file
= pxe_close_file
,
1740 .mangle_name
= pxe_mangle_name
,
1741 .chdir_start
= pxe_chdir_start
,
1742 .open_config
= pxe_open_config
,