Index: libi386/libi386.h =================================================================== --- libi386/libi386.h (revision 188186) +++ libi386/libi386.h (working copy) @@ -65,6 +65,7 @@ extern struct devsw biosdisk; extern struct devsw pxedisk; extern struct fs_ops pxe_fsops; +extern struct fs_ops pxetftp_fsops; int bc_add(int biosdev); /* Register CD booted from. */ int bc_getdev(struct i386_devdesc *dev); /* return dev_t for (dev) */ Index: libi386/pxetftp.c =================================================================== --- libi386/pxetftp.c (revision 188186) +++ libi386/pxetftp.c (working copy) @@ -1,3 +1,4 @@ +#define PXE_DEBUGx /*- * Copyright (c) 2000 Alfred Perlstein * Copyright (c) 2000 Paul Saab @@ -2,2 +3,3 @@ * Copyright (c) 2000 John Baldwin + * Copyright (c) 2008 Peter Wemm * All rights reserved. @@ -46,23 +48,29 @@ #include #include "btxv86.h" #include "pxe.h" +#include "tftp.h" +#include +#include +#include + + /* * Allocate the PXE buffers statically instead of sticking grimy fingers into * BTX's private data area. The scratch buffer is used to send information to * the PXE BIOS, and the data buffer is used to receive data from the PXE BIOS. */ #define PXE_BUFFER_SIZE 0x2000 -#define PXE_TFTP_BUFFER_SIZE 512 static char scratch_buffer[PXE_BUFFER_SIZE]; static char data_buffer[PXE_BUFFER_SIZE]; +#define TFTP_BUFFER_SIZE 1420 +#define TFTP_DEF_SEGSIZE 512 static pxenv_t *pxenv_p = NULL; /* PXENV+ */ static pxe_t *pxe_p = NULL; /* !PXE */ static BOOTPLAYER bootplayer; /* PXE Cached information. */ -static int pxe_debug = 0; -static int pxe_sock = -1; +static int pxe_debug = 1; static int pxe_opens = 0; void pxe_enable(void *pxeinfo); @@ -80,15 +88,14 @@ static void pxe_setnfshandle(char *rootpath); static void pxe_perror(int error); -static int pxe_netif_match(struct netif *nif, void *machdep_hint); -static int pxe_netif_probe(struct netif *nif, void *machdep_hint); -static void pxe_netif_init(struct iodesc *desc, void *machdep_hint); -static int pxe_netif_get(struct iodesc *desc, void *pkt, size_t len, - time_t timeout); -static int pxe_netif_put(struct iodesc *desc, void *pkt, size_t len); -static void pxe_netif_end(struct netif *nif); -extern struct netif_stats pxe_st[]; +static int tftp_open(const char *path, struct open_file *f); +static int tftp_close(struct open_file *f); +static int tftp_read(struct open_file *f, void *buf, size_t size, size_t *resid); +static int tftp_write(struct open_file *f, void *buf, size_t size, size_t *resid); +static off_t tftp_seek(struct open_file *f, off_t offset, int where); +static int tftp_stat(struct open_file *f, struct stat *sb); + extern u_int16_t __bangpxeseg; extern u_int16_t __bangpxeoff; extern void __bangpxeentry(void); @@ -96,27 +103,7 @@ extern u_int16_t __pxenvoff; extern void __pxenventry(void); -struct netif_dif pxe_ifs[] = { -/* dif_unit dif_nsel dif_stats dif_private */ - {0, 1, &pxe_st[0], 0} -}; - -struct netif_stats pxe_st[NENTS(pxe_ifs)]; - -struct netif_driver pxenetif = { - "pxenet", - pxe_netif_match, - pxe_netif_probe, - pxe_netif_init, - pxe_netif_get, - pxe_netif_put, - pxe_netif_end, - pxe_ifs, - NENTS(pxe_ifs) -}; - struct netif_driver *netif_drivers[] = { - &pxenetif, NULL }; @@ -132,6 +119,17 @@ pxe_cleanup }; + +struct fs_ops pxetftp_fsops = { + "pxetftp", + tftp_open, + tftp_close, + tftp_read, + tftp_write, + tftp_seek, + tftp_stat, + null_readdir +}; /* * This function is called by the loader to enable PXE support if we * are booted by PXE. The passed in pointer is a pointer to the @@ -251,27 +249,13 @@ pxe_open(struct open_file *f, ...) { va_list args; - char *devname; /* Device part of file name (or NULL). */ char temp[FNAME_SIZE]; int error = 0; int i; - - va_start(args, f); - devname = va_arg(args, char*); - va_end(args); - /* On first open, do netif open, mount, etc. */ + +/*printf("pxe_opens == %d\n", pxe_opens); */ if (pxe_opens == 0) { - /* Find network interface. */ - if (pxe_sock < 0) { - pxe_sock = netif_open(devname); - if (pxe_sock < 0) { - printf("pxe_open: netif_open() failed\n"); - return (ENXIO); - } - if (pxe_debug) - printf("pxe_open: netif_open() succeeded\n"); - } if (rootip.s_addr == 0) { /* * Do a bootp/dhcp request to find out where our @@ -279,9 +263,24 @@ * the proper information, fall back to the server * which brought us to life and a default rootpath. */ +#if 0 bootp(pxe_sock, BOOTP_PXE); +#endif + +#if 0 + IP4_t cip; /* Client IP */ + IP4_t yip; /* Your IP */ + IP4_t sip; /* IP to use for next boot stage */ + IP4_t gip; /* Relay IP ? */ +#endif + if (rootip.s_addr == 0) rootip.s_addr = bootplayer.sip; + if (gateip.s_addr == 0) + gateip.s_addr = bootplayer.gip; + if (myip.s_addr == 0) + myip.s_addr = bootplayer.yip; + if (!rootpath[1]) strcpy(rootpath, PXENFSROOTPATH); @@ -295,9 +294,11 @@ bcopy(&rootpath[i], &temp[0], strlen(&rootpath[i])+1); bcopy(&temp[0], &rootpath[0], strlen(&rootpath[i])+1); } + printf("pxe_open: my addr: %s\n", inet_ntoa(myip)); printf("pxe_open: server addr: %s\n", inet_ntoa(rootip)); printf("pxe_open: server path: %s\n", rootpath); printf("pxe_open: gateway ip: %s\n", inet_ntoa(gateip)); + printf("pxe_open: gatehops: %d\n", bootplayer.Gatehops); setenv("boot.netif.ip", inet_ntoa(myip), 1); setenv("boot.netif.netmask", intoa(netmask), 1); @@ -310,9 +311,9 @@ setenv("boot.nfsroot.path", rootpath, 1); setenv("dhcp.host-name", hostname, 1); } - } +} pxe_opens++; - f->f_devdata = &pxe_sock; + f->f_devdata = NULL; return (error); } @@ -325,7 +326,6 @@ printf("pxe_close: opens=%d\n", pxe_opens); #endif - /* On last close, do netif close, etc. */ f->f_devdata = NULL; /* Extra close call? */ if (pxe_opens <= 0) @@ -335,20 +335,6 @@ if (pxe_opens > 0) return(0); -#ifdef LOADER_NFS_SUPPORT - /* get an NFS filehandle for our root filesystem */ - pxe_setnfshandle(rootpath); -#endif - - if (pxe_sock >= 0) { - -#ifdef PXE_DEBUG - if (pxe_debug) - printf("pxe_close: calling netif_close()\n"); -#endif - netif_close(pxe_sock); - pxe_sock = -1; - } return (0); } @@ -375,21 +361,11 @@ #ifdef PXE_DEBUG t_PXENV_UNLOAD_STACK *unload_stack_p = (t_PXENV_UNLOAD_STACK *)scratch_buffer; - t_PXENV_UNDI_SHUTDOWN *undi_shutdown_p = - (t_PXENV_UNDI_SHUTDOWN *)scratch_buffer; #endif if (pxe_call == NULL) return; - pxe_call(PXENV_UNDI_SHUTDOWN); - -#ifdef PXE_DEBUG - if (pxe_debug && undi_shutdown_p->Status != 0) - printf("pxe_cleanup: UNDI_SHUTDOWN failed %x\n", - undi_shutdown_p->Status); -#endif - pxe_call(PXENV_UNLOAD_STACK); #ifdef PXE_DEBUG @@ -405,62 +381,10 @@ return; } -/* - * Reach inside the libstand NFS code and dig out an NFS handle - * for the root filesystem. - */ -struct nfs_iodesc { - struct iodesc *iodesc; - off_t off; - u_char fh[NFS_FHSIZE]; - /* structure truncated here */ -}; -extern struct nfs_iodesc nfs_root_node; -extern int rpc_port; - -static void -pxe_rpcmountcall() -{ - struct iodesc *d; - int error; - - if (!(d = socktodesc(pxe_sock))) - return; - d->myport = htons(--rpc_port); - d->destip = rootip; - if ((error = nfs_getrootfh(d, rootpath, nfs_root_node.fh)) != 0) - printf("NFS MOUNT RPC error: %d\n", error); - nfs_root_node.iodesc = d; -} - -static void -pxe_setnfshandle(char *rootpath) -{ - int i; - u_char *fh; - char buf[2 * NFS_FHSIZE + 3], *cp; - - /* - * If NFS files were never opened, we need to do mount call - * ourselves. Use nfs_root_node.iodesc as flag indicating - * previous NFS usage. - */ - if (nfs_root_node.iodesc == NULL) - pxe_rpcmountcall(); - - fh = &nfs_root_node.fh[0]; - buf[0] = 'X'; - cp = &buf[1]; - for (i = 0; i < NFS_FHSIZE; i++, cp += 2) - sprintf(cp, "%02x", fh[i]); - sprintf(cp, "X"); - setenv("boot.nfsroot.nfshandle", buf, 1); -} - void pxenv_call(int func) { -#ifdef PXE_DEBUG +#ifdef PXE_DEBUG_x if (pxe_debug) printf("pxenv_call %x\n", func); #endif @@ -483,7 +407,7 @@ void bangpxe_call(int func) { -#ifdef PXE_DEBUG +#ifdef PXE_DEBUG_x if (pxe_debug) printf("bangpxe_call %x\n", func); #endif @@ -512,125 +436,317 @@ return n; } -static int -pxe_netif_match(struct netif *nif, void *machdep_hint) +int +pxetftpopen(const char *filename, int pktsize) { - return 1; -} + t_PXENV_TFTP_OPEN *tftpopen_p = (t_PXENV_TFTP_OPEN *)scratch_buffer; + tftpopen_p->ServerIPAddress = bootplayer.sip; +#if 0 + if (bootplayer.Gatehops != 0) + tftpopen_p->GatewayIPAddress = bootplayer.gip; + else +#endif + tftpopen_p->GatewayIPAddress = 0; -static int -pxe_netif_probe(struct netif *nif, void *machdep_hint) -{ - t_PXENV_UDP_OPEN *udpopen_p = (t_PXENV_UDP_OPEN *)scratch_buffer; + strcpy(tftpopen_p->FileName, filename); + tftpopen_p->TFTPPort = htons(IPPORT_TFTP); + tftpopen_p->PacketSize = pktsize; - if (pxe_call == NULL) + pxe_call(PXENV_TFTP_OPEN); + + if (tftpopen_p->Status != 0) { + if (tftpopen_p->Status != 0x3b && + tftpopen_p->Status != 0x01) + printf("pxetftpopen '%s' failed %#x\n", filename, tftpopen_p->Status); return -1; + } +/* printf("pxetftpopen: returned packet size %d, requested %d\n", tftpopen_p->PacketSize, pktsize); */ + return 0; +} - bzero(udpopen_p, sizeof(*udpopen_p)); - udpopen_p->src_ip = bootplayer.yip; - pxe_call(PXENV_UDP_OPEN); +int +pxetftpclose(void) +{ + t_PXENV_TFTP_CLOSE *tftpclose_p = (t_PXENV_TFTP_CLOSE *)scratch_buffer; - if (udpopen_p->status != 0) { - printf("pxe_netif_probe: failed %x\n", udpopen_p->status); + pxe_call(PXENV_TFTP_CLOSE); + + if (tftpclose_p->Status != 0) { + printf("pxetftpclose failed %#x\n", tftpclose_p->Status); return -1; } return 0; } -static void -pxe_netif_end(struct netif *nif) +int +pxetftpread(void *buf, int *len, int *pkt) { - t_PXENV_UDP_CLOSE *udpclose_p = (t_PXENV_UDP_CLOSE *)scratch_buffer; - bzero(udpclose_p, sizeof(*udpclose_p)); + t_PXENV_TFTP_READ *tftpread_p = (t_PXENV_TFTP_READ *)scratch_buffer; - pxe_call(PXENV_UDP_CLOSE); - if (udpclose_p->status != 0) - printf("pxe_end failed %x\n", udpclose_p->status); + tftpread_p->PacketNumber = 0; /* insurance */ + tftpread_p->BufferSize = PXE_BUFFER_SIZE; + tftpread_p->Buffer.segment = VTOPSEG(data_buffer); + tftpread_p->Buffer.offset = VTOPOFF(data_buffer); + + pxe_call(PXENV_TFTP_READ); + + if (tftpread_p->Status != 0) + return (tftpread_p->Status); + bcopy(data_buffer, buf, tftpread_p->BufferSize); + *len = tftpread_p->BufferSize; + *pkt = tftpread_p->PacketNumber; + return (0); } -static void -pxe_netif_init(struct iodesc *desc, void *machdep_hint) +int +pxetftpfsize(char *path, int *size) { - int i; - for (i = 0; i < 6; ++i) - desc->myea[i] = bootplayer.CAddr[i]; - desc->xid = bootplayer.ident; + t_PXENV_TFTP_GET_FSIZE *tftpfsize_p = (t_PXENV_TFTP_GET_FSIZE *)scratch_buffer; + + tftpfsize_p->ServerIPAddress = bootplayer.sip; +#if 0 + if (bootplayer.Gatehops != 0) + tftpfsize_p->GatewayIPAdress = bootplayer.gip; + else +#endif + tftpfsize_p->GatewayIPAdress = 0; + + strcpy(tftpfsize_p->FileName, path); + + pxe_call(PXENV_TFTP_GET_FSIZE); + + if (tftpfsize_p->Status != 0) { + printf("pxetftpfsize failed %#x\n", tftpfsize_p->Status); + return -1; + } + printf("returned file size %d\n", tftpfsize_p->FileSize); + *size = tftpfsize_p->FileSize; + return 0; } + +struct tftp_handle { + int reqblksize; + int blocksize; + int currblock; /* contents of lastdata */ + int islastblock; /* flag */ + int validsize; + int off; + char *path; /* saved for re-requests */ + char lastdata[TFTP_BUFFER_SIZE]; +}; + static int -pxe_netif_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout) +tftp_nextblock(struct tftp_handle *tftpfile) { - return len; + int res; + int val; + int gotblock; + +/* printf("asking for nextblock. have block %d, valid %d\n", tftpfile->currblock, tftpfile->validsize); */ + val = TFTP_BUFFER_SIZE; + res = pxetftpread(tftpfile->lastdata, &val, &gotblock); + if (res) + return (res); + tftpfile->validsize = val; + tftpfile->currblock = gotblock; + tftpfile->islastblock = 0; + if (tftpfile->blocksize != val && gotblock == 1 && val == TFTP_DEF_SEGSIZE) { + /* might be a short file. assume no less than 512. */ + tftpfile->blocksize = val; + } + if (val < tftpfile->blocksize) + tftpfile->islastblock = 1; +/* printf("got: %d bytes for block %d\n", val, gotblock); */ + delay(1000); /* huh? why does this work? does pxe hook the bios idle function? */ + return (0); } -static int -pxe_netif_put(struct iodesc *desc, void *pkt, size_t len) +static int +tftp_open(path, f) + const char *path; + struct open_file *f; { - return len; + struct tftp_handle *tftpfile; + int res; + + if (f->f_dev != &pxedisk) + return (EINVAL); + + tftpfile = (struct tftp_handle *) malloc(sizeof(*tftpfile)); + if (!tftpfile) + return (ENOMEM); + + tftpfile->off = 0; + tftpfile->path = strdup(path); + if (tftpfile->path == NULL) { + free(tftpfile); + return(ENOMEM); + } + + tftpfile->validsize = 0; + tftpfile->reqblksize = TFTP_BUFFER_SIZE; + tftpfile->blocksize = TFTP_BUFFER_SIZE; + res = pxetftpopen(path, tftpfile->reqblksize); + if (res) { + /* printf("pxetftpopen fail\n"); */ + free(tftpfile->path); + free(tftpfile); + return (ENOENT); + } + tftpfile->currblock = 0; + res = tftp_nextblock(tftpfile); + if (res) { + printf("tftp_open: read first block failed %#x\n", res); + free(tftpfile->path); + free(tftpfile); + return (EIO); + } +/* printf("[BLOCKSIZE %s %d bytes/pkt]\n", tftpfile->path, tftpfile->blocksize); */ + f->f_fsdata = (void *) tftpfile; + return (0); } -ssize_t -sendudp(struct iodesc *h, void *pkt, size_t len) +static int +tftp_read(f, addr, size, resid) + struct open_file *f; + void *addr; + size_t size; + size_t *resid; /* out */ { - t_PXENV_UDP_WRITE *udpwrite_p = (t_PXENV_UDP_WRITE *)scratch_buffer; - bzero(udpwrite_p, sizeof(*udpwrite_p)); - - udpwrite_p->ip = h->destip.s_addr; - udpwrite_p->dst_port = h->destport; - udpwrite_p->src_port = h->myport; - udpwrite_p->buffer_size = len; - udpwrite_p->buffer.segment = VTOPSEG(pkt); - udpwrite_p->buffer.offset = VTOPOFF(pkt); + struct tftp_handle *tftpfile; + static int tc = 0; + tftpfile = (struct tftp_handle *) f->f_fsdata; + int res; - if (netmask == 0 || SAMENET(myip, h->destip, netmask)) - udpwrite_p->gw = 0; - else - udpwrite_p->gw = gateip.s_addr; + while (size > 0) { + int needblock, count; + int offinblock, inbuffer; - pxe_call(PXENV_UDP_WRITE); + if (!(tc++ % 100)) + twiddle(); + needblock = tftpfile->off / tftpfile->blocksize + 1; + + if (tftpfile->currblock > needblock) { + printf("tftp_read: cannot seek backwards yet!!!!\n"); + break; + } + while (tftpfile->currblock < needblock) { + if (tftpfile->islastblock) + goto eof; + res = tftp_nextblock(tftpfile); + if (res) { + if (res == 0x35 && /* timeout */ + tftpfile->currblock == 1 && + tftpfile->blocksize == TFTP_DEF_SEGSIZE && + tftpfile->blocksize < tftpfile->reqblksize) { + tftpfile->islastblock = 1; + /* printf("tftp_read: EOF hack to work around pxe rom bug!\n"); */ + goto eof; + } + printf("tftp_read: PXE rom returned error %#x\n", res); + return (EIO); + } + /* hexdump(tftpfile->lastdata, tftpfile->validsize); */ + } + + offinblock = tftpfile->off % tftpfile->blocksize; + + inbuffer = tftpfile->validsize - offinblock; + if (inbuffer < 0) { +#ifdef PXE_DEBUG + printf("tftp: invalid offset %d\n", + tftpfile->off); +#endif + return (EINVAL); + } + count = (size < inbuffer ? size : inbuffer); + bcopy(tftpfile->lastdata + offinblock, + addr, count); + + addr = (char *)addr + count; + tftpfile->off += count; + size -= count; + + if ((tftpfile->islastblock) && (count == inbuffer)) { +/* printf("TFTP_READ: eof! lastblock %d, count %d, inbuffer %d\n", tftpfile->islastblock, count, inbuffer); */ + eof: + break; /* EOF */ + } + } + + if (resid) + *resid = size; #if 0 - /* XXX - I dont know why we need this. */ - delay(1000); +if (size) +printf("TFTP_READ: resid %d\n", size); #endif - if (udpwrite_p->status != 0) { - /* XXX: This happens a lot. It shouldn't. */ - if (udpwrite_p->status != 1) - printf("sendudp failed %x\n", udpwrite_p->status); - return -1; + return (0); +} + +static int +tftp_close(f) + struct open_file *f; +{ + struct tftp_handle *tftpfile; + tftpfile = (struct tftp_handle *) f->f_fsdata; + + pxetftpclose(); + + if (tftpfile) { + free(tftpfile->path); + free(tftpfile); } - return len; + return (0); } -ssize_t -readudp(struct iodesc *h, void *pkt, size_t len, time_t timeout) +static int +tftp_write(f, start, size, resid) + struct open_file *f; + void *start; + size_t size; + size_t *resid; /* out */ { - t_PXENV_UDP_READ *udpread_p = (t_PXENV_UDP_READ *)scratch_buffer; - struct udphdr *uh = NULL; - - uh = (struct udphdr *) pkt - 1; - bzero(udpread_p, sizeof(*udpread_p)); - - udpread_p->dest_ip = h->myip.s_addr; - udpread_p->d_port = h->myport; - udpread_p->buffer_size = len; - udpread_p->buffer.segment = VTOPSEG(data_buffer); - udpread_p->buffer.offset = VTOPOFF(data_buffer); + return (EROFS); +} - pxe_call(PXENV_UDP_READ); +static int +tftp_stat(f, sb) + struct open_file *f; + struct stat *sb; +{ + struct tftp_handle *tftpfile; + tftpfile = (struct tftp_handle *) f->f_fsdata; -#if 0 - /* XXX - I dont know why we need this. */ - delay(1000); -#endif - if (udpread_p->status != 0) { - /* XXX: This happens a lot. It shouldn't. */ - if (udpread_p->status != 1) - printf("readudp failed %x\n", udpread_p->status); - return -1; + sb->st_mode = 0444 | S_IFREG; + sb->st_nlink = 1; + sb->st_uid = 0; + sb->st_gid = 0; + sb->st_size = -1; + return (0); +} + +static off_t +tftp_seek(f, offset, where) + struct open_file *f; + off_t offset; + int where; +{ + struct tftp_handle *tftpfile; + tftpfile = (struct tftp_handle *) f->f_fsdata; + + switch (where) { + case SEEK_SET: + tftpfile->off = offset; + break; + case SEEK_CUR: + tftpfile->off += offset; + break; + default: + errno = EOFFSET; + return (-1); } - bcopy(data_buffer, pkt, udpread_p->buffer_size); - uh->uh_sport = udpread_p->s_port; - return udpread_p->buffer_size; + return (tftpfile->off); } Index: libi386/Makefile =================================================================== --- libi386/Makefile (revision 188186) +++ libi386/Makefile (working copy) @@ -7,16 +7,22 @@ biospci.c biossmap.c bootinfo.c bootinfo32.c bootinfo64.c \ comconsole.c devicename.c elf32_freebsd.c \ elf64_freebsd.c \ - i386_copy.c i386_module.c nullconsole.c pxe.c pxetramp.s \ + i386_copy.c i386_module.c nullconsole.c pxetramp.s \ smbios.c time.c vidconsole.c amd64_tramp.S # Enable PXE TFTP or NFS support, not both. .if defined(LOADER_TFTP_SUPPORT) +.if defined(LOADER_PXETFTP_SUPPORT) +SRCS+= pxetftp.c +.else +SRCS+= pxe.c +.endif CFLAGS+= -DLOADER_TFTP_SUPPORT .else CFLAGS+= -DLOADER_NFS_SUPPORT .endif + BOOT_COMCONSOLE_PORT?= 0x3f8 CFLAGS+= -DCOMPORT=${BOOT_COMCONSOLE_PORT} Index: loader/conf.c =================================================================== --- loader/conf.c (revision 188186) +++ loader/conf.c (working copy) @@ -93,8 +93,12 @@ &nfs_fsops, #endif #ifdef LOADER_TFTP_SUPPORT +#ifdef LOADER_PXETFTP_SUPPORT + &pxetftp_fsops, +#else &tftp_fsops, #endif +#endif NULL }; Index: loader/Makefile =================================================================== --- loader/Makefile (revision 188186) +++ loader/Makefile (working copy) @@ -28,6 +28,9 @@ # Enable PXE TFTP or NFS support, not both. .if defined(LOADER_TFTP_SUPPORT) CFLAGS+= -DLOADER_TFTP_SUPPORT +.if defined(LOADER_PXETFTP_SUPPORT) +CFLAGS+= -DLOADER_PXETFTP_SUPPORT +.endif .else CFLAGS+= -DLOADER_NFS_SUPPORT .endif