Xinu7/src/cmd/download/vax/src/eul.c

Compare this file to the similar file:
Show the results in this format:

/*----------------------------------------------------------------------*/
/*									*/
/*	eul	--  Upload from VAX to UNIX machine over ethernet	*/
/*									*/
/* This program uploads a memory image from a VAX into a coreX file	*/
/* on a UNIX machine.							*/
/*									*/
/* Configuration -							*/
/*	VAX tty line connected to VAX console port			*/
/*									*/
/* Usage:								*/
/*	upload [-lx] [-o filename] [ [minaddr] max addr ]		*/
/*									*/
/* Author:								*/
/*		Tom Stonecypher						*/
/*		Purdue University CS Department				*/
/*		December, 1986						*/
/*		based on the LSI 11 ethernet uploader			*/
/*									*/
/*----------------------------------------------------------------------*/

/* UNIX include files */
#include "/usr/include/stdio.h"
#include "/usr/include/sgtty.h"
#include "/usr/include/signal.h"
#include "/usr/include/errno.h"
#include "/usr/include/setjmp.h"
#include "/usr/include/errno.h"
#include "/usr/include/sys/types.h"
#include "/usr/include/sys/file.h"
#include "/usr/include/ctype.h"

/* 4.3 Network include files */
#include "/usr/include/netinet/in.h"
#include "/usr/include/sys/socket.h"
#include "/usr/include/netdb.h"

/* Xinu domain include files */
#include <coreX.h>
#include <baud.h>
#include <getdev.h>
#include <ethdown.h>
#include <vaxul.h>
#include <dlpack.h>
#include <upload.h>
#include "../eload/src/.version"

char 	*msgval;
char	*Openfiles[20];
struct	coreX	Corehead;
jmp_buf	env;			/* environment to return to on timeout	*/
static	char *alarm_msg;	/* message printed when alarm signalled	*/
struct	arglist	A;		/* parsed argument values		*/
char	*lockfile;		/* path name of lockfile		*/
struct	sgttyb	VAXttyb;
char	*index();
int	touch();
extern	int	errno;
int	Sfd;			/* socket descriptor for udp read/writes*/

/*
 * MAIN -- upload memory image from VAX to UNIX machine
 */
main ( argc, argv )
int argc;
char *argv[];
{
	char	*machine, *cputype;
	u_long	ip_addr;
	int	i, core_fd;

	lockfile = argv[--argc];
	machine = argv[--argc];
					/* cputype = get_cputype(machine); */
	procargs(argc, argv);		/* process arguments into struct A */
	initsigs();

	if ((core_fd = creat(A.corefile, 0666)) < 0) {
		perror(A.corefile);
		exit(1);
	}
	dup2(core_fd, COREFD);	/* write to the core file using COREFD	*/
	Openfiles[DEVFD] = get_name(machine);
	Openfiles[COREFD] = A.corefile;

	Corehead.c_zero1 = Corehead.c_zero2 = 0;
	Corehead.c_zero3 = Corehead.c_zero4 = Corehead.c_zero5 = 0;
	Corehead.c_magic = COREMAGIC;
	Corehead.c_size = 0;	/* size currently not used */

	getudp_port(&ip_addr, machine);

	if (ip_addr == 0 || ethuload(ip_addr) != ETH_OK ) {
		/* can't do ethernet upload */
		message("performing serial upload...\n",0,0);
/* patch here */
	}
	touch(lockfile);
	message("\rdone         \n",0,0);
	callexit(0);
}

/*----------------------------------------------------------------------
 * initsigs -- trap software signals so that lines can be restored to
 * their original state and the core file can be written
 *----------------------------------------------------------------------
*/
initsigs()
{
	int	alarmhandler(), inthandler();

	if ( signal(SIGINT,SIG_IGN) != SIG_IGN )
		signal(SIGINT,inthandler);
	if ( signal(SIGTERM,SIG_IGN) !=	SIG_IGN	)
		signal(SIGTERM,inthandler);
	if ( signal(SIGQUIT,SIG_IGN) !=	SIG_IGN	)
		signal(SIGQUIT,inthandler);
	signal(SIGALRM,	alarmhandler);
}

/*
 *======================================================================
 * procargs - parse and check arguments, modifying global A structure
 *======================================================================
 */
procargs(argc, argv)
int argc;
char *argv[];
{
	int	arg, i, unswitched;
	/*
	 * assign default values
	 */
	A.uploader = NULL;
	A.lowaddr = 0;
	A.highaddr = MAXADDR;
	A.corefile = DEFFILE;
	A.baudrate = B9600;
	A.class = NULL;
	A.machnum = GD_ANYDEV;
	A.devflags = GD_MYDEV;
	A.verbose = FALSE;
	A.silent = FALSE;
	A.noload = FALSE;
	unswitched = 0;
	for ( arg=1 ; arg<argc ; arg++ ) {
		if ( argv[arg][0] == '-' )
			switch ( argv[arg][1] ) {
			case 'v':
				A.verbose = TRUE;
				break;
			case 's':
				A.silent = TRUE;
				break;
			case 'n':
				A.noload = TRUE;
				break;
			case 'o':
				A.corefile = argv[arg++];
				break;
			case 'a':
				fprintf(stderr,"-a arg. not implemented\n");
				break;
			default:	/* generic uploader already parsed: */
				break;	/* don't complain about arguments */
			}
	else /* there's no dash in front */
		switch ( unswitched++ )	{
		case 0:
			A.highaddr = atoi(argv[arg]);
			message("highaddr=%x\n",A.highaddr,0);
			break;
		case 1:
			A.lowaddr = A.highaddr;
			A.highaddr = atoi(argv[arg]);
			message("lowaddr=%x\n",A.lowaddr,0);
			message("highaddr=%x\n",A.highaddr,0);
			break;
		default:
			break;		/* don't complain -- already parsed */
		}
	}
}

/*
 *=========================================
 * alarmhandler - return from alarm calls
 *=========================================
 */
alarmhandler()
{
	signal(SIGALRM, alarmhandler);
	if (alarm_msg != NULL)
		fprintf(stderr, "%s\n", alarm_msg);
	longjmp(env, EINTR);
	/* not reached */
}

/*
 *=====================================
 * inthandler -- interrupt processing
 *=====================================
 */
inthandler()
{
	touch(lockfile);
	callexit(2);
}

/*
 *=======================================
 * callexit - restore ttymodes and exit
 *=======================================
 */
callexit(ret)
int ret;
{
	/* stty(DEVFD,&VAXttyb); */
	exit(ret);
}

/* 
 *==================================================
 * getipl - get	the current interrupt priority level
 *==================================================
 */
getipl(ipl)
int	*ipl;
{
	char buf[60];

	sendcons("E/I 12\r");
	readuntil("\r", buf, 7, 32);
	sscanf(buf, "%*s %*x %x", ipl);
}

/*
 *==========================================
 * getregs - get contents of the registers
 *==========================================
 */
getregs(regs)
int	*regs;
{
	int r;
	char buf[64], *val;

	for ( r=0 ; r<REGISTERS; r++ )	{
		if (r < R0) {
			sprintf(buf, "E/I %x\r", r);
			sendcons(buf, TRUE);
		}
		else if (r == R0)
			sendcons("E/L/G 0\r", TRUE);
		else if (r <= FP)
			sendcons("E +\r", TRUE);
		else if (r == PC)
			sendcons("E/L/G F\r", TRUE);
		else if (r == PSL)	/* best can do is get IPL */
			sendcons("E/I 12\r", TRUE);
		else {			/* p0br, p0lr, p1br, p1lr */
			sprintf(buf, "E/I %x\r", r-12);
			sendcons(buf, TRUE);
		}
		readuntil("\r", buf, 7, 32);
		sscanf(buf,"%*s %*x %x", regs);
		if (r == PSL)
			*regs = *regs << 16;
		regs++;
		if ( !A.silent && !A.verbose )
			displayval(r);
	}
	if ( !A.silent && !A.verbose )
		fprintf(stderr,"\r");
	getprompt(6);
}

/*
 *==================================================
 * readuntil - read from line until some character
 *==================================================
 */
readuntil ( ch, buf, time, nchars )
int time, nchars;
char *ch, *buf;
{
	settimer(time, "read timed out");
	if (setjmp(env) == EINTR) {
		fprintf(stderr, "readuntil did not find %s\n", unctrl(*ch));
		callexit(1);
	}
	if ( A.verbose ) {
		fprintf(stderr,"IN: "); fflush(stderr);
	}
	do {
		if ( read(DEVFD, buf, 1) != 1 ) {
			perror(Openfiles[DEVFD]);
			callexit(1);
		}
		if (A.verbose )
			fprintf(stderr,"%s",unctrl(*buf));
	} while	( (*buf == '\0' || index(ch,*buf++) == NULL) &&
		  --nchars > 0 );
	*buf = EOS;
	canceltimer();
	if ( A.verbose )
		fprintf(stderr,"\n");
}

/*
 *=================================================
 * message - conditionally display status message
 *=================================================
 */
message(str,arg,arg2)
int	arg, arg2;
char	*str;
{
	if ( !A.silent && !A.verbose)
		fprintf(stderr,str,arg,arg2);
	fflush(stderr);
}

/*
 *============================================
 * displayval - display a number on one line
 *============================================
 */
displayval(val)
int val;
{
	fprintf(stderr,"\r%d     ",val);
	fflush(stderr);
}

/*
 *==============================================
 * displayreg - display a register on one line
 *==============================================
 */
displayreg(val)
int val;
{
        if ( val < 0 )
		fprintf(stderr,"\rPS     ");
        else
		fprintf(stderr,"\rR%d    ",val);
	fflush(stderr);
}

/*
 *=================================================
 * getudp_port - get a udp port for packet transfer
 *=================================================
 */
getudp_port(ip_addr, machine)
u_long	*ip_addr;
char	*machine;
{
	struct	sockaddr_in sin, sout;
	struct	hostent	*phost;
	char	*hostname;

	hostname = get_hname(machine);
	if (isdigit(*hostname))	/* IP dot notation instead of name */
		*ip_addr = inet_addr(hostname);
	else {			/* hostname format */
		phost = gethostbyname(hostname);
		if ((char *)phost != NULL && phost->h_addrtype == AF_INET)
			bcopy(phost->h_addr, ip_addr, sizeof ip_addr);
		else
			*ip_addr = 0;
	}
	if ((Sfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
		perror("creating socket");
		callexit(1);
	}
	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_port = 0;
	if (bind(Sfd, (caddr_t) &sin, sizeof(sin)) < 0) {
		perror("bind");
		callexit(1);
	}
	sout.sin_family = AF_INET;
	sout.sin_addr.s_addr = *ip_addr;
	sout.sin_port = htons((u_short) DLUDP);

	if (connect(Sfd, &sout, sizeof(sout)) < 0) {
		perror("connect");
		callexit(1);
	}
}

struct	dl_pack	ackdl;

/*
 *=============================================
 * getack - get an ack from ELOAD over ethernet
 *=============================================
 */
struct	dl_pack	*getack()
{
	char	*ackmsg;

	settimer(ACKTIME, NULL);
	if (setjmp(env)	== EINTR)		/* timed out		*/
		return(NULL);
	if (read(Sfd, &ackdl, sizeof ackdl) < 0) {
		perror("udp packet read");
		callexit(1);
	}
	canceltimer();
	if (A.verbose) {
		switch (ackdl.dl_hdr.dl_op) {

		case DLACK:
			ackmsg = "ACK";
			break;
		case DLNAK:
			ackmsg = "NAK";
			break;
		case DLRAK:
			ackmsg = "ready-ack";
			break;
		case DLQAK:
			ackmsg = "quit-ack";
			break;
		case DLENK:
			ackmsg = "upload out of bounds";
			break;
		default:
			ackmsg = "invalid ack command";
			break;
		}
		fprintf( stderr,
			"IN(getack): %s, addr = 0x%x, len = 0x%x\n",
			ackmsg, ntohl(ackdl.dl_hdr.dl_addr),
			ntohs(ackdl.dl_hdr.dl_len) );
	}
	return(&ackdl);
}

/*
 *=============================================
 * ethuload - upload memory image over ethernet
 *=============================================
 */
ethuload(ip_addr)
int ip_addr;
{
	u_long	addr;
	u_short	len;
	int	iNak,	version;
	struct	dl_pack packet,	*pDlAck;

	start_eload();
	build_dlpack(DLID, 0, 0, &packet);
	usleep(400000);
	for (iNak = 0; iNak < ETHRETRY; iNak++ ) {
		if (iNak > 0)
			fprintf(stderr,"\rretrying...");
		send_ulpack(Sfd, &packet);
		pDlAck = getack();
		if (pDlAck != NULL && pDlAck->dl_hdr.dl_op == DLRAK)
			break;
	}
	if (iNak == ETHRETRY) {
		message("can't start eload\n",0,0);
		return(ETH_FAILED);
	}
	if ( (version = pDlAck->dl_hdr.dl_vers & LOWBYTE) != VERSION &&
	     version != ROMVERS && !A.silent )
		fprintf(stderr,"version mismatch, trying upload anyway\n");
	for ( addr = A.lowaddr ; addr <= A.highaddr ; addr += len ) {
		len = addr+DLMAXD > A.highaddr ? A.highaddr-addr+1 : DLMAXD;
		build_dlpack(DLEXT, len, addr, &packet);
		for (iNak = 0; iNak < ETHRETRY; iNak++ ) {
			if (iNak > 0)
				fprintf(stderr,"\rretrying...");
			send_ulpack(Sfd, &packet);
			pDlAck = getack();
			/* test for ACK & correct address sequence number */
			if (pDlAck != NULL)
			if ( pDlAck->dl_hdr.dl_op == DLEAK &&
			     ntohl(pDlAck->dl_hdr.dl_addr) == addr )
				break;
			else if (pDlAck->dl_hdr.dl_op == DLENK)
				break;	/* done uploading */
		}
		if (pDlAck->dl_hdr.dl_op == DLENK) {
			A.highaddr = addr - 1;
			break;
		}
		if (iNak == ETHRETRY) {
			message("ethernet upload failed\n",0,0);
			return(ETH_FAILED);
		}
		if (!A.silent && !A.verbose)
			if ( (addr % 0x2000) == 0 ) {
				fprintf(stderr,
					"\rpage %d       ",
					addr/0x200);
			}
		/* copy uploaded memory into place */
		len = (int) ntohs(pDlAck->dl_hdr.dl_len);
		if ( write(COREFD, pDlAck->dl_pt.dl_data, len) != len ) {
			perror(Openfiles[COREFD]);
			callexit(1);
		}
	}
	/* instruct eload to halt VAX */
	packet.dl_pt.dl_srt.dl_entry = 0; /* dummy value */
	packet.dl_pt.dl_srt.dl_delay = htonl(NOAUTOSTART);
	build_dlpack(DLSRT, 0, 0, &packet);
	for ( iNak = 0; iNak < ETHRETRY; iNak++ ) {
		send_ulpack(Sfd, &packet);
		pDlAck = getack();
		if (pDlAck != NULL && pDlAck->dl_hdr.dl_op == DLQAK)
			break;
	}
	/* don't complain if got no ACK */
	return(ETH_OK);
}

/*
 *=============================================================
 * build_dlpack - construct a packet in ether download format
 *=============================================================
 */
build_dlpack(op, datalen, addr, packet)
char	 op;
unsigned short	 datalen;
unsigned long	 addr;
struct	 dl_pack *packet;
{
	packet->dl_hdr.dl_op = op;
	packet->dl_hdr.dl_vers = VERSION;

	switch (op) {
	case DLEXT:
		packet->dl_hdr.dl_len = htons(datalen);
		packet->dl_hdr.dl_addr = htonl(addr);
		break;
	case DLID:			/* do nothing, don't complain */
	case DLSRT:
		break;
	default:
		fprintf(stderr, "build_dlpack: invalid output packet type\n");
		break;
	}
}

/*
 *============================================================
 * send_ulpack - send a downloader format packet out via UDP
 *============================================================
 */
send_ulpack(sockfd, dlpacket)
int sockfd;
struct dl_pack *dlpacket;
{
	unsigned short	len;
	unsigned long	addr;
	unsigned int	pktsize;

	switch (dlpacket->dl_hdr.dl_op) {
	case DLSRT:
		pktsize = sizeof(struct dl_header) +
					sizeof(struct dl_srtval);
		break;
	case DLEXT:
	case DLID:
	default:
		pktsize = sizeof(struct dl_header);
		break;
	}
	if (A.verbose) {
		fprintf(stderr,"packet:  ");
		switch(dlpacket->dl_hdr.dl_op) {

		case DLEXT:
			addr =  ntohl(dlpacket->dl_hdr.dl_addr);
			len  =  ntohs(dlpacket->dl_hdr.dl_len);
			fprintf(stderr, "extract, addr = %#x, len = %#x\n",
				addr, (int) len);
			break;
		case DLSRT:
			fprintf( stderr,
				"start program, delay = %d, entry pt = %x\n",
				ntohl(dlpacket->dl_pt.dl_srt.dl_delay),
				ntohl(dlpacket->dl_pt.dl_srt.dl_entry) );
			break;
		case DLID:
			fprintf(stderr, "ID packet\n");
			break;
		default:
			fprintf(stderr, "send_ulpack: invalid packet type\n");
			return;
		}
	}
	if (send(sockfd, dlpacket, pktsize, 0) < 0) {
		perror("udp packet send");
		callexit(1);
	}
}

/*====================================================
 * savestate - save initial registers before uploading
 *====================================================
*/
savestate()
{
	getcpu();
	message("getting registers...\n",0,0);
	getregs(Corehead.c_regs);
	getipl(&Corehead.c_psw);
	lseek(COREFD, 0, 0);
	write(COREFD, &Corehead, sizeof Corehead);
}

/*==========================================
 * start_eload - init vax and start up eload
 *==========================================
 * input  state: after the console prompt
 * output state: undefined (waiting for an ack from eload)
 * actions:
 *    set PC to address of eload
 *    disable interrupts
 *    start VAX
 */
start_eload()
{
	char	buf[32];

	savestate();
	sendcons(CPUINIT, TRUE);
	message("starting VAX-resident portion of loader...\n",0,0);
	setreg(KSPREG, ETH_INITSP);
	sprintf(buf, "S %x\r", ETH_INITPC);
	sendcons(buf, TRUE);
}

/*
 *============================================================
 * load_args - put arguments to ELOAD into the appropriate loc
 *============================================================
 */
load_args(ip_addr)
u_long	ip_addr;
{
	setmem(ETH_INITPC-4, ip_addr);	
}

/*
 *==================================================
 * sendcons - send a message to console half duplex
 *==================================================
 */
sendcons(msg, wait)
char *msg;
Bool wait;
{
	char buf[32], *ptr, tmpstr[2];

	if ( A.verbose ) {
		fprintf(stderr,"OUT: ");
		for ( ptr=msg ; *ptr!=EOS ; ptr++ )
			fprintf(stderr,"%s",unctrl(*ptr));
		fprintf(stderr,"\n");
	}
	while (*msg!=EOS) {
		if (islower(*msg))
			*msg = *msg - 32;
		write(DEVFD,msg,1);
                if ( wait ) {
			tmpstr[0] = *msg;
			tmpstr[1] = EOS;
                        readuntil(tmpstr, buf, 5, 32);
		}
		msg++;
	}
}

/*
 *================================================
 * setreg - preload a register through VAX console
 *================================================
 */
setreg(reg, value)
int reg, value;
{
	char buf[32];

	/* there's no machine-independent way to load PSL, so let	*/
	/* console Initialize command take care of that			*/
	if (reg == KSPREG)
		sprintf (buf, "D/I 0 %x\r", value);
        else if ( reg >= 0 ) /* general register */
                sprintf(buf,"D/G %x %x\r", reg, value);
	else /* internal privileged register -reg */
		sprintf(buf, "D/I %x %x\r", -reg, value);
	sendcons(buf, TRUE);
        if ( !A.silent && !A.verbose )
                displayreg(reg);
	getprompt (6);
}

/*
 *===================================
 * getprompt - get VAX console prompt 
 *===================================
*/
getprompt (time)
int time;
{
	int	i;
	char	buf[32];

	for (i=0; i<3; i++)
		readuntil (">", buf, time, 32);
}
	
/*
 *======================================================
 * getcpu - get VAX console's attention, initialize cpu
 *======================================================
 * Input state - undefined
 * Output state - immediately after VAX console prompt
 */
getcpu()
{
	/* get console, if it's a big VAX */
	write(DEVFD, CONSKILL, 1);	/* ^U erases current cmd. line	*/
	write(DEVFD, CONSABRT, 1);	/* ^C enables console input	*/
	write(DEVFD, CONSINTR, 1);	/* ^P breaks to console mode	*/
	usleep(100000);
	/* get console, if it's a UVAX */
	ioctl(DEVFD, TIOCSBRK, NULL);
	usleep(300000);
	ioctl(DEVFD, TIOCCBRK, NULL);
	/* now have console, initialize cpu */
	getprompt(15);
	sendcons(CPUHALT, TRUE);	/* halt the processor		*/
	sendcons("\r", TRUE);
	getprompt(6);
	sendcons(CPUNJAM, TRUE);	/* unjam bus			*/
	getprompt(15);			/* high waits for big VAXen	*/
}

/*
 *============================================================
 * setmem - load a 32-bit memory location through VAX console
 *============================================================
 */
setmem(loc, value)
int loc, value;
{
	char buf[128];

        sprintf(buf,"D/L/P %x %x\r",loc,value);
	sendcons(buf, TRUE);
	getprompt(6);
}