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