aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLiu Aleaxander <Aleaxander@gmail.com>2009-08-04 16:08:35 +0800
committerLiu Aleaxander <Aleaxander@gmail.com>2009-08-04 16:08:35 +0800
commitfa630ef0692757d381eef9f62ac69ccc00139d07 (patch)
treec35880c6dd66cfecff08e1dc57633785e4143b08
parentf0477aa673fc3c130553eb77324b0c5349241f52 (diff)
downloadpxelinux-fa630ef0692757d381eef9f62ac69ccc00139d07.tar.gz
pxelinux-fa630ef0692757d381eef9f62ac69ccc00139d07.tar.xz
pxelinux-fa630ef0692757d381eef9f62ac69ccc00139d07.zip
Core:PXELINUX: searchdir function convetted.
-rw-r--r--core/extern.inc2
-rw-r--r--core/pxe.c459
-rw-r--r--core/pxelinux.asm18
3 files changed, 434 insertions, 45 deletions
diff --git a/core/extern.inc b/core/extern.inc
index d6847d94..fd658927 100644
--- a/core/extern.inc
+++ b/core/extern.inc
@@ -19,7 +19,7 @@
extern free_socket, ip_ok, mangle_name, ack_packet, pxe_get_cached_info
extern udp_init, get_prefix, network_init, parse_dhcp_options
extern get_packet_gpxe, pxe_init
- extern getfssec, fill_buffer
+ extern getfssec, fill_buffer, searchdir_c
; debug.c
extern debug, print_reg
diff --git a/core/pxe.c b/core/pxe.c
index 836198fe..ff825e01 100644
--- a/core/pxe.c
+++ b/core/pxe.c
@@ -11,12 +11,16 @@
#define PKTBUF_SIZE (65536 / MAX_OPEN)
#define TFTP_BLOCKSIZE_LG2 9
+#define TFTP_BLOCKSIZE (1 << TFTP_BLOCKSIZE_LG2)
+
+
+#define PKTBUF_SEG 0x4000
#define DNS_MAX_SERVERS 4
#define MAX(a,b) (a > b ? a : b)
-#define GPXE 0
+#define GPXE 1
#define USE_PXE_PROVIDED_STACK 0
#define is_digit(c) (((c) >= '0') && ((c) <= '9'))
@@ -123,6 +127,31 @@ struct pxe_udp_open_pkt {
} __attribute__ ((packed));
extern char pxe_udp_open_pkt[];
+struct gpxe_file_api_check {
+ uint16_t status;
+ uint16_t size;
+ uint32_t magic;
+ uint32_t provider;
+ uint32_t apimask;
+ uint32_t flags;
+} __attribute__ ((packed));
+extern char gpxe_file_api_check[];
+
+struct gpxe_file_open {
+ uint16_t status;
+ uint16_t filehandle;
+ uint16_t filename[2];
+ uint32_t reserved;
+} __attribute__ ((packed));
+extern char gpxe_file_open[];
+
+struct gpxe_get_file_size {
+ uint16_t status;
+ uint16_t filehandle;
+ uint32_t filesize;
+} __attribute__ ((packed));
+extern char gpxe_get_file_size[];
+
struct gpxe_file_read {
uint16_t status;
uint16_t filehandle;
@@ -135,6 +164,7 @@ extern uint32_t ServerIP;
extern uint32_t MyIP;
extern uint32_t Netmask;
extern uint32_t Gateway;
+extern uint32_t ServerPort;
#define MAC_MAX 32
extern char MACStr[]; /* MAC address as a string */
@@ -159,9 +189,12 @@ extern char PathPrefix[];
extern char CurrentDirName[];
extern char LocalDomain[];
+extern char packet_buf[];
+
extern char IPOption[];
extern char DotQuadBuf[];
+
extern uint32_t DNSServers[];
extern uint16_t LastDNSServer;
@@ -179,6 +212,7 @@ char *get_packet_msg = "Getting cached packet ";
uint16_t NextSocket = 49152;
+int has_gpxe;
int HaveUUID = 0;
uint8_t UUIDType;
uint8_t uuid_dashes[] = {4, 2, 2, 2, 6, 0};
@@ -207,6 +241,20 @@ static const uint8_t TimeoutTable[] = {
92, 110, 132, 159, 191, 229, 255, 255, 255, 255, 0
};
+
+
+char *mode = "octet";
+char *tsize_str = "tsize";
+/* We should include the final null here */
+int tsize_len = 6;
+
+char *blksize_str = "blksize";
+int blksize_len = 8;
+
+char *asciidec = "1408";
+
+
+
inline void pusha(com32sys_t *regs)
{
memcpy(&temp, regs, sizeof temp);
@@ -1208,51 +1256,43 @@ void parse_dhcp(com32sys_t *regs)
}
}
-#if 0 /* GPXE */
+#if GPXE
/*
* Return CF=0 if and only if the buffer pointed to by DS:SI is a URL
* (contains ://) *and* the gPXE extensions API is available. No
* registers modified.
*/
-void is_gpxe(com32sys_t *regs)
+int is_gpxe(char *url)
{
+ int err;
struct gpxe_file_api_check *ac = (struct gpxe_file_api_check *)gpxe_file_api_check;
- int flag;
-
- if (!is_url(regs)) {
- regs->eflags.l |= EFLAGS_CF;
- return;
- }
- again:
- if (HasGPXE > 1)
- goto unknow;
- else if (HasGPXE == 0)
- regs->eflags.l |= EFLAGS_CF;
- else if (HasGPXE == 1)
- regs->eflags.l &= ~EFLAGS_CF;
- return;
-
- unknow:
+ char *gpxe_warning_msg = "URL syntax, but gPXE extensions not detected, tring plain TFTP...\n";
+
+ com32sys_t regs;
+ memset(&regs, 0, sizeof regs);
+ regs.esi.w[0] = OFFS_WRT(url, 0);
+ is_url(&regs);
+ if (regs.eflags.l & EFLAGS_CF)
+ return 0;
- if (pxe_call(PXENV_FILE_API_CHECK, gpxe_file_api_check))
- goto nogood;
+ /* If has_gpxe is greater than one, means the gpxe status is unknow */
+ while (has_gpxe > 1) {
+ err = pxe_call(PXENV_FILE_API_CHECK, gpxe_file_api_check);
+ if (err || ac->magic != 0xe9c17b20)
+ printf("%s\n", gpxe_warning_msg);
+ else
+ has_gpxe = (~ac->provider & 0xffff) & 0x4b ? 0 : 1;
+
+ if (!has_gpxe)
+ printf("%s\n", gpxe_warning_msg);
+ }
- if (ac->magic != 0xe9c17b20)
- goto nogood;
- int provider = ac->provider & 0xffff;
-
- flag = (~provider) & 0x4b ? 0 : 1;
- if (flag == 0)
- goto done;
- nogood:
- printf("URL syntax, but gPXE extensions not detected, tring plain TFTP...\n");
- done:
- HasGPXE = flag;
- goto again;
+ if (has_gpxe == 1)
+ return 1;
+ else
+ return 0;
}
-#endif /* GPXE */
-#if GPXE
/*
* Get a fresh packet from a gPXE socket; expectfs -> pktbuf_seg
* and ds:si -> socket structure
@@ -1293,7 +1333,6 @@ void get_packet_gpxe(com32sys_t *regs)
}
#endif /* GPXE */
-#if 1
/*
*
;
@@ -1387,7 +1426,9 @@ void fill_buffer(com32sys_t *regs)
* This is presumably because the ACK got lost,
* so the server just resent the previous packet.
*/
+#if 0
printf("Wrong packet, wanted %04x, got %04x\n", htons(last_pkt), htons(*(uint16_t *)(data+2)));
+#endif
goto ack_again;
}
@@ -1431,7 +1472,6 @@ void fill_buffer(com32sys_t *regs)
*/
void getfssec(com32sys_t *regs)
{
- uint16_t pktbuf_seg = 0x4000;
char *buf = MK_PTR(regs->es, regs->ebx.w[0]);
struct open_file_t *file = (struct open_file_t *)MK_PTR(0, regs->esi.w[0]);
int count = regs->ecx.w[0];
@@ -1440,11 +1480,11 @@ void getfssec(com32sys_t *regs)
sti();
- regs->fs = pktbuf_seg;
+ regs->fs = PKTBUF_SEG;
count <<= TFTP_BLOCKSIZE_LG2;
while (count) {
- fill_buffer(regs);
+ fill_buffer(regs); /* If we have no 'fresh' buffer, get it */
if (! file->tftp_bytesleft)
break;
@@ -1480,7 +1520,7 @@ void getfssec(com32sys_t *regs)
/* hpa is 99% sure this can't happen, but ... */
fill_buffer(regs); /* Receive/ACK the EOF packet */
}
-#endif
+
/*
* Generate the botif string, and the hardware-based config string
@@ -1680,12 +1720,349 @@ void pxe_init(com32sys_t *regs)
RealBaseMem = MAX(code_seg,data_seg) >> 6; /* Convert to kilobytes */
regs->es = regs->ds; /* Restore the es segment */
+}
+
+/*
+ * Fill the packet tail with the tftp informations then retures the lenght
+ */
+int fill_tail(char *dst)
+{
+ char *p = dst;
+ strcpy(p, mode);
+ p += strlen(mode) + 1;
+
+ strcpy(p, tsize_str);
+ p += tsize_len;
+
+ strcpy(p, blksize_str);
+ p += blksize_len;
+
+ strcpy(p, "0");
+ p += 2;
+
+ strcpy(p, asciidec);
+ p += strlen(asciidec) + 1;
+
+ return p - dst;
}
+
+struct tftp_options {
+ char *str_ptr; /* string pointer */
+ int str_len; /* string lenght */
+ int offset; /* offset into socket structre */
+};
+struct tftp_options tftp_options[2];
+
+void init_options()
+{
+ tftp_options[0].str_ptr = tsize_str;
+ tftp_options[0].str_len = tsize_len;
+ tftp_options[0].offset = (int)&((struct open_file_t *)0)->tftp_filesize;
+
+ tftp_options[1].str_ptr = blksize_str;
+ tftp_options[1].str_len = blksize_len;
+ tftp_options[1].offset = (int)&((struct open_file_t *)0)->tftp_blksize;
+}
+
+/*
+ *
+ ;
+ ; searchdir:
+ ;
+ ; Open a TFTP connection to the server
+ ;
+ ; On entry:
+ ; DS:DI = mangled filename
+ ; If successful:
+ ; ZF clear
+ ; SI = socket pointer
+ ; EAX = file length in bytes, or -1 if unknown
+ ; If unsuccessful
+ ; ZF set
+ ;
+*/
+void searchdir_c(com32sys_t *regs)
+{
+ char *filename = MK_PTR(regs->ds, regs->edi.w[0]);
+ char *buf = packet_buf;
+ char *p = filename;
+ char *options;
+ char *src, *dst;
+ char *data;
+ struct open_file_t *file;
+ struct pxe_udp_write_pkt *uw_pkt = (struct pxe_udp_write_pkt *)pxe_udp_write_pkt;
+ struct pxe_udp_read_pkt *ur_pkt = (struct pxe_udp_read_pkt *)pxe_udp_read_pkt;
+ struct gpxe_file_open *fo = (struct gpxe_file_open *)gpxe_file_open;
+ struct gpxe_get_file_size *gs = (struct gpxe_get_file_size *)gpxe_get_file_size;
+ struct tftp_options *tftp_opt = tftp_options;
+ int i = 0;
+ int tftp_opts = sizeof tftp_options / sizeof tftp_options[0];
+ int err;
+ int buffersize;
+ const uint8_t *timeout_ptr;
+ uint8_t timeout;
+ uint16_t oldtime;
+ uint16_t tid;
+ uint16_t *data_ptr;
+ uint16_t opcode;
+ uint16_t blk_num;
+ uint32_t ip;
+ uint32_t filesize;
+
+ init_options();
+ allocate_socket(regs);
+ file = MK_PTR(0, regs->ebx.w[0]);
+ if (!file) {
+ regs->eflags.l |= EFLAGS_ZF;
+ return;
+ }
+
+ timeout_ptr = TimeoutTable; /* Reset timeout */
+
+ sendreq:
+ uw_pkt->buffer[0] = OFFS_WRT(buf, 0);
+ uw_pkt->buffer[1] = 0;
+ *(uint16_t *)buf = TFTP_RRQ; /* TFTP opcode */
+ buf += 2;
+
+ ip = *(uint32_t *)p; /* ip <- server override (if any) */
+ p += 4;
+ if (ip == 0) {
+ /* Have prefix */
+ strcpy(buf, PathPrefix);
+ buf += strlen(PathPrefix);
+ ip = ServerIP; /* Get the default server */
+ }
+
+ strcpy(buf, p); /* Copy the filename */
+ buf += strlen(p);
+
+#if GPXE
+ if (is_gpxe(packet_buf + 2)) {
+ fo->status = PXENV_STATUS_BAD_FUNC;
+ fo->filename[0] = OFFS_WRT(packet_buf + 2, 0);
+ fo->filename[1] = regs->ds;
+ err = pxe_call(PXENV_FILE_OPEN, fo);
+ if (err) {
+ free_socket(regs);
+ regs->eflags.l |= EFLAGS_ZF;
+ return;
+ }
+
+ file->tftp_localport = -1;
+ file->tftp_remoteport = fo->filehandle;
+ gs->filehandle = fo->filehandle;
+
+#if 0
+ err = pxe_call(PXENV_GET_FILE_SIZE, gs);
+ if (!err)
+ filesize = gs->filesize;
+ else
+#endif
+ filesize = -1;
+ file->tftp_filesize = -1;
+ goto got_file;
+ }
+#endif /* GPXE */
+
+ file->tftp_remoteip = ip;
+ tid = file->tftp_localport; /* TID(local port No) */
+ uw_pkt->sip = ip;
+ uw_pkt->gip = ((uw_pkt->sip ^ MyIP) & Netmask) ? Gateway : 0;
+ uw_pkt->lport = tid;
+ uw_pkt->rport = ServerPort;
+ buf += fill_tail(buf);
+ uw_pkt->buffersize = buf - packet_buf;
+ err = pxe_call(PXENV_UDP_WRITE, uw_pkt);
+ if (err || uw_pkt->status != 0)
+ goto failure; /* In fact, the 'failure' target will not do a failure thing;
+ it will move on to the next timeout, then tries again until
+ _real_ time out */
+
+ /*
+ * Danger, Will Robinson! We need to support tiemout
+ * and retry lest we just lost a packet ...
+ */
+
+ /* Packet transmitted OK, now we need to receive */
+ timeout = *timeout_ptr++;
+ oldtime = BIOS_timer;
+ while (timeout) {
+ buf = packet_buf;
+ ur_pkt->buffer[0] = OFFS_WRT(buf, 0);
+ ur_pkt->buffer[1] = regs->ds;
+ ur_pkt->buffersize = 2048;
+ ur_pkt->dip = MyIP;
+ ur_pkt->lport = tid;
+ err = pxe_call(PXENV_UDP_READ, ur_pkt);
+ if (err) {
+ if (oldtime == BIOS_timer)
+ continue;
+ timeout --; /* Decrease one timer tick */
+ if (!timeout)
+ goto failure;
+ }
+
+ /* Make sure the packet actually came from the server */
+ if (ur_pkt->sip == file->tftp_remoteip)
+ break;
+ }
+
+ /* Got packet; reset timeout */
+ timeout_ptr = TimeoutTable;
+ file->tftp_remoteport = ur_pkt->rport;
+
+ /* filesize <- -1 == unknow */
+ file->tftp_filesize = -1;
+ /* Default blksize unless blksize option negotiated */
+ file->tftp_blksize = TFTP_BLOCKSIZE;
+ buffersize = ur_pkt->buffersize - 2; /* bytes after opcode */
+ if (buffersize < 0)
+ goto failure; /* Garbled reply */
+
+ /*
+ * Get the opcode type, and parse it
+ */
+ opcode = *(uint16_t *)packet_buf;
+ if (opcode == TFTP_ERROR)
+ goto error; /* ERROR reply; don't try again */
+ else if (opcode == TFTP_DATA) {
+ /*
+ * If the server doesn't support any options, we'll get a
+ * DATA reply instead of OACK. Stash the data in the file
+ * buffer and go with the default value for all options...
+ *
+ * We got a DATA packet, meaning no options are
+ * suported. Save the data away and consider the
+ * length undefined, *unless* this is the only
+ * data packet...
+ */
+ buffersize -= 2;
+ if (buffersize < 0)
+ goto failure;
+ data = packet_buf + 2;
+ blk_num = *(uint16_t *)data;
+ data += 2;
+ if (blk_num != htons(1))
+ goto failure;
+ file->tftp_lastpkt = blk_num;
+ if (buffersize > TFTP_BLOCKSIZE)
+ goto err_reply; /* Corrupt */
+ else if (buffersize < TFTP_BLOCKSIZE) {
+ /*
+ * This is the final EOF packet, already...
+ * We know the filesize, but we also want to
+ * ack the packet and set the EOF flag.
+ */
+ file->tftp_filesize = buffersize;
+ file->tftp_goteof = 1;
+ regs->eax.w[0] = blk_num;
+ regs->esi.w[0] = OFFS_WRT(file, 0);
+ ack_packet(regs);
+ }
+
+ file->tftp_bytesleft = buffersize;
+ file->tftp_dataptr = file->tftp_pktbuf;
+ memcpy(MK_PTR(PKTBUF_SEG, file->tftp_pktbuf), data, buffersize);
+ goto done_pkt;
+
+ } else if (opcode == TFTP_OACK) {
+ /*
+ * Now we need to parse the OACK packet to get the transfer
+ * and packet sizes.
+ */
+ if (!buffersize)
+ goto done_pkt; /* No options acked */
+
+ /*
+ * If we find an option which starts with a NUL byte,
+ * (a null option), we're either seeing garbage that some
+ * TFTP servers add to the end of the packet, or we have
+ * no clue how to parse the rest of the packet (what is
+ * an option name and what is a value?) In either case,
+ * discard the rest.
+ */
+ options = packet_buf + 2;
+ if (*options == 0)
+ goto done_pkt;
+
+ dst = src = options;
+ while (*src) {
+ *dst++ = *src++ | 0x20;
+ buffersize--;
+ if (!buffersize)
+ goto done_pkt; /* found no final null */
+ }
+
+ /*
+ * Parse option pointed to by options; guaranteed to be null-terminated
+ */
+ p = options;
+ do {
+ tftp_opt = tftp_options;
+ for (i = 0; i < tftp_opts; i++) {
+ if (!strncmp(p, tftp_opt->str_ptr,tftp_opt->str_len))
+ break;
+ tftp_opt++;
+ }
+ if (i == tftp_opts)
+ goto err_reply; /* Non-negotitated option returned, no idea what it means ...*/
+ p += tftp_opt->str_len;
+ buffersize -= tftp_opt->str_len;
+ /* get the address of the filed that we want to write on */
+ data_ptr = (uint16_t *)((char *)file + tftp_opt->offset);
+
+ /* do convert a number-string to decimal number, just like atoi */
+ while (*p) {
+ if (*p > '9')
+ goto err_reply; /* Not a decimal digit */
+ *data_ptr = *data_ptr * 10 + *p - '0';
+
+ buffersize --;
+ if (!buffersize)
+ goto done_pkt;
+ p++;
+ }
+
+ }while (buffersize);
+
+ } else {
+ extern char tftp_proto_err[];
+ err_reply:
+
+ uw_pkt->rport = file->tftp_remoteport;
+ uw_pkt->buffer[0] = OFFS_WRT(tftp_proto_err, 0);
+ uw_pkt->buffer[1] = 0;
+ uw_pkt->buffersize = 24;
+ pxe_call(PXENV_UDP_WRITE, uw_pkt);
+ printf("TFTP server sent an incomprehesible reply\n");
+ call16(kaboom, NULL, NULL);
+ }
+
+ done_pkt:
+ regs->eax.l = file->tftp_filesize;
+ got_file:
+ regs->eflags.l &= ~EFLAGS_ZF;
+ if (!file->tftp_filesize) {
+ error:
+ regs->esi.w[0] = OFFS_WRT(file, 0);
+ free_socket(regs);
+ regs->eflags.l |= EFLAGS_ZF;
+ }
+ return;
-
+
+ failure:
+ timeout_ptr++;
+ if (*timeout_ptr)
+ goto sendreq; /* Try again */
+}
+
+
+
/*
* Network-specific initialization
*/
diff --git a/core/pxelinux.asm b/core/pxelinux.asm
index 5e593d75..b4a61e94 100644
--- a/core/pxelinux.asm
+++ b/core/pxelinux.asm
@@ -218,6 +218,7 @@ pxe_unload_stack_pkt_len equ $-pxe_unload_stack_pkt
; BOOTP/DHCP packet buffer
section .bss16
+ global packet_buf
alignb 16
packet_buf resb 2048 ; Transfer packet
packet_buf_size equ $-packet_buf
@@ -448,6 +449,11 @@ close_file:
mov word [si],0 ; Not in use
.closed: ret
+%if 0
+searchdir:
+ pm_call searchdir_c
+ ret
+%else
;
; searchdir:
;
@@ -781,6 +787,8 @@ searchdir:
jmp .ret
+
+
%if GPXE
.gpxe:
push bx ; Socket pointer
@@ -813,6 +821,8 @@ searchdir:
jmp .got_file
%endif ; GPXE
+%endif
+
%if GPXE
;
; is_gpxe: Return CF=0 if and only if the buffer pointed to by
@@ -1190,7 +1200,7 @@ pxe_udp_read_pkt:
%if GPXE
section .data16
-
+ global gpxe_file_api_check
gpxe_file_api_check:
.status: dw 0 ; Status
.size: dw 20 ; Size in bytes
@@ -1200,7 +1210,8 @@ gpxe_file_api_check:
.flags: dd 0
section .bss16
- global gpxe_file_read
+ global gpxe_file_read, gpxe_get_file_size
+ global gpxe_file_open
gpxe_file_open:
.status: resw 1 ; Status
@@ -1259,6 +1270,7 @@ tftp_opts equ ($-tftp_opt_table)/6
; Error packet to return on TFTP protocol error
; Most of our errors are OACK parsing errors, so use that error code
;
+ global tftp_proto_err, tftp_proto_err_len
tftp_proto_err dw TFTP_ERROR ; ERROR packet
dw TFTP_EOPTNEG ; ERROR 8: OACK error
db 'TFTP protocol error', 0 ; Error message
@@ -1270,7 +1282,7 @@ ack_packet_buf: dw TFTP_ACK, 0 ; TFTP ACK packet
;
; IP information (initialized to "unknown" values)
- global MyIP, ServerIP, Netmask, Gateway
+ global MyIP, ServerIP, Netmask, Gateway, ServerPort
MyIP dd 0 ; My IP address
ServerIP dd 0 ; IP address of boot server
Netmask dd 0 ; Netmask of this subnet