| /*- |
| * Copyright (c) 2000 Alfred Perlstein <alfred@freebsd.org> |
| * Copyright (c) 2000 Paul Saab <ps@freebsd.org> |
| * Copyright (c) 2000 John Baldwin <jhb@freebsd.org> |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| */ |
| |
| #include <sys/cdefs.h> |
| |
| #include <stand.h> |
| #include <string.h> |
| #include <stdarg.h> |
| |
| #include <netinet/in_systm.h> |
| #include <netinet/in.h> |
| #include <netinet/udp.h> |
| |
| #include <net.h> |
| #include <netif.h> |
| #include <nfsv2.h> |
| #include <iodesc.h> |
| |
| #include <bootp.h> |
| #include <bootstrap.h> |
| #include "btxv86.h" |
| #include "pxe.h" |
| |
| /* |
| * 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]; |
| |
| static pxenv_t *pxenv_p = NULL; /* PXENV+ */ |
| static pxe_t *pxe_p = NULL; /* !PXE */ |
| BOOTPLAYER bootplayer = {0}; /* PXE Cached information. */ |
| |
| static int pxe_debug = 0; |
| static int pxe_sock = -1; |
| static int pxe_opens = 0; |
| |
| void pxe_enable(void *pxeinfo); |
| static void (*pxe_call)(int func); |
| static void pxenv_call(int func); |
| static void bangpxe_call(int func); |
| |
| static int pxe_init(void); |
| static int pxe_strategy(void *devdata, int flag, daddr_t dblk, |
| size_t offset, size_t size, char *buf, size_t *rsize); |
| static int pxe_open(struct open_file *f, ...); |
| static int pxe_close(struct open_file *f); |
| static int pxe_print(int verbose); |
| static void pxe_cleanup(void); |
| 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); |
| |
| #ifdef OLD_NFSV2 |
| int nfs_getrootfh(struct iodesc*, char*, u_char*); |
| #else |
| int nfs_getrootfh(struct iodesc*, char*, uint32_t*, u_char*); |
| #endif |
| |
| extern struct netif_stats pxe_st[]; |
| extern u_int16_t __bangpxeseg; |
| extern u_int16_t __bangpxeoff; |
| extern void __bangpxeentry(void); |
| extern u_int16_t __pxenvseg; |
| 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 |
| }; |
| |
| struct devsw pxedisk = { |
| "pxe", |
| DEVT_NET, |
| pxe_init, |
| pxe_strategy, |
| pxe_open, |
| pxe_close, |
| noioctl, |
| pxe_print, |
| pxe_cleanup |
| }; |
| |
| /* |
| * 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 |
| * PXENV+ structure. |
| */ |
| void |
| pxe_enable(void *pxeinfo) |
| { |
| pxenv_p = (pxenv_t *)pxeinfo; |
| pxe_p = (pxe_t *)PTOV(pxenv_p->PXEPtr.segment * 16 + |
| pxenv_p->PXEPtr.offset); |
| pxe_call = NULL; |
| } |
| |
| /* |
| * return true if pxe structures are found/initialized, |
| * also figures out our IP information via the pxe cached info struct |
| */ |
| static int |
| pxe_init(void) |
| { |
| t_PXENV_GET_CACHED_INFO *gci_p; |
| int counter; |
| uint8_t checksum; |
| uint8_t *checkptr; |
| |
| if(pxenv_p == NULL) |
| return (0); |
| |
| /* look for "PXENV+" */ |
| if (bcmp((void *)pxenv_p->Signature, S_SIZE("PXENV+"))) { |
| pxenv_p = NULL; |
| return (0); |
| } |
| |
| /* make sure the size is something we can handle */ |
| if (pxenv_p->Length > sizeof(*pxenv_p)) { |
| printf("PXENV+ structure too large, ignoring\n"); |
| pxenv_p = NULL; |
| return (0); |
| } |
| |
| /* |
| * do byte checksum: |
| * add up each byte in the structure, the total should be 0 |
| */ |
| checksum = 0; |
| checkptr = (uint8_t *) pxenv_p; |
| for (counter = 0; counter < pxenv_p->Length; counter++) |
| checksum += *checkptr++; |
| if (checksum != 0) { |
| printf("PXENV+ structure failed checksum, ignoring\n"); |
| pxenv_p = NULL; |
| return (0); |
| } |
| |
| |
| /* |
| * PXENV+ passed, so use that if !PXE is not available or |
| * the checksum fails. |
| */ |
| pxe_call = pxenv_call; |
| if (pxenv_p->Version >= 0x0200) { |
| for (;;) { |
| if (bcmp((void *)pxe_p->Signature, S_SIZE("!PXE"))) { |
| pxe_p = NULL; |
| break; |
| } |
| checksum = 0; |
| checkptr = (uint8_t *)pxe_p; |
| for (counter = 0; counter < pxe_p->StructLength; |
| counter++) |
| checksum += *checkptr++; |
| if (checksum != 0) { |
| pxe_p = NULL; |
| break; |
| } |
| pxe_call = bangpxe_call; |
| break; |
| } |
| } |
| |
| printf("\nPXE version %d.%d, real mode entry point ", |
| (uint8_t) (pxenv_p->Version >> 8), |
| (uint8_t) (pxenv_p->Version & 0xFF)); |
| if (pxe_call == bangpxe_call) |
| printf("@%04x:%04x\n", |
| pxe_p->EntryPointSP.segment, |
| pxe_p->EntryPointSP.offset); |
| else |
| printf("@%04x:%04x\n", |
| pxenv_p->RMEntry.segment, pxenv_p->RMEntry.offset); |
| |
| gci_p = (t_PXENV_GET_CACHED_INFO *) scratch_buffer; |
| bzero(gci_p, sizeof(*gci_p)); |
| gci_p->PacketType = PXENV_PACKET_TYPE_BINL_REPLY; |
| pxe_call(PXENV_GET_CACHED_INFO); |
| if (gci_p->Status != 0) { |
| pxe_perror(gci_p->Status); |
| pxe_p = NULL; |
| return (0); |
| } |
| bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset), |
| &bootplayer, gci_p->BufferSize); |
| return (1); |
| } |
| |
| |
| static int |
| pxe_strategy(void *devdata, int flag, daddr_t dblk, size_t offset, size_t size, |
| char *buf, size_t *rsize) |
| { |
| return (EIO); |
| } |
| |
| static int |
| 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. */ |
| 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 |
| * NFS/TFTP server is. Even if we dont get back |
| * the proper information, fall back to the server |
| * which brought us to life and a default rootpath. |
| */ |
| bootp(pxe_sock, BOOTP_PXE); |
| if (rootip.s_addr == 0) |
| rootip.s_addr = bootplayer.sip; |
| #ifdef LOADER_NFS_SUPPORT |
| if (!rootpath[0]) |
| strcpy(rootpath, PXENFSROOTPATH); |
| #endif |
| |
| for (i = 0; rootpath[i] != '\0' && i < FNAME_SIZE; i++) |
| if (rootpath[i] == ':') |
| break; |
| if (i && i != FNAME_SIZE && rootpath[i] == ':') { |
| rootpath[i++] = '\0'; |
| if (inet_addr(&rootpath[0]) != INADDR_NONE) |
| rootip.s_addr = inet_addr(&rootpath[0]); |
| bcopy(&rootpath[i], &temp[0], strlen(&rootpath[i])+1); |
| bcopy(&temp[0], &rootpath[0], strlen(&rootpath[i])+1); |
| } |
| setenv("boot.netif.ip", inet_ntoa(myip), 1); |
| setenv("boot.netif.netmask", intoa(netmask), 1); |
| setenv("boot.netif.gateway", inet_ntoa(gateip), 1); |
| if (bootplayer.Hardware == ETHER_TYPE) { |
| sprintf(temp, "%6D", bootplayer.CAddr, ":"); |
| setenv("boot.netif.hwaddr", temp, 1); |
| } |
| #ifdef LOADER_NFS_SUPPORT |
| 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)); |
| |
| setenv("boot.nfsroot.server", inet_ntoa(rootip), 1); |
| setenv("boot.nfsroot.path", rootpath, 1); |
| #else |
| setenv("boot.netif.server", inet_ntoa(rootip), 1); |
| setenv("boot.tftproot.path", rootpath, 1); |
| #endif |
| setenv("dhcp.host-name", hostname, 1); |
| |
| setenv("pxeboot.ip", inet_ntoa(myip), 1); |
| if (bootplayer.Hardware == ETHER_TYPE) { |
| sprintf(temp, "%6D", bootplayer.CAddr, ":"); |
| setenv("pxeboot.hwaddr", temp, 1); |
| } |
| } |
| } |
| pxe_opens++; |
| f->f_devdata = &pxe_sock; |
| return (error); |
| } |
| |
| static int |
| pxe_close(struct open_file *f) |
| { |
| |
| #ifdef PXE_DEBUG |
| if (pxe_debug) |
| 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) |
| return (0); |
| pxe_opens--; |
| /* Not last close? */ |
| 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); |
| } |
| |
| static int |
| pxe_print(int verbose) |
| { |
| char line[255]; |
| if (pxe_call == NULL) |
| return (0); |
| |
| snprintf(line, sizeof (line), " pxe0: %s:%s\n", inet_ntoa(rootip), |
| rootpath); |
| return (pager_output(line)); |
| } |
| |
| static void |
| pxe_cleanup(void) |
| { |
| #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 |
| if (pxe_debug && unload_stack_p->Status != 0) |
| printf("pxe_cleanup: UNLOAD_STACK failed %x\n", |
| unload_stack_p->Status); |
| #endif |
| } |
| |
| void |
| pxe_perror(int err) |
| { |
| return; |
| } |
| |
| #ifdef LOADER_NFS_SUPPORT |
| /* |
| * Reach inside the libstand NFS code and dig out an NFS handle |
| * for the root filesystem. |
| */ |
| #ifdef OLD_NFSV2 |
| 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); |
| } |
| #else /* !OLD_NFSV2 */ |
| |
| #define NFS_V3MAXFHSIZE 64 |
| |
| struct nfs_iodesc { |
| struct iodesc *iodesc; |
| off_t off; |
| uint32_t fhsize; |
| u_char fh[NFS_V3MAXFHSIZE]; |
| /* structure truncated */ |
| }; |
| 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.fhsize, |
| nfs_root_node.fh)) != 0) { |
| printf("NFS MOUNT RPC error: %d\n", error); |
| nfs_root_node.fhsize = 0; |
| } |
| nfs_root_node.iodesc = d; |
| } |
| |
| static void |
| pxe_setnfshandle(char *rootpath) |
| { |
| int i; |
| u_char *fh; |
| char buf[2 * NFS_V3MAXFHSIZE + 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_root_node.fhsize; i++, cp += 2) |
| sprintf(cp, "%02x", fh[i]); |
| sprintf(cp, "X"); |
| setenv("boot.nfsroot.nfshandle", buf, 1); |
| sprintf(buf, "%d", nfs_root_node.fhsize); |
| setenv("boot.nfsroot.nfshandlelen", buf, 1); |
| } |
| #endif /* OLD_NFSV2 */ |
| #endif /* LOADER_NFS_SUPPORT */ |
| |
| void |
| pxenv_call(int func) |
| { |
| #ifdef PXE_DEBUG |
| if (pxe_debug) |
| printf("pxenv_call %x\n", func); |
| #endif |
| |
| bzero(&v86, sizeof(v86)); |
| bzero(data_buffer, sizeof(data_buffer)); |
| |
| __pxenvseg = pxenv_p->RMEntry.segment; |
| __pxenvoff = pxenv_p->RMEntry.offset; |
| |
| v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; |
| v86.es = VTOPSEG(scratch_buffer); |
| v86.edi = VTOPOFF(scratch_buffer); |
| v86.addr = (VTOPSEG(__pxenventry) << 16) | VTOPOFF(__pxenventry); |
| v86.ebx = func; |
| v86int(); |
| v86.ctl = V86_FLAGS; |
| } |
| |
| void |
| bangpxe_call(int func) |
| { |
| #ifdef PXE_DEBUG |
| if (pxe_debug) |
| printf("bangpxe_call %x\n", func); |
| #endif |
| |
| bzero(&v86, sizeof(v86)); |
| bzero(data_buffer, sizeof(data_buffer)); |
| |
| __bangpxeseg = pxe_p->EntryPointSP.segment; |
| __bangpxeoff = pxe_p->EntryPointSP.offset; |
| |
| v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; |
| v86.edx = VTOPSEG(scratch_buffer); |
| v86.eax = VTOPOFF(scratch_buffer); |
| v86.addr = (VTOPSEG(__bangpxeentry) << 16) | VTOPOFF(__bangpxeentry); |
| v86.ebx = func; |
| v86int(); |
| v86.ctl = V86_FLAGS; |
| } |
| |
| |
| time_t |
| getsecs() |
| { |
| time_t n = 0; |
| time(&n); |
| return n; |
| } |
| |
| static int |
| pxe_netif_match(struct netif *nif, void *machdep_hint) |
| { |
| return 1; |
| } |
| |
| |
| static int |
| pxe_netif_probe(struct netif *nif, void *machdep_hint) |
| { |
| t_PXENV_UDP_OPEN *udpopen_p = (t_PXENV_UDP_OPEN *)scratch_buffer; |
| |
| if (pxe_call == NULL) |
| return -1; |
| |
| 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 -1; |
| } |
| return 0; |
| } |
| |
| static void |
| pxe_netif_end(struct netif *nif) |
| { |
| t_PXENV_UDP_CLOSE *udpclose_p = (t_PXENV_UDP_CLOSE *)scratch_buffer; |
| bzero(udpclose_p, sizeof(*udpclose_p)); |
| |
| pxe_call(PXENV_UDP_CLOSE); |
| if (udpclose_p->status != 0) |
| printf("pxe_end failed %x\n", udpclose_p->status); |
| } |
| |
| static void |
| pxe_netif_init(struct iodesc *desc, void *machdep_hint) |
| { |
| int i; |
| for (i = 0; i < 6; ++i) |
| desc->myea[i] = bootplayer.CAddr[i]; |
| desc->xid = bootplayer.ident; |
| } |
| |
| 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)); |
| |
| 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); |
| |
| if (netmask == 0 || SAMENET(myip, h->destip, netmask)) |
| udpwrite_p->gw = 0; |
| else |
| udpwrite_p->gw = gateip.s_addr; |
| |
| pxe_call(PXENV_UDP_WRITE); |
| |
| #if 0 |
| /* XXX - I dont know why we need this. */ |
| delay(1000); |
| #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 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; |
| |
| 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); |
| |
| pxe_call(PXENV_UDP_READ); |
| |
| #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; |
| } |
| bcopy(data_buffer, pkt, udpread_p->buffer_size); |
| uh->uh_sport = udpread_p->s_port; |
| return udpread_p->buffer_size; |
| } |