pxe.c revision 59390
158713Sjhb/* 258713Sjhb * Copyright (c) 2000 Alfred Perlstein <alfred@freebsd.org> 358713Sjhb * All rights reserved. 458713Sjhb * Copyright (c) 2000 Paul Saab <ps@freebsd.org> 558713Sjhb * All rights reserved. 658713Sjhb * Copyright (c) 2000 John Baldwin <jhb@freebsd.org> 758713Sjhb * All rights reserved. 858713Sjhb * 958713Sjhb * Redistribution and use in source and binary forms, with or without 1058713Sjhb * modification, are permitted provided that the following conditions 1158713Sjhb * are met: 1258713Sjhb * 1. Redistributions of source code must retain the above copyright 1358713Sjhb * notice, this list of conditions and the following disclaimer. 1458713Sjhb * 2. Redistributions in binary form must reproduce the above copyright 1558713Sjhb * notice, this list of conditions and the following disclaimer in the 1658713Sjhb * documentation and/or other materials provided with the distribution. 1758713Sjhb * 1858713Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1958713Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2058713Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2158713Sjhb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2258713Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2358713Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2458713Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2558713Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2658713Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2758713Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2858713Sjhb * SUCH DAMAGE. 2958713Sjhb * 3058713Sjhb * $FreeBSD: head/sys/boot/i386/libi386/pxe.c 59390 2000-04-19 11:22:38Z ps $ 3158713Sjhb */ 3258713Sjhb 3358713Sjhb#include <stand.h> 3458713Sjhb 3559087Sps#include <netinet/in_systm.h> 3659087Sps#include <netinet/in.h> 3759087Sps#include <netinet/udp.h> 3859087Sps#include <netinet/ip.h> 3959087Sps 4058713Sjhb#include <sys/reboot.h> 4158713Sjhb#include <string.h> 4258713Sjhb#include <sys/reboot.h> 4358713Sjhb#include <arpa/tftp.h> 4458713Sjhb 4559087Sps#include <net.h> 4659087Sps#include <netif.h> 4759087Sps 4858713Sjhb#include <stdarg.h> 4958713Sjhb 5058713Sjhb#include <bootstrap.h> 5158713Sjhb#include "btxv86.h" 5258993Sps#include "pxe.h" 5358713Sjhb 5458713Sjhb/* 5558713Sjhb * Allocate the PXE buffers statically instead of sticking grimy fingers into 5658713Sjhb * BTX's private data area. The scratch buffer is used to send information to 5758713Sjhb * the PXE BIOS, and the data buffer is used to receive data from the PXE BIOS. 5858713Sjhb */ 5958993Sps#define PXE_BUFFER_SIZE 0x2000 6058993Sps#define PXE_TFTP_BUFFER_SIZE 512 6158993Spsstatic char scratch_buffer[PXE_BUFFER_SIZE]; 6258993Spsstatic char data_buffer[PXE_BUFFER_SIZE]; 6358713Sjhb 6459390Spsstatic pxenv_t *pxenv_p = NULL; /* PXENV+ */ 6559390Spsstatic pxe_t *pxe_p = NULL; /* !PXE */ 6659087Spsstatic BOOTPLAYER bootplayer; /* PXE Cached information. */ 6758713Sjhb 6859087Spsstatic int debug = 0; 6959087Spsstatic int pxe_sock = -1; 7059087Spsstatic int pxe_opens = 0; 7159087Sps 7258713Sjhbvoid pxe_enable(void *pxeinfo); 7359087Spsvoid pxe_call(int func); 7459087Sps 7558713Sjhbstatic int pxe_init(void); 7659087Spsstatic int pxe_strategy(void *devdata, int flag, daddr_t dblk, 7759087Sps size_t size, void *buf, size_t *rsize); 7858713Sjhbstatic int pxe_open(struct open_file *f, ...); 7958713Sjhbstatic int pxe_close(struct open_file *f); 8058713Sjhbstatic void pxe_print(int verbose); 8159390Spsstatic void pxe_cleanup(void); 8258713Sjhb 8358713Sjhbstatic void pxe_perror(int error); 8459087Spsstatic int pxe_netif_match(struct netif *nif, void *machdep_hint); 8559087Spsstatic int pxe_netif_probe(struct netif *nif, void *machdep_hint); 8659087Spsstatic void pxe_netif_init(struct iodesc *desc, void *machdep_hint); 8759087Spsstatic int pxe_netif_get(struct iodesc *desc, void *pkt, size_t len, 8859087Sps time_t timeout); 8959087Spsstatic int pxe_netif_put(struct iodesc *desc, void *pkt, size_t len); 9059087Spsstatic void pxe_netif_end(struct netif *nif); 9158713Sjhb 9259087Spsextern struct netif_stats pxe_st[]; 9359087Spsextern struct in_addr rootip; 9459390Spsextern char rootpath[FNAME_SIZE]; 9559390Spsextern u_int16_t __pxeseg; 9659390Spsextern u_int16_t __pxeoff; 9759390Spsextern void __h0h0magic(void); 9858713Sjhb 9959087Spsstruct netif_dif pxe_ifs[] = { 10059087Sps/* dif_unit dif_nsel dif_stats dif_private */ 10159087Sps {0, 1, &pxe_st[0], 0} 10259087Sps}; 10358713Sjhb 10459087Spsstruct netif_stats pxe_st[NENTS(pxe_ifs)]; 10559087Sps 10659087Spsstruct netif_driver pxenetif = { 10759087Sps "pxenet", 10859087Sps pxe_netif_match, 10959087Sps pxe_netif_probe, 11059087Sps pxe_netif_init, 11159087Sps pxe_netif_get, 11259087Sps pxe_netif_put, 11359087Sps pxe_netif_end, 11459087Sps pxe_ifs, 11559087Sps NENTS(pxe_ifs) 11659087Sps}; 11759087Sps 11859087Spsstruct netif_driver *netif_drivers[] = { 11959087Sps &pxenetif, 12059087Sps NULL 12159087Sps}; 12259087Sps 12358713Sjhbstruct devsw pxedisk = { 12458713Sjhb "pxe", 12558713Sjhb DEVT_NET, 12658713Sjhb pxe_init, 12758713Sjhb pxe_strategy, 12858713Sjhb pxe_open, 12958713Sjhb pxe_close, 13058713Sjhb noioctl, 13158713Sjhb pxe_print 13258713Sjhb}; 13358713Sjhb 13458713Sjhb/* 13558713Sjhb * This function is called by the loader to enable PXE support if we 13658713Sjhb * are booted by PXE. The passed in pointer is a pointer to the 13758713Sjhb * PXENV+ structure. 13858713Sjhb */ 13958713Sjhbvoid 14058713Sjhbpxe_enable(void *pxeinfo) 14158713Sjhb{ 14258993Sps pxenv_p = (pxenv_t *)pxeinfo; 14358713Sjhb} 14458713Sjhb 14558713Sjhb/* 14658713Sjhb * return true if pxe structures are found/initialized, 14758713Sjhb * also figures out our IP information via the pxe cached info struct 14858713Sjhb */ 14958713Sjhbstatic int 15058713Sjhbpxe_init(void) 15158713Sjhb{ 15258713Sjhb t_PXENV_GET_CACHED_INFO *gci_p; 15358713Sjhb int counter; 15458713Sjhb uint8_t checksum; 15558713Sjhb uint8_t *checkptr; 15658713Sjhb 15758713Sjhb if(pxenv_p == NULL) 15858713Sjhb return (0); 15958713Sjhb 16058713Sjhb /* look for "PXENV+" */ 16158713Sjhb if (bcmp((void *)pxenv_p->Signature, S_SIZE("PXENV+"))) 16258713Sjhb return (0); 16358713Sjhb 16458713Sjhb /* make sure the size is something we can handle */ 16558713Sjhb if (pxenv_p->Length > sizeof(*pxenv_p)) { 16658713Sjhb printf("PXENV+ structure too large, ignoring\n"); 16758713Sjhb pxenv_p = NULL; 16858713Sjhb return (0); 16958713Sjhb } 17058713Sjhb 17158713Sjhb /* 17258713Sjhb * do byte checksum: 17358713Sjhb * add up each byte in the structure, the total should be 0 17458713Sjhb */ 17558713Sjhb checksum = 0; 17658713Sjhb checkptr = (uint8_t *) pxenv_p; 17758713Sjhb for (counter = 0; counter < pxenv_p->Length; counter++) 17858713Sjhb checksum += *checkptr++; 17958713Sjhb if (checksum != 0) { 18058713Sjhb printf("PXENV+ structure failed checksum, ignoring\n"); 18158713Sjhb pxenv_p = NULL; 18258713Sjhb return (0); 18358713Sjhb } 18459390Sps 18559390Sps if (pxenv_p->Version < 0x0201) { 18659390Sps printf("PXENV+ is not supported.\n"); 18759390Sps return (0); 18859390Sps } 18959390Sps 19059390Sps pxe_p = (pxe_t *)PTOV(pxenv_p->PXEPtr.segment * 16 + 19159390Sps pxenv_p->PXEPtr.offset); 19259390Sps 19359390Sps if (bcmp((void *)pxe_p->Signature, S_SIZE("!PXE"))) 19459390Sps return(0); 19559390Sps 19659390Sps checksum = 0; 19759390Sps checkptr = (uint8_t *)pxe_p; 19859390Sps for (counter = 0; counter < pxe_p->StructLength; counter++) 19959390Sps checksum += *checkptr++; 20059390Sps if (checksum != 0) { 20159390Sps printf("!PXE structure failed checksum. %x\n", checksum); 20259390Sps return(0); 20359390Sps } 20459390Sps 20559390Sps 20659390Sps printf("\n!PXE version %d.%d, real mode entry point @%04x:%04x\n", 20758713Sjhb (uint8_t) (pxenv_p->Version >> 8), 20858713Sjhb (uint8_t) (pxenv_p->Version & 0xFF), 20959390Sps pxe_p->EntryPointSP.segment, pxe_p->EntryPointSP.offset); 21058713Sjhb 21158713Sjhb gci_p = (t_PXENV_GET_CACHED_INFO *) scratch_buffer; 21258713Sjhb bzero(gci_p, sizeof(*gci_p)); 21358713Sjhb gci_p->PacketType = PXENV_PACKET_TYPE_BINL_REPLY; 21458713Sjhb pxe_call(PXENV_GET_CACHED_INFO); 21558713Sjhb if (gci_p->Status != 0) { 21658713Sjhb pxe_perror(gci_p->Status); 21758713Sjhb pxenv_p = NULL; 21858713Sjhb return (0); 21958713Sjhb } 22059087Sps bcopy(PTOV((gci_p->Buffer.segment << 4) + gci_p->Buffer.offset), 22159087Sps &bootplayer, gci_p->BufferSize); 22258713Sjhb 22359087Sps /* 22459087Sps * XXX - This is a major cop out. We should request this 22559087Sps * from DHCP, but we can't do that until we have full UNDI 22659087Sps * support. 22759087Sps * 22859087Sps * Also set the nfs server's IP. 22959087Sps */ 23059087Sps strcpy(rootpath, PXENFSROOTPATH); 23159087Sps rootip.s_addr = bootplayer.sip; 23259087Sps 23358713Sjhb return (1); 23458713Sjhb} 23558713Sjhb 23659087Sps 23759087Spsstatic int 23859087Spspxe_strategy(void *devdata, int flag, daddr_t dblk, size_t size, 23959087Sps void *buf, size_t *rsize) 24058713Sjhb{ 24159087Sps return (EIO); 24258713Sjhb} 24358713Sjhb 24459087Spsstatic int 24559087Spspxe_open(struct open_file *f, ...) 24658713Sjhb{ 24759087Sps va_list args; 24859087Sps char *devname; /* Device part of file name (or NULL). */ 24959087Sps int error = 0; 25058713Sjhb 25159087Sps va_start(args, f); 25259087Sps devname = va_arg(args, char*); 25359087Sps va_end(args); 25459087Sps 25559087Sps /* On first open, do netif open, mount, etc. */ 25659087Sps if (pxe_opens == 0) { 25759087Sps /* Find network interface. */ 25859087Sps if (pxe_sock < 0) { 25959087Sps pxe_sock = netif_open(devname); 26059087Sps if (pxe_sock < 0) { 26159087Sps printf("pxe_open: netif_open() failed\n"); 26259087Sps return (ENXIO); 26359087Sps } 26459087Sps if (debug) 26559087Sps printf("pxe_open: netif_open() succeeded\n"); 26659087Sps } 26759087Sps } 26859087Sps pxe_opens++; 26959087Sps f->f_devdata = &pxe_sock; 27059087Sps return (error); 27158713Sjhb} 27258713Sjhb 27359087Spsstatic int 27459087Spspxe_close(struct open_file *f) 27558713Sjhb{ 27658713Sjhb 27759087Sps#ifdef PXE_DEBUG 27859087Sps if (debug) 27959087Sps printf("pxe_close: opens=%d\n", pxe_opens); 28059087Sps#endif 28158713Sjhb 28259087Sps /* On last close, do netif close, etc. */ 28359087Sps f->f_devdata = NULL; 28459087Sps /* Extra close call? */ 28559087Sps if (pxe_opens <= 0) 28659087Sps return (0); 28759087Sps pxe_opens--; 28859087Sps /* Not last close? */ 28959087Sps if (pxe_opens > 0) 29059087Sps return(0); 29159087Sps rootip.s_addr = 0; 29259087Sps if (pxe_sock >= 0) { 29359087Sps#ifdef PXE_DEBUG 29459087Sps if (debug) 29559087Sps printf("pxe_close: calling netif_close()\n"); 29659087Sps#endif 29759087Sps netif_close(pxe_sock); 29859087Sps pxe_sock = -1; 29959087Sps } 30059087Sps return (0); 30159087Sps} 30258713Sjhb 30359087Spsstatic void 30459087Spspxe_print(int verbose) 30559087Sps{ 30659087Sps if (pxenv_p != NULL) { 30759087Sps if (*bootplayer.Sname == '\0') { 30859087Sps printf(" "IP_STR":%s\n", 30959087Sps IP_ARGS(htonl(bootplayer.sip)), 31059087Sps bootplayer.bootfile); 31159087Sps } else { 31259087Sps printf(" %s:%s\n", bootplayer.Sname, 31359087Sps bootplayer.bootfile); 31459087Sps } 31559087Sps } 31658713Sjhb 31759087Sps return; 31858713Sjhb} 31958713Sjhb 32059390Spsstatic void 32159390Spspxe_cleanup(void) 32259390Sps{ 32359390Sps t_PXENV_UNLOAD_STACK *unload_stack_p = 32459390Sps (t_PXENV_UNLOAD_STACK *)scratch_buffer; 32559390Sps t_PXENV_UNDI_SHUTDOWN *undi_shutdown_p = 32659390Sps (t_PXENV_UNDI_SHUTDOWN *)scratch_buffer; 32759087Sps 32859390Sps pxe_call(PXENV_UNLOAD_STACK); 32959390Sps if (unload_stack_p->Status != 0) 33059390Sps panic("pxe_cleanup: UNLOAD_STACK failed"); 33159390Sps 33259390Sps pxe_call(PXENV_UNDI_SHUTDOWN); 33359390Sps if (undi_shutdown_p->Status != 0) 33459390Sps panic("pxe_cleanup: UNDI_SHUTDOWN failed"); 33559390Sps printf("All cleaned up!\n"); 33659390Sps} 33759390Sps 33858713Sjhbvoid 33958713Sjhbpxe_perror(int err) 34058713Sjhb{ 34158713Sjhb return; 34258713Sjhb} 34358713Sjhb 34458713Sjhbvoid 34558713Sjhbpxe_call(int func) 34658713Sjhb{ 34758713Sjhb bzero(&v86, sizeof(v86)); 34858713Sjhb bzero(data_buffer, sizeof(data_buffer)); 34959390Sps 35059390Sps __pxeseg = pxe_p->EntryPointSP.segment; 35159390Sps __pxeoff = pxe_p->EntryPointSP.offset; 35259390Sps 35359390Sps v86.ctl = V86_ADDR | V86_CALLF | V86_FLAGS; 35459390Sps v86.edx = VTOPSEG(scratch_buffer); 35559390Sps v86.eax = VTOPOFF(scratch_buffer); 35659390Sps v86.addr = (VTOPSEG(__h0h0magic) << 16) | VTOPOFF(__h0h0magic); 35759390Sps v86.ebx = func; 35858713Sjhb v86int(); 35959390Sps v86.ctl = V86_FLAGS; 36058713Sjhb} 36158713Sjhb 36259087Sps 36359087Spstime_t 36459087Spsgetsecs() 36558713Sjhb{ 36659087Sps time_t n = 0; 36759087Sps time(&n); 36859087Sps return n; 36958713Sjhb} 37058713Sjhb 37158713Sjhbstatic int 37259087Spspxe_netif_match(struct netif *nif, void *machdep_hint) 37358713Sjhb{ 37459087Sps return 1; 37558713Sjhb} 37658713Sjhb 37759087Sps 37858713Sjhbstatic int 37959087Spspxe_netif_probe(struct netif *nif, void *machdep_hint) 38058713Sjhb{ 38159087Sps t_PXENV_UDP_OPEN *udpopen_p = (t_PXENV_UDP_OPEN *)scratch_buffer; 38259087Sps bzero(udpopen_p, sizeof(*udpopen_p)); 38358713Sjhb 38459087Sps udpopen_p->src_ip = bootplayer.yip; 38559087Sps pxe_call(PXENV_UDP_OPEN); 38658713Sjhb 38759390Sps if (udpopen_p->status != 0) { 38859087Sps printf("pxe_netif_probe: failed %x\n", udpopen_p->status); 38959390Sps return -1; 39059390Sps } 39159087Sps return 0; 39258713Sjhb} 39358713Sjhb 39459087Spsstatic void 39559087Spspxe_netif_end(struct netif *nif) 39658713Sjhb{ 39759087Sps t_PXENV_UDP_CLOSE *udpclose_p = (t_PXENV_UDP_CLOSE *)scratch_buffer; 39859087Sps bzero(udpclose_p, sizeof(*udpclose_p)); 39958713Sjhb 40059087Sps pxe_call(PXENV_UDP_CLOSE); 40159087Sps if (udpclose_p->status != 0) 40259087Sps printf("pxe_end failed %x\n", udpclose_p->status); 40358713Sjhb} 40458713Sjhb 40559087Spsstatic void 40659087Spspxe_netif_init(struct iodesc *desc, void *machdep_hint) 40758713Sjhb{ 40858713Sjhb} 40958713Sjhb 41058713Sjhbstatic int 41159087Spspxe_netif_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout) 41258713Sjhb{ 41359087Sps return len; 41458713Sjhb} 41558713Sjhb 41658713Sjhbstatic int 41759087Spspxe_netif_put(struct iodesc *desc, void *pkt, size_t len) 41858713Sjhb{ 41959087Sps return len; 42058713Sjhb} 42158713Sjhb 42259087Spsssize_t 42359087Spssendudp(struct iodesc *h, void *pkt, size_t len) 42458713Sjhb{ 42559087Sps t_PXENV_UDP_WRITE *udpwrite_p = (t_PXENV_UDP_WRITE *)scratch_buffer; 42659087Sps bzero(udpwrite_p, sizeof(*udpwrite_p)); 42759087Sps 42859087Sps udpwrite_p->ip = bootplayer.sip; 42959087Sps udpwrite_p->dst_port = h->destport; 43059087Sps udpwrite_p->src_port = h->myport; 43159087Sps udpwrite_p->buffer_size = len; 43259087Sps udpwrite_p->buffer.segment = VTOPSEG(pkt); 43359087Sps udpwrite_p->buffer.offset = VTOPOFF(pkt); 43458713Sjhb 43559087Sps pxe_call(PXENV_UDP_WRITE); 43658713Sjhb 43759390Sps#if 0 43859087Sps /* XXX - I dont know why we need this. */ 43959087Sps delay(1000); 44059390Sps#endif 44159390Sps if (udpwrite_p->status != 0) { 44259087Sps printf("sendudp failed %x\n", udpwrite_p->status); 44359390Sps return -1; 44459390Sps } 44559087Sps return len; 44658713Sjhb} 44758713Sjhb 44859087Spsssize_t 44959087Spsreadudp(struct iodesc *h, void *pkt, size_t len, time_t timeout) 45058713Sjhb{ 45159087Sps t_PXENV_UDP_READ *udpread_p = (t_PXENV_UDP_READ *)scratch_buffer; 45259087Sps struct udphdr *uh = NULL; 45359087Sps 45459087Sps uh = (struct udphdr *) pkt - 1; 45559087Sps bzero(udpread_p, sizeof(*udpread_p)); 45659087Sps 45759087Sps udpread_p->dest_ip = bootplayer.yip; 45859087Sps udpread_p->d_port = h->myport; 45959087Sps udpread_p->buffer_size = len; 46059087Sps udpread_p->buffer.segment = VTOPSEG(data_buffer); 46159087Sps udpread_p->buffer.offset = VTOPOFF(data_buffer); 46258713Sjhb 46359087Sps pxe_call(PXENV_UDP_READ); 46458713Sjhb 46559390Sps#if 0 46659087Sps /* XXX - I dont know why we need this. */ 46759087Sps delay(1000); 46859390Sps#endif 46959390Sps if (udpread_p->status != 0) { 47059390Sps /* XXX: This happens a lot. It shouldn't. */ 47159390Sps if (udpread_p->status != 1) 47259390Sps printf("readudp failed %x\n", udpread_p->status); 47359390Sps return -1; 47459390Sps } 47559087Sps bcopy(data_buffer, pkt, udpread_p->buffer_size); 47659087Sps uh->uh_sport = udpread_p->s_port; 47759087Sps return udpread_p->buffer_size; 47858713Sjhb} 479