diff --git a/sys/boot/i386/libi386/bootinfo.c b/sys/boot/i386/libi386/bootinfo.c index a8738e12dcc3..33e2b63b195e 100644 --- a/sys/boot/i386/libi386/bootinfo.c +++ b/sys/boot/i386/libi386/bootinfo.c @@ -271,7 +271,10 @@ bi_load(char *args, int *howtop, int *bootdevp, vm_offset_t *bip) break; printf("root device %s invalid\n", i386_fmtdev(rootdev)); return(EINVAL); - + + case DEVT_NET: + break; + default: printf("WARNING - don't know how to boot from device type %d\n", rootdev->d_type); } diff --git a/sys/boot/i386/libi386/bootinfo32.c b/sys/boot/i386/libi386/bootinfo32.c index a8738e12dcc3..33e2b63b195e 100644 --- a/sys/boot/i386/libi386/bootinfo32.c +++ b/sys/boot/i386/libi386/bootinfo32.c @@ -271,7 +271,10 @@ bi_load(char *args, int *howtop, int *bootdevp, vm_offset_t *bip) break; printf("root device %s invalid\n", i386_fmtdev(rootdev)); return(EINVAL); - + + case DEVT_NET: + break; + default: printf("WARNING - don't know how to boot from device type %d\n", rootdev->d_type); } diff --git a/sys/boot/i386/libi386/bootinfo64.c b/sys/boot/i386/libi386/bootinfo64.c index a8738e12dcc3..33e2b63b195e 100644 --- a/sys/boot/i386/libi386/bootinfo64.c +++ b/sys/boot/i386/libi386/bootinfo64.c @@ -271,7 +271,10 @@ bi_load(char *args, int *howtop, int *bootdevp, vm_offset_t *bip) break; printf("root device %s invalid\n", i386_fmtdev(rootdev)); return(EINVAL); - + + case DEVT_NET: + break; + default: printf("WARNING - don't know how to boot from device type %d\n", rootdev->d_type); } diff --git a/sys/boot/i386/libi386/pxe.c b/sys/boot/i386/libi386/pxe.c index f84fd812ddff..88a530ded569 100644 --- a/sys/boot/i386/libi386/pxe.c +++ b/sys/boot/i386/libi386/pxe.c @@ -32,11 +32,19 @@ #include +#include +#include +#include +#include + #include #include #include #include +#include +#include + #include #include @@ -53,34 +61,59 @@ static char scratch_buffer[PXE_BUFFER_SIZE]; static char data_buffer[PXE_BUFFER_SIZE]; -static uint32_t myip; /* my IP address */ -static uint32_t serverip; /* where I got my initial bootstrap from */ -static uint32_t secondip; /* where I should go to get the rest of my boot files */ -static char *servername = NULL; /* name of server I DHCP'd from */ -static char *bootfile = NULL; /* name of file that I booted with */ -static uint16_t pxe_return_status; -static uint16_t pxe_open_status; static pxenv_t *pxenv_p = NULL; /* PXENV+ */ -static pxe_t *pxe_p = NULL; /* !PXE */ +static BOOTPLAYER bootplayer; /* PXE Cached information. */ + +static int debug = 0; +static int pxe_sock = -1; +static int pxe_opens = 0; void pxe_enable(void *pxeinfo); +void pxe_call(int func); + static int pxe_init(void); -static int pxe_strategy(void *devdata, int flag, daddr_t dblk, size_t size, - void *buf, size_t *rsize); +static int pxe_strategy(void *devdata, int flag, daddr_t dblk, + size_t size, void *buf, size_t *rsize); static int pxe_open(struct open_file *f, ...); static int pxe_close(struct open_file *f); static void pxe_print(int verbose); static void pxe_perror(int error); -void pxe_call(int func); +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); -static int pxe_fs_open(const char *path, struct open_file *f); -static int pxe_fs_close(struct open_file *f); -static int pxe_fs_read(struct open_file *f, void *buf, size_t size, size_t *resid); -static int pxe_fs_write(struct open_file *f, void *buf, size_t size, size_t *resid); -static off_t pxe_fs_seek(struct open_file *f, off_t offset, int where); -static int pxe_fs_stat(struct open_file *f, struct stat *sb); +extern struct netif_stats pxe_st[]; +extern struct in_addr rootip; +extern char rootpath[FNAME_SIZE]; +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 +}; struct devsw pxedisk = { "pxe", @@ -93,16 +126,6 @@ struct devsw pxedisk = { pxe_print }; -struct fs_ops pxe_fsops = { - "pxe", - pxe_fs_open, - pxe_fs_close, - pxe_fs_read, - pxe_fs_write, - pxe_fs_seek, - pxe_fs_stat -}; - /* * 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 @@ -122,7 +145,6 @@ static int pxe_init(void) { t_PXENV_GET_CACHED_INFO *gci_p; - BOOTPLAYER *bootplayer; int counter; uint8_t checksum; uint8_t *checkptr; @@ -168,81 +190,113 @@ pxe_init(void) pxenv_p = NULL; return (0); } - bootplayer = (BOOTPLAYER *) - PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset); - serverip = bootplayer->sip; - servername = strdup(bootplayer->Sname); - bootfile = strdup(bootplayer->bootfile); - myip = bootplayer->yip; - secondip = bootplayer->sip; + bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset), + &bootplayer, gci_p->BufferSize); + + /* + * XXX - This is a major cop out. We should request this + * from DHCP, but we can't do that until we have full UNDI + * support. + * + * Also set the nfs server's IP. + */ + strcpy(rootpath, PXENFSROOTPATH); + rootip.s_addr = bootplayer.sip; return (1); } -int -pxe_tftpopen(uint32_t srcip, uint32_t gateip, char *filename, uint16_t port, - uint16_t pktsize) -{ - t_PXENV_TFTP_OPEN *tftpo_p; - tftpo_p = (t_PXENV_TFTP_OPEN *)scratch_buffer; - bzero(tftpo_p, sizeof(*tftpo_p)); - tftpo_p->ServerIPAddress = srcip; - tftpo_p->GatewayIPAddress = gateip; - tftpo_p->TFTPPort = port; - tftpo_p->PacketSize = pktsize; - bcopy(filename, tftpo_p->FileName, strlen(filename)); - pxe_call(PXENV_TFTP_OPEN); - pxe_return_status = tftpo_p->Status; - if (tftpo_p->Status != 0) - return (-1); - return (tftpo_p->PacketSize); +static int +pxe_strategy(void *devdata, int flag, daddr_t dblk, size_t size, + void *buf, size_t *rsize) +{ + return (EIO); } -int -pxe_tftpclose(void) +static int +pxe_open(struct open_file *f, ...) { - t_PXENV_TFTP_CLOSE *tftpc_p; + va_list args; + char *devname; /* Device part of file name (or NULL). */ + int error = 0; - tftpc_p = (t_PXENV_TFTP_CLOSE *)scratch_buffer; - bzero(tftpc_p, sizeof(*tftpc_p)); - pxe_call(PXENV_TFTP_CLOSE); - pxe_return_status = tftpc_p->Status; - if (tftpc_p->Status != 0) - return (-1); - return (1); + va_start(args, f); + devname = va_arg(args, char*); + va_end(args); + + /* On first open, do netif open, mount, etc. */ + 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 (debug) + printf("pxe_open: netif_open() succeeded\n"); + } + } + pxe_opens++; + f->f_devdata = &pxe_sock; + return (error); } -int -pxe_tftpread(void *buf) +static int +pxe_close(struct open_file *f) { - t_PXENV_TFTP_READ *tftpr_p; - tftpr_p = (t_PXENV_TFTP_READ *)scratch_buffer; - bzero(tftpr_p, sizeof(*tftpr_p)); - - tftpr_p->Buffer.segment = VTOPSEG(data_buffer); - tftpr_p->Buffer.offset = VTOPOFF(data_buffer); +#ifdef PXE_DEBUG + if (debug) + printf("pxe_close: opens=%d\n", pxe_opens); +#endif - pxe_call(PXENV_TFTP_READ); - - /* XXX - I don't know why we need this. */ - delay(1000); - - pxe_return_status = tftpr_p->Status; - if (tftpr_p->Status != 0) - return (-1); - bcopy(data_buffer, buf, tftpr_p->BufferSize); - return (tftpr_p->BufferSize); + /* On last close, do netif close, etc. */ + f->f_devdata = NULL; + /* Extra close call? */ + if (pxe_opens <= 0) + return (0); + pxe_opens--; + /* Not last close? */ + if (pxe_opens > 0) + return(0); + rootip.s_addr = 0; + if (pxe_sock >= 0) { +#ifdef PXE_DEBUG + if (debug) + printf("pxe_close: calling netif_close()\n"); +#endif + netif_close(pxe_sock); + pxe_sock = -1; + } + return (0); } +static void +pxe_print(int verbose) +{ + if (pxenv_p != NULL) { + if (*bootplayer.Sname == '\0') { + printf(" "IP_STR":%s\n", + IP_ARGS(htonl(bootplayer.sip)), + bootplayer.bootfile); + } else { + printf(" %s:%s\n", bootplayer.Sname, + bootplayer.bootfile); + } + } + + return; +} + + void pxe_perror(int err) { return; } - void pxe_call(int func) { @@ -259,251 +313,109 @@ pxe_call(int func) v86.ctl = V86_FLAGS; } -static int -pxe_strategy(void *devdata, int flag, daddr_t dblk, size_t size, - void *buf, size_t *rsize) + +time_t +getsecs() { - return (EIO); + time_t n = 0; + time(&n); + return n; } static int -pxe_open(struct open_file *f, ...) +pxe_netif_match(struct netif *nif, void *machdep_hint) { - return (0); + return 1; } + static int -pxe_close(struct open_file *f) +pxe_netif_probe(struct netif *nif, void *machdep_hint) { - return (0); + t_PXENV_UDP_OPEN *udpopen_p = (t_PXENV_UDP_OPEN *)scratch_buffer; + bzero(udpopen_p, sizeof(*udpopen_p)); + + udpopen_p->src_ip = bootplayer.yip; + pxe_call(PXENV_UDP_OPEN); + + if (udpopen_p->status != 0) + printf("pxe_netif_probe: failed %x\n", udpopen_p->status); + return 0; } static void -pxe_print(int verbose) +pxe_netif_end(struct netif *nif) { - if (pxenv_p != NULL) { - if (*servername == '\0') { - printf(" "IP_STR":/%s\n", IP_ARGS(htonl(serverip)), - bootfile); - } else { - printf(" %s:/%s\n", servername, bootfile); - } - } + t_PXENV_UDP_CLOSE *udpclose_p = (t_PXENV_UDP_CLOSE *)scratch_buffer; + bzero(udpclose_p, sizeof(*udpclose_p)); - return; + pxe_call(PXENV_UDP_CLOSE); + if (udpclose_p->status != 0) + printf("pxe_end failed %x\n", udpclose_p->status); } - -/* - * Most of this code was ripped from libstand/tftp.c and - * modified to work with pxe. :) - */ -#define RSPACE 520 /* max data packet, rounded up */ - -struct tftp_handle { - int currblock; /* contents of lastdata */ - int islastblock; /* flag */ - int validsize; - int off; - int opened; - char *path; /* saved for re-requests */ - u_char space[RSPACE]; -}; - -static int -tftp_makereq(h) - struct tftp_handle *h; +static void +pxe_netif_init(struct iodesc *desc, void *machdep_hint) { - ssize_t res; - char *p; +} + +static int +pxe_netif_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout) +{ + return len; +} + +static int +pxe_netif_put(struct iodesc *desc, void *pkt, size_t len) +{ + return len; +} + +ssize_t +sendudp(struct iodesc *h, void *pkt, size_t len) +{ + t_PXENV_UDP_WRITE *udpwrite_p = (t_PXENV_UDP_WRITE *)scratch_buffer; + bzero(udpwrite_p, sizeof(*udpwrite_p)); - p = h->path; + udpwrite_p->ip = bootplayer.sip; + 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); - if (*p == '/') - ++p; - if (h->opened) - pxe_tftpclose(); + pxe_call(PXENV_UDP_WRITE); + + /* XXX - I dont know why we need this. */ + delay(1000); + if (udpwrite_p->status != 0) + printf("sendudp failed %x\n", udpwrite_p->status); + + return len; +} + +ssize_t +readudp(struct iodesc *h, void *pkt, size_t len, time_t timeout) +{ + t_PXENV_UDP_READ *udpread_p = (t_PXENV_UDP_READ *)scratch_buffer; + struct udphdr *uh = NULL; - if (pxe_tftpopen(serverip, 0, p, htons(69), PXE_TFTP_BUFFER_SIZE) < 0) - return(ENOENT); - pxe_open_status = pxe_return_status; - res = pxe_tftpread(h->space); + uh = (struct udphdr *) pkt - 1; + bzero(udpread_p, sizeof(*udpread_p)); - if (res == -1) - return (errno); - h->currblock = 1; - h->validsize = res; - h->islastblock = 0; - if (res < SEGSIZE) - h->islastblock = 1; /* very short file */ - return (0); -} - -/* ack block, expect next */ -static int -tftp_getnextblock(h) - struct tftp_handle *h; -{ - int res; - - res = pxe_tftpread(h->space); - - if (res == -1) /* 0 is OK! */ - return (errno); - - h->currblock++; - h->validsize = res; - if (res < SEGSIZE) - h->islastblock = 1; /* EOF */ - return (0); -} - -static int -pxe_fs_open(const char *path, struct open_file *f) -{ - struct tftp_handle *tftpfile; - int res; - - /* make sure the device is a PXE device */ - 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); - } - - res = tftp_makereq(tftpfile); - - if (res) { - free(tftpfile->path); - free(tftpfile); - return (res); - } - tftpfile->opened = 1; - f->f_fsdata = (void *) tftpfile; - return(0); -} - -static int -pxe_fs_close(struct open_file *f) -{ - struct tftp_handle *tftpfile; - tftpfile = (struct tftp_handle *) f->f_fsdata; - - if (tftpfile) { - if (tftpfile->opened) - pxe_tftpclose(); - free(tftpfile->path); - free(tftpfile); - } - return (0); -} - -static int -pxe_fs_read(struct open_file *f, void *addr, size_t size, size_t *resid) -{ - struct tftp_handle *tftpfile; - static int tc = 0; - char *dest = (char *)addr; - tftpfile = (struct tftp_handle *) f->f_fsdata; - - while (size > 0) { - int needblock, count; - - if (!(tc++ % 16)) - twiddle(); - - needblock = tftpfile->off / SEGSIZE + 1; - - if (tftpfile->currblock > needblock) /* seek backwards */ - tftp_makereq(tftpfile); /* no error check, it worked - * for open */ - - while (tftpfile->currblock < needblock) { - int res; - - res = tftp_getnextblock(tftpfile); - if (res) { /* no answer */ - return (res); - } - if (tftpfile->islastblock) - break; - } - - if (tftpfile->currblock == needblock) { - int offinblock, inbuffer; - offinblock = tftpfile->off % SEGSIZE; - - inbuffer = tftpfile->validsize - offinblock; - if (inbuffer < 0) { - return (EINVAL); - } - count = (size < inbuffer ? size : inbuffer); - bcopy(tftpfile->space + offinblock, - dest, count); - - dest += count; - tftpfile->off += count; - size -= count; - - if ((tftpfile->islastblock) && (count == inbuffer)) - break; /* EOF */ - } else { - printf("tftp: block %d not found\n", needblock); - return (EINVAL); - } - - } - - if (resid) - *resid = size; - return(0); -} - -static int -pxe_fs_write(struct open_file *f, void *buf, size_t size, size_t *resid) -{ - return 0; -} - -static off_t -pxe_fs_seek(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); - } - return (tftpfile->off); -} - -static int -pxe_fs_stat(struct open_file *f, struct stat *sb) -{ - if (pxe_open_status != 0) - 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; + udpread_p->dest_ip = bootplayer.yip; + 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); + + pxe_call(PXENV_UDP_READ); + + /* XXX - I dont know why we need this. */ + delay(1000); + if (udpread_p->status > 1) + printf("readudp failed %x\n", udpread_p->status); + bcopy(data_buffer, pkt, udpread_p->buffer_size); + uh->uh_sport = udpread_p->s_port; + return udpread_p->buffer_size; } diff --git a/sys/boot/i386/libi386/pxe.h b/sys/boot/i386/libi386/pxe.h index 164760e2258c..f49546a62792 100644 --- a/sys/boot/i386/libi386/pxe.h +++ b/sys/boot/i386/libi386/pxe.h @@ -61,6 +61,8 @@ #define MAC_ARGS(mac) \ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5] +#define PXENFSROOTPATH "/pxeroot" + typedef struct { uint16_t offset; uint16_t segment; @@ -445,6 +447,17 @@ typedef struct { SEGOFF16_t buffer; /* SEG:OFF to the packet buffer */ } PACKED t_PXENV_UDP_WRITE; +#define PXENV_UDP_WRITE 0x0033 +typedef struct { + PXENV_STATUS_t status; + IP4_t ip; /* dest ip addr */ + IP4_t gw; /* ip gateway */ + UDP_PORT_t src_port; /* source udp port */ + UDP_PORT_t dst_port; /* destination udp port */ + uint16_t buffer_size; /* Size of the packet buffer */ + SEGOFF16_t buffer; /* SEG:OFF to the packet buffer */ +} PACKED t_PXENV_UDP_WRITE; + #define PXENV_UNLOAD_STACK 0x0070 typedef struct { PXENV_STATUS_t Status; diff --git a/sys/boot/i386/loader/Makefile b/sys/boot/i386/loader/Makefile index 7e2126a46683..a6eea869d14f 100644 --- a/sys/boot/i386/loader/Makefile +++ b/sys/boot/i386/loader/Makefile @@ -13,6 +13,10 @@ BINDIR?= /boot SRCS= main.c conf.c pxe.c .PATH: ${.CURDIR}/../libi386 +# Enable PXE TFTP or NFS support, not both. +CFLAGS+= -DLOADER_NFS_SUPPORT +#CFLAGS+= -DLOADER_TFTP_SUPPORT + # Enable PnP and ISA-PnP code. HAVE_PNP= yes HAVE_ISABUS= yes @@ -45,8 +49,8 @@ CFLAGS+= -I${.CURDIR}/.. # where to get libstand from LIBSTAND= -lstand -#LIBSTAND= ${.CURDIR}/../../../lib/libstand/libstand.a -#CFLAGS+= -I${.CURDIR}/../../../lib/libstand/ +#LIBSTAND= ${.CURDIR}/../../../../lib/libstand/libstand.a +CFLAGS+= -I${.CURDIR}/../../../../lib/libstand/ # BTX components .if exists(${.OBJDIR}/../btx) diff --git a/sys/boot/i386/loader/conf.c b/sys/boot/i386/loader/conf.c index e7acadeacfb8..ab1f122aaba4 100644 --- a/sys/boot/i386/loader/conf.c +++ b/sys/boot/i386/loader/conf.c @@ -41,11 +41,16 @@ * XXX as libi386 and biosboot merge, some of these can become linker sets. */ +#if defined(LOADER_NFS_SUPPORT) && defined(LOADER_TFTP_SUPPORT) +#error "Cannot have both tftp and nfs support yet." +#endif + /* Exported for libstand */ struct devsw *devsw[] = { &biosdisk, +#if defined(LOADER_NFS_SUPPORT) || defined(LOADER_TFTP_SUPPORT) &pxedisk, - /* XXX network devices? */ +#endif NULL }; @@ -53,7 +58,12 @@ struct fs_ops *file_system[] = { &ufs_fsops, &dosfs_fsops, &zipfs_fsops, - &pxe_fsops, +#ifdef LOADER_NFS_SUPPORT + &nfs_fsops, +#endif +#ifdef LOADER_TFTP_SUPPORT + &tftp_fsops, +#endif NULL }; diff --git a/sys/boot/i386/loader/main.c b/sys/boot/i386/loader/main.c index 97449ae0e3ed..db35e597d08e 100644 --- a/sys/boot/i386/loader/main.c +++ b/sys/boot/i386/loader/main.c @@ -70,6 +70,11 @@ extern char bootprog_name[], bootprog_rev[], bootprog_date[], bootprog_maker[]; /* XXX debugging */ extern char end[]; +/* XXX - I dont know why we have to do this, but it helps. */ +#if defined(LOADER_NFS_SUPPORT) || defined(LOADER_TFTP_SUPPORT) +char Heap[200*1024]; +#endif + void main(void) { @@ -85,7 +90,13 @@ main(void) * Initialise the heap as early as possible. Once this is done, malloc() is usable. */ bios_getmem(); + + /* XXX - I dont know why we have to do this, but it helps PXE. */ +#if defined(LOADER_NFS_SUPPORT) || defined(LOADER_TFTP_SUPPORT) + setheap(Heap, Heap+sizeof(Heap)); +#else setheap((void *)end, (void *)bios_basemem); +#endif /* * XXX Chicken-and-egg problem; we want to have console output early, but some