Xinu7/src/cmd/download/OLD/eload/src/eload.c

/* eload.c - eload */

#include "../h/eload.h"
#include "../h/dlpack.h"
#include "../h/deqna.h"
#include "../h/ether.h"
#include "../h/network.h"
#include "../h/slu.h"
#include "../h/ip.h"
#include "../h/udp.h"
#include "../h/arp.h"

/*------------------------------------------------------------------------
 *  Etherload - LSI 11/23 resident piece of Ethernet downloader.  We try
 *		to keep as much as possible on the stack so it run from
 *		ROM.  There are two parts: ecopy and erun
 *
 *  Calling conventions:
 *	eload:	main entry point.  It assumes that either MMU is enabled
 *		and code is resident in memory page 7 or MMU is disabled
 *		and code it resident in memory page 0.  It checks MMU to
 *		decide whether to use "ecopy" or "erun"
 *
 *	erun:	assumes itself resident in memory page 7, MMU enabled,
 *		addresses in virtual page 0, interrupts disabled, kernel
 *		mode. This version can load memory pages 0-5 only (up
 *		to 48K).  Will disable MMU and branch to downloaded
 *		program.
 *
 *	ecopy:	assumes itself resident in memory page 0, MMU disabled,
 *		interrupts disabled, kernel mode.  Also assumes machine
 *		has 64K bytes of real memory.  Ecopy sets up MMU and
 *		executes eload once it finishes copying itself to real
 *		memory page 7.
 *------------------------------------------------------------------------
 */
eload()
{
	int	*p;

	p = (int *) 0177572;		/* Memory management register	*/
	if ( isodd(*p) )
		erun();
	else
		ecopy();
}

/*------------------------------------------------------------------------
 *  ecopy - copy self to memory page 7 and execute from there
 *------------------------------------------------------------------------
 */
ecopy()
{
	int	eload();
	char	*from, *to;
	int	*p;

/*T*/	kputc(CDEVICE, 'a');
	p = (int *) 0172340;		/* PAR for page 0		*/
	*p++ = 0;			/* map virtual 0 to real 0	*/
	*p++ = 01600;			/* map virtual 1 to real 7	*/
	p = (int *) 0172356;		/* PAR for page 7		*/
	*p = 0177600;			/* map virtual 7 to I/O		*/
/*T*/	kputc(CDEVICE, 'b');
	p = (int *) 0172300;		/* PDR for page 0		*/
	*p++ = 077406;			/* page 0 len=8K, RW access	*/
	*p++ = 077406;			/* page 1 len=8K, RW access	*/
	*p++ = 077406;			/* page 2 len=8K, RW access	*/
	*p++ = 077406;			/* page 3 len=8K, RW access	*/
	*p++ = 077406;			/* page 4 len=8K, RW access	*/
	*p++ = 077406;			/* page 5 len=8K, RW access	*/
	*p++ = 077406;			/* page 6 len=8K, RW access	*/
	*p++ = 077406;			/* page 7 len=8K, RW access	*/
/*T*/	kputc(CDEVICE, 'c');

	p = (int *) 0177572;		/* MMU control register		*/
	*p = 1;				/* Turn on memory management	*/
/*T*/	kputc(CDEVICE, 'd');
	from = (char *)0;
	to   = (char *)020000;
	while (from < (char *)020000)	/* copy real page 0 to real 7	*/
		*to++ = *from++;
	/* have now copied myself from real page 0 to real page 7	*/
	p = (int *) 0172340;		/* PAR for virtual page 0	*/
	*p = 01600;			/* set virtual 0 to real 7	*/
/*T*/	kputc(CDEVICE, 'e');
	/* SP just jumped from real 0 to real 7, but should not cause	*/
	/* problem because we copied code into real 7 from real 0	*/
	/* However, we have to remember that local variables revert to	*/
	/* values before the copy.					*/
/*T*/	kputc(CDEVICE, 'f');
	erun();
}

/*------------------------------------------------------------------------
 *  erun - perform Ethernet download executing from memory page 7
 *------------------------------------------------------------------------
 */
erun()
{
	struct	dqregs	*dqptr;		/* pointer to DEQNA registers	*/
	struct	dcmd	*dcmptr;	/* pointer to DEQNA command list*/
	struct	epacket	*packet;	/* pointer to Ethernet frame	*/
	struct	arppak	*apacptr;	/* pointer to ARP packet	*/
	struct	ip	*ipptr;		/* pointer to IP part of packet	*/
	struct	udp	*udpptr;	/* pointer to udp part		*/
	struct	dlpack	*dlptr;		/* pointer to downloader packet	*/
	struct	csr	*console;	/* console line CSR address	*/
	int	*iptr;			/* integer pointer		*/
	int	i, j, len;		/* indexes and length of input	*/
	char	buffer[EMAXPAK];	/* general buffer for reading	*/
	char	setup[DQ_ROWS][DQ_COLS];/* area for setup packet	*/
	char	secho[sizeof(setup)+4];	/* area for setup packet echo	*/
	struct	dcmd	ercmd[2];	/* DEQNA read command list	*/
	struct	dcmd	ewcmd[2];	/* DEQNA write command list	*/
	Eaddr	myeaddr;		/* my Ethernet address		*/
	IPaddr	myaddr;			/* my IP address		*/
	IPaddr	dladdr;			/* downloader's IP address	*/
	short	dludp;			/* downloader's UDP port	*/
	char	*from, *to;
	int	(*q)();

	/* set up remaining memory management (0, 7) already done */

	iptr = (int *)0172342;		/* PAR for page 1		*/
	*iptr++ = 00200;		/* map virtual 1 to real 1	*/
	*iptr++ = 00400;		/* map virtual 2 to real 2	*/
	*iptr++ = 00600;		/* map virtual 3 to real 3	*/
	*iptr++ = 01000;		/* map virtual 4 to real 4	*/
	*iptr++ = 01200;		/* map virtual 5 to real 5	*/
	*iptr++ = 00000;		/* map virtual 6 to real 0	*/

	iptr = (int *)0172300;		/* PDR for page 1		*/
	*iptr++ = 077406;		/* page 0 len=8K, RW access	*/
	*iptr++ = 077406;		/* page 1 len=8K, RW access	*/
	*iptr++ = 077406;		/* page 2 len=8K, RW access	*/
	*iptr++ = 077406;		/* page 3 len=8K, RW access	*/
	*iptr++ = 077406;		/* page 4 len=8K, RW access	*/
	*iptr++ = 077406;		/* page 5 len=8K, RW access	*/
	*iptr++ = 077406;		/* page 6 len=8K, RW access	*/

	/* set up device addresses */

	dqptr = (struct dqregs *) EDEVICE;
	console = (struct csr *)  CDEVICE;
/*T*/	kputc(console, '0');

	/* Pick up arguments from (real) locations 0-9 */

	blkcopy(&myaddr, 0140000, IPLEN);
	blkcopy(&dladdr, 0140004, IPLEN);
	blkcopy(&dludp,  0140010, sizeof(short));

	/* establish read and write buffer descriptor lists */

	dcmptr = &ercmd[1];
	dcmptr->dc_flag = DC_NUSED;
	dcmptr->dc_bufh = DC_ENDL;
	dcmptr->dc_buf = NULL;
	dcmptr->dc_st1 = dcmptr->dc_st2 = DC_INIT;

	dcmptr = &ewcmd[1];
	dcmptr->dc_flag = DC_NUSED;
	dcmptr->dc_bufh = DC_ENDL;
	dcmptr->dc_buf = NULL;
	dcmptr->dc_st1 = dcmptr->dc_st2 = DC_INIT;

	/* initialize device */

	dqptr->d_csr |=  DQ_REST;
	dqptr->d_csr &= ~DQ_REST;
	dqptr->d_vect = ETHINTV;

	/* extract physical ethernet address and setup device for it	*/

	for (iptr=(int *)dqptr ,i=0 ; i<EPADLEN ; i++)
		myeaddr[i] = LOWBYTE & *iptr++;

	for (i=0 ; i < (DQ_ROWS>>1) ; i++) {
		setup[i+DQ_SETD][0] = setup[i][0] = 0;
		setup[i+DQ_SETD][1] = setup[i][1] = i<EPADLEN ? DQ_BCST : 0;
		for (j=2 ; j<DQ_COLS ; j++)
			setup[i+DQ_SETD][j] = setup[i][j] = 
				i<EPADLEN ? myeaddr[i] : 0;
	}
	erstart(&ercmd[0], secho, sizeof(secho));
	ewstart(&ewcmd[0], setup, sizeof(setup), DC_SETUP);
/*T*/	kputc(console,'1');

	/* poll device until setup processed */

/*T*/	kputc(console,'2');
	for (dcmptr=ercmd ; dcmptr->dc_st1 == DC_INIT ; )
		;
/*T*/	kputc(console,'3');

	/* reset device, leaving it online */

	dqptr->d_csr |=  DQ_REST;
	dqptr->d_csr &= ~DQ_REST;
	dqptr->d_csr &= ~DQ_ELOP;
	dqptr->d_csr |= (DQ_ENBL| DQ_ILOP);
/*T*/	kputc(console, '4');

	/* DEQNA has been initialized and is ready to use; need to  */
	/* send ARP packet to advertise physical address before     */
	/* being ready to receive data packets			    */

	packet = (struct epacket *) buffer;

	/* manufacture ARP packet with my IP and Ether addresses */
	blkcopy(packet->ep_hdr.e_dest, EBCAST, AR_HLEN);
	blkcopy(packet->ep_hdr.e_src, myeaddr, AR_HLEN);
	packet->ep_hdr.e_ptype = hs2net(EP_ARP);
	apacptr = packet->ep_data;
	apacptr->ar_hrd = hs2net(AR_HRD);
	apacptr->ar_prot = hs2net(AR_PROT);
	apacptr->ar_hlen = AR_HLEN;
	apacptr->ar_plen = AR_PLEN;
	apacptr->ar_op = hs2net(AR_RPLY);
	blkcopy(apacptr->ar_sha, myeaddr, AR_HLEN);
	blkcopy(apacptr->ar_spa, myaddr, AR_PLEN);
	blkcopy(apacptr->ar_tha, myeaddr, AR_HLEN);
	blkcopy(apacptr->ar_tpa, myaddr, AR_PLEN);
	/* send ARP packet */
	ewrite(ewcmd, myeaddr, packet, EMINPAK);
/*T*/	kputc(console,'5');
	kputc(console, 'R');

	/* Main loop: read packet and take action */

	ipptr = (struct ip *)packet->ep_data;
	udpptr = (struct udp *)ipptr->i_data;
	dlptr = (struct	dlpack *) udpptr->u_data;
	while (TRUE) {
		len = eread(ercmd, buffer, EMAXPAK);
/*T*/		kputc(console,'6');
		if (len == SYSERR) {
/*T*/			kputc(console, 'l');
			continue;
		} else if ( net2hs(packet->e_ptype) != EP_IP) {
/*T*/			kputc(console, 'i');
			continue;
		} else if (ipptr->i_proto != IPRO_UDP) {
/*T*/			kputc(console, 'u');
			continue;
		} else if (!blkequ(ipptr->i_src, dladdr, IPLEN)) {
/*T*/			kputc(console, 's');
			continue;
		} else if (!blkequ(ipptr->i_dest, myaddr, IPLEN)) {
			kputc(console, 'd');
			continue;

		} else if ( net2hs(udpptr->u_sport) != dludp) {
			kputc(console, 'p');
			continue;
		}

		/* Have received valid UDP/IP packet from our download	*/
		/* port on the correct machine.  ACK and then honor it.	*/

		kputc(console, 'A');
		if ((to = (char *)net2hs(dlptr->dl_addr)) != DLCMD) {
/*T*/			kputc(console,'7');
			/* copy dlptr->dl_len bytes from dlptr->dl to	*/
			/* location given by i but translate refs in	*/
			/* page 0 to refs in virtual page 6		*/
			from = (char *) &dlptr->dl;
			len = net2hs(dlptr->dl_len);
			while (len-- > 0) {
				if ( ((unsigned)to) < 020000) {
					*(to+0140000) = *from++;
					to++;
				} else {
					*to++ = *from++;
				}
			}
/*T*/			kputc(console,'8');
		} else { /* Command packet to start prog execution	*/
/*T*/			kputc(console,'9');

			/* handle command packet */

			iptr = (int *)0172354;	/* PAR for virtual 6	*/
			*iptr = 0140000;	/* map Vir. 6 to real 6	*/
			iptr = (int *)0157776;	/* top of page 6	*/
			*iptr-- = dlptr->dl.dl.dl_regs[7];
			*iptr-- = 0137;		/* jump instruction	*/
			*iptr   = 05;		/* reset instruction	*/
			q = iptr;
			(*q)();
			/* This is the easiest way to code a jump in C	*/
			/* It should be cleaned up.  Maybe put in the	*/
			/* startup routine... Can we make it the action	*/
			/* if eload returns?				*/

			/* have now gone off into another universe to	*/
			/* execute the program we just downloaded	*/
		}
	}
}