aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLiu Aleaxander <Aleaxander@gmail.com>2009-07-30 16:24:35 +0800
committerLiu Aleaxander <Aleaxander@gmail.com>2009-07-30 16:24:35 +0800
commit0df8459206c0f622442af76e80d5851100d30318 (patch)
treeae2a86770fbcc2bdef768ff8fc4187ff66c4074e
parent8f8ea4e6b4051c947c9c3d6f2cc1c563beced56b (diff)
downloadpxelinux-0df8459206c0f622442af76e80d5851100d30318.tar.gz
pxelinux-0df8459206c0f622442af76e80d5851100d30318.tar.xz
pxelinux-0df8459206c0f622442af76e80d5851100d30318.zip
Core:PXELINUX: trying to convert getfssec and fill_buffer
-rw-r--r--core/bios.inc1
-rw-r--r--core/extern.inc1
-rw-r--r--core/layout.inc2
-rw-r--r--core/loadhigh.inc2
-rw-r--r--core/pxe.c230
-rw-r--r--core/pxelinux.asm17
6 files changed, 242 insertions, 11 deletions
diff --git a/core/bios.inc b/core/bios.inc
index e8d62c7e..d1f83a6c 100644
--- a/core/bios.inc
+++ b/core/bios.inc
@@ -30,6 +30,7 @@ serial_base resw 4 ; Base addresses for 4 serial ports
BIOS_fbm resw 1 ; Free Base Memory (kilobytes)
absolute 0462h
BIOS_page resb 1 ; Current video page
+ global BIOS_timer
absolute 046Ch
BIOS_timer resw 1 ; Timer ticks
absolute 0472h
diff --git a/core/extern.inc b/core/extern.inc
index 1d52a0b8..236e6cc7 100644
--- a/core/extern.inc
+++ b/core/extern.inc
@@ -19,6 +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_c, fill_buffer
; debug.c
extern debug, print_reg
diff --git a/core/layout.inc b/core/layout.inc
index 3f5a7457..44c148f0 100644
--- a/core/layout.inc
+++ b/core/layout.inc
@@ -129,7 +129,7 @@ auxseg resb aux_size
; ISOLINUX doesn't have a block cache yet
real_mode_seg equ 4000h
%else
- global cache_seg, core_cache_buf
+ global cache_seg, core_cache_buf, pktbuf_seg
cache_seg equ 4000h ; 64K area for metadata cache
core_cache_buf equ cache_seg << 4
real_mode_seg equ 5000h
diff --git a/core/loadhigh.inc b/core/loadhigh.inc
index 5c9969f6..42a1354b 100644
--- a/core/loadhigh.inc
+++ b/core/loadhigh.inc
@@ -69,7 +69,7 @@ load_high:
push edi ; <C> Target buffer
mov cx,ax
xor bx,bx ; ES:0
- call getfssec ; Load the data into xfer_buf_seg
+ call getfssec ; Load the data into xfer_buf_seg
pop edi ; <C> Target buffer
pushf ; <C> EOF status
lea ebx,[edi+ecx] ; End of data
diff --git a/core/pxe.c b/core/pxe.c
index 450313c2..da989f94 100644
--- a/core/pxe.c
+++ b/core/pxe.c
@@ -9,6 +9,8 @@
#define FILENAME_MAX (1 << FILENAME_MAX_LG2)
#define PKTBUF_SIZE (65536 / MAX_OPEN)
+#define TFTP_BLOCKSIZE_LG2 9
+
#define DNS_MAX_SERVERS 4
#define MAX(a,b) (a > b ? a : b)
@@ -23,6 +25,29 @@
( ((x) & 0xff0000) >> 8 ) + ( ((x) & 0xff000000) >> 24) )
#define ntohl(x) htonl(x)
+/*
+ * TFTP operation codes
+ */
+#define TFTP_RRQ htons(1) // Read rest
+#define TFTP_WRQ htons(2) // Write rest
+#define TFTP_DATA htons(3) // Data packet
+#define TFTP_ACK htons(4) // ACK packet
+#define TFTP_ERROR htons(5) // ERROR packet
+#define TFTP_OACK htons(6) // OACK packet
+
+/*
+ * TFTP error codes
+ */
+#define TFTP_EUNDEF htons(0) // Unspecified error
+#define TFTP_ENOTFOUND htons(1) // File not found
+#define TFTP_EACCESS htons(2) // Access violation
+#define TFTP_ENOSPACE htons(3) // Disk full
+#define TFTP_EBADOP htons(4) // Invalid TFTP operation
+#define TFTP_EBADID htons(5) // Unknown transfer
+#define TFTP_EEXISTS htons(6) // File exists
+#define TFTP_ENOUSER htons(7) // No such user
+#define TFTP_EOPTNEG htons(8) // Option negotiation failure
+
#define BOOTP_OPTION_MAGIC htonl(0x63825363)
struct bootp_t {
@@ -71,6 +96,17 @@ struct pxe_udp_write_pkt {
} __attribute__ ((packed));
extern char pxe_udp_write_pkt[];
+struct pxe_udp_read_pkt {
+ uint16_t status;
+ uint32_t sip;
+ uint32_t dip;
+ uint16_t rport;
+ uint16_t lport;
+ uint16_t buffersize;
+ uint16_t buffer[2];
+} __attribute__ ((packed));
+extern char pxe_udp_read_pkt[];
+
struct pxe_bootp_query_pkt {
uint16_t status;
uint16_t packettype;
@@ -166,6 +202,11 @@ struct stack {
extern char BaseStack[];
struct stack *stack = (struct stack *)BaseStack;
+extern uint16_t volatile BIOS_timer;
+uint8_t TimeoutTable[] = {2, 2, 3, 3, 4, 5, 6, 7, 9, 10, 12, 15, 18,
+ 21, 26, 31, 37, 44, 53, 64, 77, 92, 110, 132,
+ 159, 191, 229, 255, 255, 255, 255, 0};
+
inline void pusha(com32sys_t *regs)
{
@@ -717,6 +758,7 @@ void free_socket(com32sys_t *regs)
}
+
/************
;
; Tests an IP address in EAX for validity; return with ZF=1 for bad.
@@ -1232,7 +1274,187 @@ void get_packet_gpxe(com32sys_t *regs)
}
#endif /* GPXE */
+#if 1
+/*
+ *
+ ;
+ ; Get a fresh packet if the buffer is drained, and we haven't hit
+ ; EOF yet. The buffer should be filled immediately after draining!
+ ;
+ ; expects fs -> pktbuf_seg and ds:si -> socket structure
+ ;
+*/
+void fill_buffer(com32sys_t *regs)
+{
+ int err;
+ int last_pkt;
+ uint8_t *timeout_ptr = TimeoutTable;
+ uint8_t timeout;
+ uint16_t buffersize;
+ uint16_t old_time;
+ void *data = NULL;
+ struct pxe_udp_read_pkt *pkt = (struct pxe_udp_read_pkt *)pxe_udp_read_pkt;
+ struct open_file_t *file = (struct open_file_t *)MK_PTR(regs->ds, regs->esi.w[0]);
+
+ if (file->tftp_bytesleft || file->tftp_goteof)
+ return;
+
+#if GPXE
+ if (file->tftp_localport == 0xffff) {
+ get_packet_gpxe(regs);
+ return;
+ }
+#endif
+
+
+ /*
+ * Start by ACKing the previous packet; this should cause
+ * the next packet to be sent.
+ */
+ again:
+ regs->eax.w[0] = file->tftp_lastpkt;
+ ack_packet(regs);
+
+ timeout_ptr = TimeoutTable;
+ timeout = *timeout_ptr++;
+ old_time = BIOS_timer;
+ while (timeout) {
+ pkt->buffer[0] = file->tftp_pktbuf;
+ pkt->buffer[1] = regs->fs;
+ pkt->buffersize = PKTBUF_SIZE;
+ pkt->sip = file->tftp_remoteip;
+ pkt->dip = MyIP;
+ pkt->rport = file->tftp_remoteport;
+ pkt->lport = file->tftp_localport;
+ err = pxe_call(PXENV_UDP_READ, pkt);
+ if (err) {
+ if (BIOS_timer == old_time)
+ continue;
+
+ timeout --; /* decrease one second */
+ if (!timeout)
+ timeout = *timeout_ptr++;
+ continue;
+ }
+
+ if (pkt->buffersize < 4) /* Bad size for a DATA packet */
+ continue;
+
+ data = MK_PTR(regs->fs, file->tftp_pktbuf);
+ if (*(uint16_t *)data != TFTP_DATA) /* Not a data packet */
+ continue;
+
+ /* If goes here, recevie OK, break */
+ break;
+ }
+
+ /* time runs out */
+ if (timeout == 0)
+ call16(kaboom, NULL, NULL);
+
+ last_pkt = file->tftp_lastpkt;
+ last_pkt = ntohs(last_pkt); /* Host byte order */
+ last_pkt ++;
+ last_pkt = htons(last_pkt); /* Network byte order */
+ if (*(uint16_t *)(data + 2) != last_pkt) {
+ /*
+ * Wrong packet, ACK the packet and try again.
+ * This is presumably because the ACK got lost,
+ * so the server just resent the previous packet.
+ */
+ regs->eax.w[0] = *(uint16_t *)(data + 2);
+ ack_packet(regs);
+ goto again;
+ }
+
+ /* It's the packet we want. We're also EOF if the size < blocksize */
+ file->tftp_lastpkt = last_pkt; /* Update last packet number */
+ buffersize = pkt->buffersize - 4; /* Skip TFTP header */
+ file->tftp_dataptr = file->tftp_pktbuf + 4;
+ file->tftp_filepos += buffersize;
+ file->tftp_bytesleft = buffersize;
+ if (buffersize < file->tftp_blksize) {
+ /* it's the last block, ACK packet immediately */
+ regs->eax.w[0] = *(uint16_t *)data;
+ ack_packet(regs);
+
+ /* Make sure we know we are at end of file */
+ file->tftp_filesize = file->tftp_filepos;
+ file->tftp_goteof = 1;
+ }
+
+ return;
+}
+
+
+
+/*
+ *
+ ;
+ ; getfssec: Get multiple clusters from a file, given the starting cluster.
+ ;
+ ; In this case, get multiple blocks from a specific TCP connection.
+ ;
+ ; On entry:
+ ; ES:BX -> Buffer
+ ; SI -> TFTP socket pointer
+ ; CX -> 512-byte block count; 0FFFFh = until end of file
+ ; On exit:
+ ; SI -> TFTP socket pointer (or 0 on EOF)
+ ; CF = 1 -> Hit EOF
+ ; ECX -> number of bytes actually read
+ ;
+*/
+void getfssec_c(com32sys_t *regs)
+{
+ uint16_t pktbuf_seg = 0x4000;
+ char *buf = (char *)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];
+ int chunk;
+ int bytes_read = 0;
+
+ regs->fs = pktbuf_seg;
+
+ count <<= TFTP_BLOCKSIZE_LG2;
+ while (count) {
+ fill_buffer(regs);
+ if (! file->tftp_bytesleft)
+ break;
+
+ chunk = count;
+ if (chunk > file->tftp_bytesleft)
+ chunk = file->tftp_bytesleft;
+ file->tftp_bytesleft -= chunk;
+ memcpy(buf, (void *)MK_PTR(regs->fs, file->tftp_dataptr), chunk);
+ buf += chunk;
+ bytes_read += chunk;
+ count -= chunk;
+ }
+
+ fill_buffer(regs);
+ regs->ecx.l = bytes_read; /* number of bytes actually read */
+
+ if (file->tftp_bytesleft || (file->tftp_filepos < file->tftp_filesize)) {
+ regs->eflags.l &= ~EFLAGS_CF;
+ return;
+ }
+
+ if (file->tftp_goteof) {
+ /*
+ * The socket is closed and the buffer dranined
+ * close socket structure and re-init for next user
+ */
+ free_socket(regs);
+ regs->eflags.l |= EFLAGS_CF; /* Hit EOF */
+ return;
+ }
+
+ /* 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
@@ -1326,7 +1548,7 @@ void pxe_init(com32sys_t *regs)
int plan = 'A';
uint16_t code_seg, code_len;
uint16_t data_seg, data_len;
- void *base = (init_stack[1] << 4) + init_stack[0];
+ void *base = (void *)((init_stack[1] << 4) + init_stack[0]);
/* Assume API version 2.1 */
APIVer = 0x201;
@@ -1377,7 +1599,7 @@ void pxe_init(com32sys_t *regs)
have_pxenv:
StructPtr[0] = regs->ebx.w[0];
StructPtr[1] = regs->es;
- base = (StructPtr[1] << 4) + StructPtr[0];
+ base = (void *)((StructPtr[1] << 4) + StructPtr[0]);
APIVer = *(uint16_t *)(base + 6);
printf("Found PXENV+ structure\nPXE API version is %04x\n", APIVer);
@@ -1412,7 +1634,7 @@ void pxe_init(com32sys_t *regs)
have_pxe:
StructPtr[0] = regs->ebx.w[0];
StructPtr[1] = regs->es;
- base = (StructPtr[1] << 4) + StructPtr[0];
+ base = (void *)((StructPtr[1] << 4) + StructPtr[0]);
data_len = *(uint16_t *)(base + 0x2e); /* UNDI data len */
data_seg = *(uint16_t *)(base + 0x28); /* UNDI data seg */
@@ -1431,7 +1653,7 @@ void pxe_init(com32sys_t *regs)
RealBaseMem = MAX(code_seg,data_seg) >> 6; /* Convert to kilobytes */
- regs->es = regs->ds; /* Restore teh es segment */
+ regs->es = regs->ds; /* Restore the es segment */
}
diff --git a/core/pxelinux.asm b/core/pxelinux.asm
index 298ffa33..7a1f6f76 100644
--- a/core/pxelinux.asm
+++ b/core/pxelinux.asm
@@ -949,6 +949,11 @@ PXEEntry equ pxenv.jump+1
PXEStatus resb 2
section .text16
+%if 1
+getfssec:
+ pm_call getfssec_c
+ ret
+%else
;
; getfssec: Get multiple clusters from a file, given the starting cluster.
@@ -981,7 +986,7 @@ getfssec:
jz .hit_eof ; Nothing to do?
.need_more:
- call fill_buffer
+ pm_call fill_buffer
movzx eax,word [si+tftp_bytesleft]
and ax,ax
jz .hit_eof
@@ -1004,7 +1009,7 @@ getfssec:
jnz .need_more
.hit_eof:
- call fill_buffer
+ pm_call fill_buffer
pop eax ; Initial request amount
xchg eax,ecx
@@ -1021,10 +1026,10 @@ getfssec:
cmp [si+tftp_bytesleft],ax ; AX == 0
jne .bytes_left
- cmp byte [si+tftp_goteof],0
+ cmp byte [si+tftp_goteof],1
je .done
; I'm 99% sure this can't happen, but...
- call fill_buffer ; Receive/ACK the EOF packet
+ pm_call fill_buffer ; Receive/ACK the EOF packet
.done:
; The socket is closed and the buffer drained
; Close socket structure and re-init for next user
@@ -1062,6 +1067,7 @@ fill_buffer:
; having gotten the official EOF.
cmp byte [si+tftp_goteof],0
jne .ret ; Already EOF
+
%if GPXE
cmp word [si+tftp_localport], -1
jne .get_packet_tftp
@@ -1185,6 +1191,7 @@ fill_buffer:
mov byte [si+tftp_goteof],1
jmp .ret
+%endif
;
; TimeoutTable: list of timeouts (in 18.2 Hz timer ticks)
@@ -1391,7 +1398,7 @@ old_api_unload:
;
section .bss16
global pxe_bootp_query_pkt, pxe_udp_write_pkt
- global pxe_udp_open_pkt
+ global pxe_udp_open_pkt, pxe_udp_read_pkt
pxe_bootp_query_pkt:
.status: resw 1 ; Status
.packettype: resw 1 ; Boot server packet type