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 */
}
}
}