/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION  1986,1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:autoconf.c 12.1$ */
/* $ACIS:autoconf.c 12.1$ */
/* $Source: /ibm/acis/usr/sys/ca/RCS/autoconf.c,v $ */

#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid = "$Header:autoconf.c 12.1$";
#endif


#include <syslog.h>
#include "reg.h"
#include "pte.h"
#include "mmu.h"
#include "scr.h"
#include "debug.h"
#include "param.h"
#include "systm.h"
#include "dir.h"
#include "user.h"
#include "kernel.h"
#include "map.h"
#include "vm.h"
#include "proc.h"
#include "buf.h"
#include "reboot.h"
#include "conf.h"
#ifdef VFS
#include "vnode.h"
#include "../ufs/inode.h"
#else VFS
#include "inode.h"
#endif VFS
#include "file.h"
#include "text.h"
#include "clist.h"
#include "callout.h"
#include "cmap.h"
#include "mbuf.h"
#ifdef VFS
#ifdef QUOTA
#include "../ufs/quota.h"
#endif QUOTA
#else VFS
#include "../h/quota.h"
#endif VFS
#include "dk.h"
#include "io.h"
#include "../machineio/ioccvar.h"
 /* #include "dmap.h"		/* already included in user.h */
#include "cpu.h"
#ifdef ROMPC
#include "ioim.h"
#endif ROMPC

#ifdef ATR
#include "../ca_atr/pcif.h"
#include "vga.h"
#include "ibmvbarmmmdxiv.h"
#include "ms.h"
#if	NVGA > 0
#include "../machinecons/vgaio.h"
#endif
int sv_pc_cb;				/* PS/2 address */
/*
 * MMU lock bits to control access to io space in the 64k windows
 */
struct io_locks {
	char	write_bit;
	unsigned short lock_bits;
} io_locks[] = {
	/*  Read/Write access to 0x03b0-0x03df */
#if NVGA > 0
	{1,0x0500},		/* page 0  0x0000-0x07ff */
#else !VGA
#ifdef NIBMVBARMMMDXIV > 0
	{1,0x0400},		/* page 0  0x0000-0x07ff */
#else !IBM8514
	{0,0x0000},		/* page 0  0x0000-0x07ff */
#endif !IBM8514
#endif !VGA
#ifdef NIBMVBARMMMDXIV > 0
	{0,0x0404},		/* page 1  0x0800-0x0fff */
	{0,0x0404},		/* page 2  0x1000-0x17ff */
	{0,0x0404},		/* page 3  0x1800-0x1fff */
	{1,0x0400},		/* page 4  0x2000-0x27ff */
#else
	{0,0x0000},		/* page 1  0x0800-0x0fff */
	{0,0x0000},		/* page 2  0x1000-0x17ff */
	{0,0x0000},		/* page 3  0x1800-0x1fff */
	{0,0x0000},		/* page 4  0x2000-0x27ff */
#endif
	{0,0x0000},		/* page 5  0x2800-0x2fff */
	{0,0x0000},		/* page 6  0x3000-0x37ff */
	{0,0x0000},		/* page 7  0x3800-0x3fff */
#ifdef NIBMVBARMMMDXIV > 0
	{1,0x0404},		/* page 8  0x4000-0x47ff */
	{1,0x0404},		/* page 9  0x4800-0x4fff */
#else
	{0,0x0000},		/* page 8  0x4000-0x47ff */
	{0,0x0000},		/* page 9  0x4800-0x4fff */
#endif
	{0,0x0000},		/* page a  0x5000-0x57ff */
	{0,0x0000},		/* page b  0x5800-0x5fff */
	{0,0x0000},		/* page c  0x6000-0x67ff */
	{0,0x0000},		/* page d  0x6800-0x6fff */
	{0,0x0000},		/* page e  0x7000-0x77ff */
	{0,0x0000},		/* page f  0x7800-0x7fff */
#ifdef NIBMVBARMMMDXIV > 0
	{1,0x0404},		/* page 10 0x8000-0x87ff */
	{1,0x0404},		/* page 11 0x8800-0x8fff */
	{1,0x0404},		/* page 12 0x9000-0x97ff */
	{1,0x0404},		/* page 13 0x9800-0x9fff */
	{1,0x0404},		/* page 14 0xa000-0xa7ff */
	{1,0x0404},		/* page 15 0xa800-0xafff */
	{1,0x0404},		/* page 16 0xb000-0xb7ff */
	{1,0x0404},		/* page 17 0xb800-0xbfff */
	{1,0x0404},		/* page 18 0xc000-0xc7ff */
	{1,0x0404},		/* page 19 0xc800-0xcfff */
	{1,0x0404},		/* page 1a 0xd000-0xd7ff */
	{1,0x0404},		/* page 1b 0xd800-0xdfff */
	{1,0x0404},		/* page 1c 0xe000-0xe7ff */
	{1,0x0404},		/* page 1d 0xe800-0xefff */
	{1,0x0404},		/* page 1e 0xf000-0xf7ff */
	{1,0x0404}		/* page 1f 0xf800-0xffff */
#else
	{0,0x0000},		/* page 10 0x8000-0x87ff */
	{0,0x0000},		/* page 11 0x8800-0x8fff */
	{0,0x0000},		/* page 12 0x9000-0x97ff */
	{0,0x0000},		/* page 13 0x9800-0x9fff */
	{0,0x0000},		/* page 14 0xa000-0xa7ff */
	{0,0x0000},		/* page 15 0xa800-0xafff */
	{0,0x0000},		/* page 16 0xb000-0xb7ff */
	{0,0x0000},		/* page 17 0xb800-0xbfff */
	{0,0x0000},		/* page 18 0xc000-0xc7ff */
	{0,0x0000},		/* page 19 0xc800-0xcfff */
	{0,0x0000},		/* page 1a 0xd000-0xd7ff */
	{0,0x0000},		/* page 1b 0xd800-0xdfff */
	{0,0x0000},		/* page 1c 0xe000-0xe7ff */
	{0,0x0000},		/* page 1d 0xe800-0xefff */
	{0,0x0000},		/* page 1e 0xf000-0xf7ff */
	{0,0x0000}		/* page 1f 0xf800-0xffff */
#endif
};
extern int current_512_w; 		/* The active windows */
extern int current_128_w;
extern int pcif_io_b;

int cbcb_wait = 50000;
struct cbcb *cbcb;			/* Pointer to cbcb in PC memory */
char *pcvec_map;
struct pcif_reg *pcif_reg;		/* Pointer to pcif register set  */

ushort *pcdebugptr = (ushort *)0;
#endif ATR

/*
 * Setup the system to run on the current machine.
 *
 * Configure() is called at boot time and initializes the
 * device tables and the memory controller monitoring.  Available
 * devices are determined (from possibilities mentioned in ioconf.c),
 * and the drivers are initialized.
 *
 */

int cold = 1;				  /* if 1, still working on cold-start */
int dkn;				  /* number of iostat dk numbers assigned so far 
					  */
int nulldev();				  /* a null routine */
int int_irq;				  /* the irq level of an interrupt (autoconf) */
int int_level;				  /* the CPU level of an interrupt (autoconf) */
int (*int_rtn)() = nulldev;		/* int rtn for EARLY_INT driver being probed */
int int_ctlr;


configure()
{
	cold = 1;			  /* doing startup */
#ifdef DEBUG
	printf("rootdev=%x swapdev=%x argdev=%x dumpdev=%x\n", rootdev, swapdev, argdev, dumpdev);
#endif

	float_init();		/* init FPA if present */
#ifdef IBMRTPC
	dma_init();		/* init dma now, so it doesn't mess up lan */
#endif IBMRTPC

	iow(MMUBASE + MMU_SER, 0);	  /* clear the SER! */
	DEBUGF(iodebug, printf("clear SER done\n"));

	autoconf();
	if ((boothowto & RB_ASKNAME) == 0)
		setroot();
#if GENERIC
	setconf();
#endif
	swapconf();
	cold = 0;
#ifdef DEBUGF
	printf("configure end\n");
#endif	
}


/*
 * print out warning for stray interrupts.
 */

devunk(info, icscs, iar)
	register int info, icscs, iar;
{
	register int s = spl7();	  /* inhibit interrupts */

	if (cold) {
		int_level = s & 0x07;
		DEBUGF(autodebug & 0x01, printf("autoconf interrupt: level %x irq %d\n", int_level, int_irq));
	} else {
		log(LOG_WARNING, "Stray Interrupt! level=%d info=%x", s & 07, info);
		if (iodebug & 0x01)
			printf(" icscs=%x iar=%x", icscs, iar);
		printf("\n");
	}
	splx(s);			  /* return to previous interrupt level */
	return (0);			  /* last entry in list - must terminate search 
					  */
}


#ifdef IODEBUG

iotrap(info, icscs, iar, fn)
	register int info, icscs, iar;
	int (*fn)();
{
	register int n;

	DEBUGF(indebug, printf("call SLIH (prio=%x) at %x with %x %x %x\n",
	    mfsr(SCR_ICS) & 0x07, fn, info, icscs, iar));
	n = (*fn)(info, icscs, iar);
	DEBUGF(indebug, printf("SLIH returned %x\n", n));
	return (n);
}


#endif IODEBUG

#define MAX_8259_LEVELS 8		  /* max number of levels on a 8259 */
#define MAX_SLIH	24		  /* max number of SLIHs */
#define MAX_8259	2		  /* max number of 8259s */

/*
 * following is for "vmstat" display in systat(1)
 */
#define OTHER_INTS	2		/* other ints (clock+soft) */
long intrcnt[MAX_SLIH+OTHER_INTS];	/* the I/O counts (+OTHER) */
long eintrcnt;			/* the end of the counts */
#ifdef IBMRTPC
char intrnames[MAX_SLIH*6] = 
"clock\0soft\0DMA\0IRQ10\0IRQ9 \0IRQ3 \0IRQ4 \0KLS\0IRQ2 \0IRQ7 \0IRQ8 \0IRQ11\0IRQ14\0IRQ12\0IRQ6 \0IRQ5 \0IRQ15\0IRQ13";
#endif IBMRTPC
#ifdef ATR
char intrnames[MAX_SLIH*6] = 
"clock\0soft\0IRQ0 \0IRQ1 \0IRQ2 \0IRQ3 \0IRQ4 \0IRQ5 \0IRQ6 \0IRQ7 \0IRQ8 \0IRQ9 \0IRQ10\0IRQ11\0IRQ12\0IRQ13\0IRQ14\0IRQ15";
#endif ATR
char eintrnames[] = "";
/* end of additions for vmstat display of systat(1) */

int devunk();
#ifdef IBMRTPC
int dma_int();
#endif IBMRTPC
int klsint();

struct slihtab slih_table[MAX_SLIH] =
{
/* rtn info irq flags */
#ifdef ATR		
    0, 0, 0, 0, 0,		/* 0 IRQ 0   */
    klsint, 0, 0, 1, FIXED,	/* 1 IRQ 1   */
    0, 0, 0, 2, 0,		/* 2 IRQ 2   */
    0, 0, 0, 3, 0,		/* 3 IRQ 3   */
    0, 0, 0, 4, 0,		/* 4 IRQ 4   */
    0, 0, 0, 5, 0,		/* 5 IRQ 5   */
    0, 0, 0, 6, 0,		/* 6 IRQ 6   */
    0, 0, 0, 7, 0,		/* 7 IRQ 7   */

    0, 0, 0, 8, 0,		/* 8 IRQ 8   */
    0, 0, 0, 9, 0,		/* 9 IRQ 9   */
    0, 0, 0, 10, 0,		/* 10 IRQ 10 */
    0, 0, 0, 11, 0,		/* 11 IRQ 11 */
    0, 0, 0, 12, 0,		/* 12 IRQ 12 */
    0, 0, 0, 13, 0,		/* 13 IRQ 13 */
    0, 0, 0, 14, 0,		/* 14 IRQ 14 */
    0, 0, 0, 15, 0,		/* 15 IRQ 15 */
#endif ATR
#ifdef IBMRTPC
/*	rtn	info irq flags */
	dma_int, 0, 0, 0, FIXED,	  /* 0 (Planar) 8237 terminal count */
	0, 0, 0, 10, 0,			  /* 1 IRQ 10 */
	0, 0, 0, 9, 0,			  /* 2 IRQ 9 */
	0, 0, 0, 3, 0,			  /* 3 IRQ 3 */
	0, 0, 0, 4, 0,			  /* 4 IRQ 4 */
	klsint, 0, 0, 1, FIXED,		  /* 5 (planar) keyboard */
	0, 0, 0, 2, 0,			  /* 6 8530 serial port */
	0, 0, 0, 7, 0,			  /* 7 IO BUS IRQ 7 */

	0, 0, 0, 8, 0,			  /* 8 (planar) reserved */
	0, 0, 0, 11, 0,			  /* 9 IRQ 11 */
	0, 0, 0, 14, 0,			  /* 10 IRQ 14 */
	0, 0, 0, 12, 0,			  /* 11 IRQ 12 */
	0, 0, 0, 6, 0,			  /* 12 IRQ 6 */
	0, 0, 0, 5, 0,			  /* 13 IRQ 5 */
	0, 0, 0, 15, 0,			  /* 14 IRQ 15 */
	0, 0, 0, 13, 0,			  /* 15 (planar) serial port */
#endif IBMRTPC
};

/*
 * last_slih indicates where there is spare space for duplicate
 * (chain) entries.
 */
struct slihtab *last_slih = slih_table + (MAX_8259_LEVELS * MAX_8259);

/*
 * in "int_table" is passed to the 8259 interrupt controller service
 * routine for level 3 and level 4 interrupts.
 * it specifies the address of the 8259 and the SLIH table for the
 * 8 8259 levels. Each level is a linked list of driver interrupt service
 * routines that are called in turn. when one claims an interrupt the
 * list is terminated.
 * if none claim the interrupt then 'devunk' will be called.
 */


#define MAX_IRQ 16
char irq_map[MAX_IRQ];

char shr_dummy;
#ifdef IBMRTPC
char volatile * shr_int_reset[MAX_IRQ] = {
	&shr_dummy,		&shr_dummy,		&shr_dummy,
	(char *)0xf00002f3,	(char *)0xf00002f4,	(char *)0xf00002f5,
	(char *)0xf00002f6,	(char *)0xf00002f7,	&shr_dummy,
	(char *)0xf00002f2,	(char *)0xf00006f2,	(char *)0xf00006f3,
	(char *)0xf00006f4,	&shr_dummy,		(char *)0xf00006f6,
	(char *)(char *)0xf00006f7
};
#endif IBMRTPC

#ifdef ATR
char volatile * shr_int_reset[MAX_IRQ] = {
	&shr_dummy,		&shr_dummy,		&shr_dummy,
	&shr_dummy,		&shr_dummy,		&shr_dummy,
	&shr_dummy,		&shr_dummy,		&shr_dummy,
	&shr_dummy,		&shr_dummy,		&shr_dummy,
	&shr_dummy,		&shr_dummy,		&shr_dummy,
	&shr_dummy
};

#endif ATR


struct int_table int3table =
{
#ifdef IBMRTPC
	(char *)Adr_8259A,		  /* the first 8259A */
#endif IBMRTPC
#ifdef ATR
	(char *)0,
#endif ATR
	slih_table
};

struct int_table int4table = {
#ifdef IBMRTPC
	(char *)Adr_8259B,		  /* the second 8259A */
#endif IBMRTPC
#ifdef ATR
	(char *)0,
#endif ATR
	slih_table + MAX_8259_LEVELS
};

int ign_devunk = 0;		/* can be patched to ignore some devunks */

#ifdef ATR
#define INT8259RETURN(value,p,r) {int8259return(p,r,eoipending); return(value);}

/*
 * handle end of interrupt processing:
 * 1. send an EOI to the pc if it is expecting one
 * 2. clear shared memory location in cbcb that indicates we
 *	are busy processing an interrupt.
 * 3. clear the interrupt request bit in the pcif.
 */
int8259return(p,r,eoipending)
char *p;
int r;
int eoipending;
{
	set_512_window (cbcb_addr);	/* point to the cbcb */
	if (eoipending&QEOIPENDING)
		pc_req(r == R_INT3 ? CB_EOI3 : CB_EOI4, (char *) ((int) *p & 0x07), SVENT);
	*p = 0;				/* clear cbcb->rompint3 or rompint4 */
	RSETBIT(&pcif_reg->romp_intr,r);
}

#endif ATR

#ifdef IBMRTPC
#define INT8259RETURN(v,p,r) return(v)
#endif IBMRTPC
/*
 * int_8259 services an interrupt generated by an 8259A.
 * it determines which 8259 line caused the interrupt and then
 * calls the appropriate SLIH to service it.
 * if that SLIH does not claim the interrupt then the next SLIH
 * in the chain is called until either there are no more left or
 * one claims it.
 * the SLIH is called with the info field from the table which is
 * the controller number from autoconfig.
 */


int_8259(table, icscs, iar)
	register struct int_table *table;
	register int iar, icscs;
{
	register char volatile *adr_8259 = table->addr_8259;
	register struct slihtab *s;
	register int i;
	register int irq;

#ifdef ATR
	register int j;
	register int pcifreset;
	int eoipending;
/*
 * The SLIH for the ATR version of the system looks into the cbcb
 * to figure out what hardware interrupt occured. The PC code 
 * writes values into the cbcb in the same manner the 8259
 * chip would respond in poll mode .
 */

	set_512_window (cbcb_addr);	/* point to the cbcb */
#endif ATR



	cnt.v_intr++;		/* count device interrupts */

#ifdef IBMRTPC
	DEBUGF(iodebug & 0x80, {
		register int isr;
 
		*adr_8259 = GET_ISR;
		isr = *adr_8259;	  /* read the ISR */
		*adr_8259 = GET_IRR;
		i = *adr_8259;		  /* read the IRR */
		printf("8259 @ %x ISR=%x IRR=%x IMR=%x\n",
		    adr_8259, isr, i, *(adr_8259 + 1));
	}
	);

	*adr_8259 = POLL_CMD;
/*	DELAY(1);			  /* just in case */
	i = *adr_8259;			  /* pick up the active device */
	DEBUGF(iodebug & 0x20, printf("8259 @ %x POLL=%x\n", adr_8259, i));
#endif IBMRTPC

#ifdef ATR
/*
 * Interrupts on the first 8259 are passed to us via romp level 3, 
 * while interupts on the second 8259 come by way of romp level 4.
 * For either case we need to look at the cbcb to figure out what
 * IRQ level has occured. 
 */

	if ((table == &int3table)) {
	    adr_8259 = (char *)&cbcb->romp_int3;
	    pcifreset = R_INT3; 
	}
	else {
	    adr_8259 = (char *)&cbcb->romp_int4;
	    pcifreset = R_INT4; 
	}

	j = 0;
	while(((i = *adr_8259) == 0) && (j++ < 10)); 	/* XXXX GRL */
	DEBUGF(iodebug & 0x20, printf("8259 POLL=%x\n",i));
	/*
	 * Interrupts fowarded from the PS/2 can be from hardware
	 * (PS/2 needs to send an EOI, see rompint.c) or from software
	 * (PS/2 does not need to send an EOI).
	 *
	 * When devices are at the same IRQ level it is possible for
	 * one device to take anothers interrupt. Ordinarily this would
	 * be OK since the next interrupt would go the the next device.
	 * However when a driver that gets an interrupt directly from
	 * hardware take a interrupt from a software generated driver,
	 * (or the inverse) we have problems.
	 *
	 * To get around this the PS/2 code sets a bit (EOIPENDING) in
	 * the cbcb->rompint? field to tell us if this interrupt is
	 * software generated or hardware generated. We then set a flag
	 * that each driver looks at. If that driver expects only software
	 * interupts then he does not service the request if the flag is set.
	 *
	 */
	eoipending = i & (EOIPENDING | QEOIPENDING);
#endif ATR



	if ((i & 0x80) == 0) {
		if (ign_devunk) 
			INT8259RETURN(0,adr_8259,pcifreset);

		printf("8259 @ %x (%x): ", adr_8259, i);
		i = devunk((int)table, iar, icscs);
		INT8259RETURN(i,adr_8259,pcifreset); /* complain */
	}

#ifdef IBMRTPC
	*adr_8259 = SEOI_CMD + (i& 0x7);  /* specific EOI */
#endif IBMRTPC

	s = table->slihtab + (i & 0x7);	  /* get the appropriate slih table */
	irq = s->slih_irq;		  /* get its IRQ number */
	if (cold) {
		register int called_int_rtn = 0;

		for (;;) {
			DEBUGF(iodebug & 0x10,
				printf("8259 @ %x level %x slih=%x info=%x irq=%x\n",
					adr_8259, i, s->slih_rtn, s->slih_info, irq));
			if (s->slih_rtn == 0)
				break;
			if (s->slih_rtn == int_rtn)
				called_int_rtn = 1;
			if ((*s->slih_rtn)(s->slih_info, irq, i) == 0 ||
			    (s = s->slih_next) == 0)
				break;
		}
		if (s && s->slih_rtn) {	/* interrupt accepted by a driver? */
			INT8259RETURN(0,adr_8259,pcifreset);
		} else if (int_rtn != nulldev) {	/* EARLY_INT set? */
			if (!called_int_rtn)
				(void)(*int_rtn)(int_ctlr, irq, i); /* it'll clear int */
			*shr_int_reset[irq] = 0xff;
		} /* else driver's probe rtn clears int, & does share int
		   * reset too if it is needed */
		if (int_irq != -1 && int_irq != irq)
			printf("autoconf: help - interrupt at irq %d and irq %d!\n",
			    int_irq, irq);
		int_irq = irq;
	} else {	/* keep this path optimal! */
		for (;;) {
			DEBUGF(iodebug & 0x10,
				printf("8259 @ %x level %x slih=%x info=%x irq=%x\n",
					adr_8259, i, s->slih_rtn, s->slih_info, irq));
			if (s->slih_rtn == 0) {
				*shr_int_reset[irq] = 0xff;
				break;
			}
			if ((*s->slih_rtn)(s->slih_info, irq, i) == 0) {
				*shr_int_reset[irq] = 0xff;
				intrcnt[s-slih_table+OTHER_INTS]++;  /* count interrupts */
				INT8259RETURN(0,adr_8259,pcifreset);
			}
			if ((s = s->slih_next) == 0) {
				*shr_int_reset[irq] = 0xff;
				break;
			}
		}
		log(LOG_WARNING, "8259 (%x) IRQ %d: %s ", i, irq, s ? "no SLIH" : "unclaimed");
	}
	i = devunk((int)table, iar, icscs);
	INT8259RETURN(i,adr_8259,pcifreset);
}




/* following table is MAX_SLIH long on the assumption that if that limit
 * is reasonable for slih_table it is also reasonable for the number
 * of device addresses.
 */
caddr_t probe_addrs[MAX_SLIH];		  /* table to remember used device addresses */
int trap();



/* following table is MAX_SLIH long on the assumption that if that limit
 * is reasonable for slih_table it is also reasonable for the number
 * of device addresses.
 */
caddr_t probe_addrs[MAX_SLIH];		  /* table to remember used device addresses */
int trap();
int (*_trap)();
int autotrap();

autoconf()
{
/*
 * go thru the iocc_device structure and configure each existing device
 * into the system.
 */
	register struct iocc_ctlr *ic;
	register struct iocc_driver *id, *idr;
	register struct iocc_device *iod;
	register caddr_t * addrp;
	register caddr_t addr;		  /* address of adapter register */
	extern struct iocc_ctlr iocccinit[];
	extern struct iocc_device ioccdinit[];
	int s;

	_trap = autotrap;		  /* intercept traps due to autoconfig */
	printf("autoconf");
	s = spl1();			  /* allow interrupts (but keep as system ) */
	klswait();
	printf("\n");
	dkn = 0;
	init_slih();			  /* initialize slih_table */
/*
 * scan thru the data structures resetting values
 * in case this is a restart of the kernel while in
 * memory.
 */
	for (ic = iocccinit; id = ic->ic_driver; ++ic) {
		ic->ic_alive = 0;
	}
	for (iod = ioccdinit; id = iod->iod_driver; ++iod) {
		iod->iod_alive = 0;
		iod->iod_forw = 0;
		iod->iod_mi = 0;
	}
/*
 * loop thru the controller structures
 * for each on which is potentially present check to see if it is
 * realy there.
 * if no address is given then check the standard list given in
 * the driver.
 */
	for (ic = iocccinit; id = ic->ic_driver; ++ic) {
		if (id->idr_flags & EARLY_INT) {
			int_rtn = id->idr_intr;	/* enable early interrupt(s) */
			int_ctlr = ic->ic_ctlr;
		} else
			int_rtn = nulldev;
		addr = ic->ic_addr;	  /* address from ctlr structure */
		for (addrp = id->idr_addr; addr || (addrp && (addr = *addrp++)); addr = 0)
			if (ctlr_probe(ic, addr, id))
				break;
		if (addr == 0)
			continue;	  /* this one is not here */
		id->idr_minfo[ic->ic_ctlr] = ic; /* remember controler */
		ic->ic_alive++;			 /* remember it's alive */
/*
 * loop thru the device structures looking for mass storage
 * peripherals attached to this adapter.
 */
		for (iod = ioccdinit; idr = iod->iod_driver; ++iod) {
			if (id != idr || iod->iod_alive ||
			    (ic->ic_ctlr != iod->iod_ctlr && iod->iod_ctlr != '?'))
				continue; /* not us */
			if (slave(id, iod, addr)) {
				iod->iod_alive = 1;
				iod->iod_ctlr = ic->ic_ctlr;
				iod->iod_addr = addr;
				if (iod->iod_dk && dkn < DK_NDRIVE)
					iod->iod_dk = dkn++;
				else
					iod->iod_dk = -1;
				iod->iod_mi = ic;
				printf("%s%d at %s%d slave %d\n",
				    id->idr_dname, iod->iod_unit,
				    id->idr_mname, ic->ic_ctlr, iod->iod_slave);
				attach(iod, id);
			}
		}
	}
/*
 * now look for non-mass storage peripherals.
 */
	for (iod = ioccdinit; id = iod->iod_driver; ++iod) {
		if (id->idr_flags & EARLY_INT) {
			int_rtn = id->idr_intr;	/* enable early interrupt(s) */
			int_ctlr = iod->iod_unit;
		} else
			int_rtn = nulldev;
		if (iod->iod_alive || iod->iod_slave != -1)
			continue;	  /* already done, or not present */
		addr = iod->iod_addr;	  /* address from device structure */
		for (addrp = id->idr_addr; addr || (addrp && (addr = *addrp++)); addr = 0) {
			if (!device_probe(iod, addr, id))
				continue;
			iod->iod_alive = 1;
			iod->iod_addr = addr;
			attach(iod, id);
			break;
		}
	}
	int_rtn = nulldev;
	splx(s);			  /* restore interrupt state */
	_trap = trap;
	DEBUGF(autodebug, printf("autoconf end\n"));
}


ctlr_probe(ic, addr, id)
	register struct iocc_ctlr *ic;
	register caddr_t addr;
	register struct iocc_driver *id;
{
	register int result;
	DEBUGF(autodebug, printf("testing controller %s at %x\n", id->idr_dname, addr));
	if (present(addr + id->idr_csr) && (result = probe(addr, id, (caddr_t) ic)) != PROBE_BAD) {
		printf("%s%d adapter %x ", id->idr_mname, ic->ic_ctlr, addr);
		if (result == PROBE_BAD_INT) {
			printf("didn't interrupt\n");
			return (PROBE_BAD);
		}
		ic->ic_addr = addr;
		if (result == PROBE_OK)
			ic->ic_irq = int_irq; /* use actual */
		set_vector(ic->ic_irq, id->idr_intr, ic->ic_ctlr, id->idr_mname, ic->ic_ctlr); /* set up IRQ */
		printf("\n");
		return (PROBE_OK);
	}
	return (PROBE_BAD);
}


device_probe(iod, addr, id)
	register struct iocc_device *iod;
	register caddr_t addr;
	register struct iocc_driver *id;
{
	register int result;

	DEBUGF(autodebug, printf("testing device %s at %x\n", id->idr_dname, addr));
	if (present(addr + id->idr_csr) && (result = probe(addr, id, (caddr_t)id)) != PROBE_BAD) {
		DEBUGF(autodebug, printf("device %s found at %x\n", id->idr_dname, addr));
		printf("%s%d adapter %x ", id->idr_dname, iod->iod_unit, addr);
		if (result == PROBE_BAD_INT) {
			printf("didn't interrupt\n");
			return (PROBE_BAD);
		}
		iod->iod_alive = 1;
		if (result == PROBE_OK)
			iod->iod_irq = int_irq;	/* use actual interrupt level */
		set_vector(iod->iod_irq, id->idr_intr, iod->iod_unit, id->idr_dname, iod->iod_unit); /* set up IRQ */
		printf("\n");
		return (1);
	} else {
		DEBUGF(autodebug, printf("device %s not found at %x\n", id->idr_dname, addr));
		return (0);
	}
}

#define PROBE_NULL	0xffffffff

/*
 * first check to see if the address is already used - if so then don't
 * regard it as present.
 * if probe succeedes then enter it in used address table.
 */
probe(addr, id, ic)
	register struct iocc_driver *id;
	register caddr_t addr;
	register caddr_t ic;
{
	register int result;
	register caddr_t * t;

	int_irq = -1;			  /* set up for interrupt determination */
	int_level = -1;
	for (t = probe_addrs; *t; ++t)
		if (*t == addr && addr != (caddr_t) PROBE_NULL)
			return (PROBE_BAD); /* oops, already in use */
	if (id->idr_probe == 0) {
		DEBUGF(autodebug, printf("no probe routine - ignored\n"));
		return (PROBE_BAD);	  /* assume that it exist if no probe rtn */
	}
	DEBUGF(autodebug, printf("calling probe routine at %x(%x)\n", id->idr_probe, addr));
	result = (*id->idr_probe)(addr,ic);
	switch (result) {
	case PROBE_OK:			  /* device thinks it caused an interrupt */
		if (int_irq == -1 || int_level == -1) {
			result = PROBE_BAD_INT;	/* too bad */
			break;
		}
/* fall thru */
	case PROBE_NOINT:
		*t = addr;	  /* device found - remember it's address */
		break;
	default:
		result = PROBE_BAD;
	case PROBE_BAD:
		break;
	}
	return (result);
}


slave(id, iod, addr)
	register struct iocc_driver *id;
	register struct iocc_device *iod;
	register int addr;
{
	register int result;
	if (id->idr_slave == 0) {
		DEBUGF(autodebug, printf("%s%d: no slave routine present - ignored\n",
		    id->idr_dname, iod->iod_unit));
		return (0);		  /* not there if no slave routine */
	}
	DEBUGF(autodebug, printf("calling slave at %x (%x,%x)\n", id->idr_slave, iod, addr));
	result = (*id->idr_slave)(iod, addr);
	return (result);
}


attach(iod, id)
	register struct iocc_device *iod;
	register struct iocc_driver *id;
{
	register int result;

	if (id->idr_dinfo)
		id->idr_dinfo[iod->iod_unit] = iod; /* link together */
	else
		printf("%s: dinfo pointer is null\n", id->idr_dname);
	if (id->idr_attach == 0) {
		printf("%s: no attach routine - ignored\n", id->idr_dname);
		return (0);		  /* assume that it doesn't exist if no attach
					     rtn */
	}
	DEBUGF(autodebug, printf("calling attach at %x(%x)\n", id->idr_attach, iod));
	result = (*id->idr_attach)(iod);
	return (result);
}


/*
 * store new interrupt service routine into slih_table.
 * scan thru slih_table until we find an entry with no slih_rtn
 * specified (better be the first one!)
 * or one with slih_next == NULL.
 */
set_vector(irq, rtn, info, name, unit)
	int (*rtn)();
	char *name;
{
	register struct slihtab *s = &slih_table[irq_map[irq]], *p = 0;

	DEBUGF(autodebug, printf("set interrupt (IRQ %d) to %x info %x\n", irq, rtn, info));
	printf("IRQ %d ", irq);
	if (rtn == 0) {
		printf("null interrupt service routine for irq %d\n", irq);
		rtn = nulldev;
	}
	if (int_level >= 0)
		printf("CPU level %d ", int_level);
	for (; s && s->slih_rtn; s = s->slih_next)
		p = s;
	if (s == 0) {
		s = last_slih++;	  /* allocate new slih entry */
		s->slih_irq = irq;	  /* remember the irq number */
		p->slih_next = s;	  /* link it into the chain */
	}
	s->slih_rtn = rtn;
	s->slih_info = info;
	set_int_name(s, name, unit);
	int_irq = -1;			  /* reset the interrupt info */
	int_level = -1;
}

/*
 * locate the proper place in interrupt name table 
 * and store controller name and unit number.
 */
set_int_name(s, name, unit)
char *name;
struct slihtab *s;
{
	int index = s - slih_table + OTHER_INTS;
	char *p;

	for (p=intrnames; --index >= 0; )
		p += strlen(p) + 1;
	/*
	 * p now points to the proper place for this entry
	 * we may need to add some blanks at the end of the table
	 */
	if (*p == 0)
		strcpy(p, "      ");	/* put in some blanks */
	while (*p && *name)
		*p++ = *name++;		/* copy the name */
	if (*p)
		*p++ = unit + '0';
	while (*p)
		*p++ = ' ';
}


/*
 * we assume that a non-existant location will always return the same
 * value. We also assume that UNDEFINED_ADDR is just such an undefined
 * location. It appears that usually 0xff is returned but we have seen
 * cases where 0x1f came back.
 * the following should even work if the value returned is zero but in
 * that case we will write a non-zero (0xff) value into the register
 * to see if we get a zero back out again. If all this fails it is up
 * to the driver probe routine to figure out if the device is there.
 */
present(addr)
	register char volatile *addr;
{
#ifdef IBMRTPC
#define UNDEFINED_ADDR	0xf0000000	  /* assume nothing at this address */
	register char *p = (char *)UNDEFINED_ADDR;

	addr[0] = 0;
	if (addr[0] == *p && addr[1] == *p) {
		addr[0] = ~*p;		  /* invert it just in case */
		return (addr[0] != *p);
	}
#endif IBMRTPC
#ifdef ATR
#ifdef AT
#define MIN_IO	0x0000			/* minimum address we can look at */
#define UNDEFINED_ADDR	0x00ff		/* assume nothing at this address */
#else
#define UNDEFINED_ADDR	0x0000		/* assume nothing at this address */
#define MIN_IO	0x0100			/* minimum address we can look at */
#endif
#define MAX_IO	0x10000
	/*
	 * if the address given looks like an I/O address then
	 * we test to see if anything is present at that address
	 */
	if ((int) addr >= MIN_IO && (int) addr < MAX_IO) {
		register int undef = IN(UNDEFINED_ADDR);
		if (config.cpu != 0)
			return(0);	/* give real hardware to master only */
		OUT(addr, 0);		/* set it to zero */
		if (IN(addr) == undef) {
			OUT(addr, ~undef);	/* invert it just in case */
			return(IN(addr) != undef);
		}
	}
#endif ATR
	return (1);
}


init_slih()
{
/*
 * scan thru slih_table and build the reverse mapping from IRQ number
 * to index into slih_tab.
 */
	register struct slihtab *s;
	register int i;

	for (i = 0, s = slih_table; i < MAX_8259_LEVELS * MAX_8259; ++i, ++s) {
		if ((s->slih_flags & FIXED) == 0) {
			s->slih_rtn = 0;  /* reset the pointer */
			s->slih_info = 0;
		}
		irq_map[s->slih_irq] = i; /* remember the mapping */
	}
	last_slih = s;			  /* first available spot */
	for (; i < MAX_SLIH; ++i, ++s) {
		s->slih_rtn = 0;
		s->slih_next = 0;
	}
}


autotrap(mcs_pcs, info, ics_cs, regs)
        register mcs_pcs, info;
        int ics_cs;			  /* must not be a register variable */
        int regs;			  /* must not be a register variable */
{

	prstate("trap", mcs_pcs, info, (&regs)[IAR], ics_cs, &regs);
	panic("trap during autoconfig");
}


/*
 * Configure swap space and related parameters.
 */

swapconf()
{
	register struct swdevt *swp;
	register int nswap = 0;
	extern unsigned int maxdsize;

	for (swp = swdevt; swp->sw_dev; swp++) {
		if (bdevsw[major(swp->sw_dev)].d_psize)
			swp->sw_nblks =
			    (*bdevsw[major(swp->sw_dev)].d_psize)(swp->sw_dev);
		if (swp->sw_nblks > 0)
			nswap += swp->sw_nblks;
	}
	if (nswap <= 0)
		panic("swap blocks <= 0");
	/*
	 * adjust max data size to account for swap space limitations
	 * we assume all potential swap devices are used.
	 */
	if (nswap * DEV_BSIZE < maxdsize)
		maxdsize = (nswap * DEV_BSIZE) / NBPG * NBPG;
	if (!cold)			  /* in case called for mba device */
		return;
	if (dumplo == 0 && bdevsw[major(dumpdev)].d_psize)
		dumplo = (*bdevsw[major(dumpdev)].d_psize)(dumpdev) - endmem * NBPG / DEV_BSIZE;
	if (dumplo < 0)
		dumplo = 0;
}

init_intr()
{
#ifdef ATR
	register int i;
	register int rctrl;
	int	frame,vpage;
	struct pte tmp_pte;

	struct	hatipt_entry	*ipte;
	struct	io_locks *io;

	/*
	 * map the pcif into MMU_IO_SEG (segment D)
	 */
	frame = btoc(pcif_addr);
	vpage = btop(MMU_IO_SEG << 28);

	/*
	 * map in the all of pcif NOTE the kernel may have the "key"
	 * bit on, so we have to pretend the pages are user read/write.
	 * user access is controlled by the "cpu" bit (no access at all
	 * or the "special" bit (making the lock bits active))
	 */
	for (ipte = &MMU_HATIPT[frame], i=0; i < btoc(PCIF_SIZE); ipte++,i++) {
		/* fix up the ipte to make map in happy */
		ipte->key_addrtag = (MMU_SID_IO) << MMU_VPAGE_BITS;
		ipte->ipt_ptr = MMU_UNDEF_PTR;
#ifdef SIM_CHG_BIT
		mapin(&tmp_pte, vpage++, PG_UW|PG_M|frame++, 1);
#else
		mapin(&tmp_pte, vpage++, PG_UW|frame++, 1);
#endif SIM_CHG_BIT
	}

	/*
	 * now set up the lockbits. The 512K window is no access,
	 * the 128k windows are full access, and the 64K windows
	 * are limited access.
	 */
	frame = btoc(pcif_addr);
	for (ipte = &MMU_HATIPT[frame], i=0; i < btoc(PCIF_512K_SIZE); 
								ipte++,i++) {
		ipte->w = 0;
		ipte->lockbits = 0;
	}
	/*
	 * 128k windows
	 */
	for (i=0; i < btoc(PCIF_128K_SIZE*PCIF_NUMBER_128K); ipte++,i++) {
		ipte->w = 1;
		ipte->lockbits = -1;
	}
	/*
	 * first 64k window (half word access), io_locks is the desired access
	 */
	for (i=0,io=io_locks; i < btoc(PCIF_64K_SIZE); ipte++,i++,io++) {
		ipte->w = io->write_bit;
		ipte->lockbits = io->lock_bits;
	}
	/*
	 * first 64k window (byte access), io_locks is the desired access
	 * NOTE: the access is identical to the half word window.
	 */
	for (i=0,io=io_locks; i < btoc(PCIF_64K_SIZE); ipte++,i++,io++) {
		ipte->w = io->write_bit;
		ipte->lockbits = io->lock_bits;
	}

	/*
	 * initialize the pcif window pointers
	 */
	pcif_addr = MMU_IO_SEG << 28;	/* the virtual address of the pcif */
	pcif_512_fw = pcif_addr;
	pcif_128_fw = pcif_512_fw + PCIF_512K_SIZE;
	pcif_128_hw = pcif_128_fw + PCIF_128K_SIZE;
	pcif_128_b = pcif_128_hw + PCIF_128K_SIZE;
	pcif_io_hw = pcif_128_b + PCIF_128K_SIZE;
	pcif_io_b =  pcif_io_hw + PCIF_64K_SIZE;
	u.u_pcb.pcb_io = MMU_IO_DISABLE; /* make sure the IO seg is defined */
	iow(MMU_TID_ADDR,u.u_pcb.pcb_io);

	pcif_reg = (struct pcif_reg *)(pcif_base + pcif_io_b); 
	rctrl = (int)&pcif_reg->romp_ctrl;

#if NVGA > 0
	/*
	 * If the VGA is enabled, the user may get access to the 128K window.
	 * Be sure this window is initialized to a VGA address (not just 
	 * random memory).
	 */
	set_128_window(VGA_SCREEN_MIN);
	u.u_pcb.pcb_win = current_128_w;
#endif NVGA

/* 
 * Next we compute the address of the cbcb (in the PC's memory)
 * and set up a pointer to it.
 */

	cbcb = (struct cbcb *)(set_512_window(cbcb_addr)+pcif_512_fw);
	sv_pc_cb =  cbcb->cbcb_ent[SVENT].pc_cb;
	pcvec_map = (char *)(set_512_window(sv_pc_cb) + pcif_512_fw);
#ifdef DEBUG
	pcdebugptr = (ushort *)&cbcb->debug;
#endif DEBUG

	SETBIT(rctrl,R_IENAB);			/* Enable ROMP to PC interrupts */
	pc_req(CB_QEOI, (char *) 0, SVENT);	/* we are queing EOI's */
#endif ATR

#ifdef IBMRTPC
	INIT_INTR();		/* setup 8259 interrupt controllers */
	shr_int_init();		/* reset shared interrupts */

	* (char *) CCR = u.u_pcb.pcb_ccr = ccr_default;	/* set up default user access */
#endif IBMRTPC

#ifdef ROMPC
#ifdef SGP
#ifdef MOD135
	if (cpu_model == CPU_ROMPC || cpu_model == CPU_MOD135)
#else  MOD135
	if (cpu_model == CPU_ROMPC)
#endif MOD135
#endif
		init_ioims(cpu_model);
#endif ROMPC
}

#ifdef ROMPC

int ioim1_config;		/* IOIM 1 configuration */
int ioim2_config;		/* IOIM 2 configuration */
int ioim2;			/* non-zero if IOIM2 present */
label_t ioim_label;

#define MEM_2_CYCLE 0x80	/* On if 2 Cycle Memory */
/*
 * initialize both IOIM1 and IOIM2 (IOIM2 requires complete setup
 * IOIM1 has already been initialized by the ROS)
 */
init_ioims(cpu_model)
int cpu_model;
{
#ifdef IBMRTPC
	extern int memconfig;	/* memory configuration register (locore) */
	int mem_cycle = (memconfig & MEM_2_CYCLE) != 0 ? 0 : IOIM_2_CYCLE;

	iow(IOIM1_BASE, (BUS1 << 8) + (IOIM1 >> 16));
	ioim1_config = ior(IOIM1 + IOIM_MCC);	/* get configuration */
	/* for now disable parity (chips don't work at 100ns) */
	iow(IOIM1+IOIM_GCR, ior(IOIM1+IOIM_GCR) | IOIM_NOPARITY | mem_cycle);
	/* Turn off user access to FPA's, but leave IOCC controlled alone */
	iow(IOIM1+IOIM_SUB_PROB, SEG_0+SEG_4);
#endif IBMRTPC

#ifdef MOD135
	if (cpu_model != CPU_MOD135)
#endif MOD135
	{
		extern int (*_trap)();
		extern int ioim_trap();
		register int (*oldtrap)() = _trap;
		_trap = ioim_trap;
		if (setjmp(&ioim_label)) {
			_trap = oldtrap;
			return;
		}
		/* initialize IOIM2 for 68881 operation */
		iow(IOIM2_BASE, (BUS2 << 8) + (IOIM2 >> 16));
		ioim2_config = ior(IOIM2 + IOIM_MCC);	/* get configuration */
		iow(IOIM2+IOIM_MCC, IOIM2_MCC);		/* set TAG and ARB */
		iow(IOIM2+IOIM_GCR, IOIM_NOPARITY | IOIM_LOOPMODE);
		iow(IOIM2+IOIM_MISC_0, IOIM2_MISC_0);	/* XXX obsolete hardware */
		iow(IOIM2+IOIM_MISC_1, IOIM2_MISC_1);
		iow(IOIM2+IOIM_SUB_SEG_1, IOIM2_VALUE1);
		iow(IOIM2+IOIM_SUB_PROB, IOIM2_PROB_DISABLE);	/* */
/*		iow(IOIM2+IOIM_SUB_PROB, IOIM2_PROB_ENABLE);	/* for testing */
		iow(IOIM2+IOIM_SUB_SEG_3, IOIM2_VALUE3);
		iow(IOIM2+IOIM_SUB_SEG_4, IOIM2_VALUE4);
		iow(IOIM2+IOIM_SUB_SEG_5, IOIM2_VALUE5);
		_trap = oldtrap;
		ioim2 = IOIM2;
	}
}

ioim_trap()
{
	longjmp(&ioim_label);
}

check_mem_config()
{
#ifndef ATR
	extern int memconfig;	/* memory configuration register (locore) */
	int mem_cycle = (memconfig & MEM_2_CYCLE);
	int x;
#ifdef SGP
	if (cpu_model == CPU_ROMPC)
#endif
		{
		if (mem_cycle) {
			printf("Warning: slow memory cards are not supported on APC\n");
			printf("	machine checks are likely\n");
		}
		/* following tests if APC has an 8meg card in first slot - which 
		 * means that either the cards should be swapped so that the 8meg
		 * card is in the second slot, or that the two 8meg cards are present
		 * which is wasteful as the first could be replaced by a 4meg card.
		 */
		if ((memconfig & 0xf07) == 0x804)
			printf("Warning: 4MB of 8MB memory card unusable - %s\n",
				((memconfig & 0x070) == 0x040) ? "4MB card recommended" : "exchange card order");
	/*
	 * test to see if the reply register exists
	 * if it does then the value will change when the
	 * base register is read.
	 */
		if ((ioim1_config & 0x0f00) != 0x0900)
			printf("IOIM1 EC level different than expected\n");
		x = ior(IOIM1+IOIM_REPLY);
		(void) ior(IOIM1+IOIM_BASE);
		if (ior(IOIM1+IOIM_REPLY) == x)
			printf("Warning: back-level IOIM - no reply register\n");
		}
#endif ATR
}
#endif ROMPC

shr_int_init()
{	/* reset all shared interrupts before we probe */
	register int irq;

	for(irq = 0; irq < MAX_IRQ; irq++) {
		*shr_int_reset[irq] = 0xff;
	}
}

#ifdef ATR
/*
 * Establish addressability to variables passed to us from the pc code.
 */

/*
 *  Functions for controlling the mapping of addresses from the ROMP
 *  address space to the PC address space.
 */


set_512_window(pc_addr)
{
	register int ioaddr = (int)&pcif_reg->pc_wind512;
	register int boundary,offset;

	boundary = (pc_addr & 0xFFF80000) >> PCIF_R5_SHIFT;
	current_512_w = boundary;
	offset = (pc_addr & 0x0007FFFF);
	IOOUT(ioaddr, boundary); 
	return(offset);
}

set_128_window(pc_addr)
{
	register int ioaddr = (int)&pcif_reg->pc_wind128;
	register int boundary,offset;

	boundary = (pc_addr & 0xFFFE0000) >> PCIF_R4_SHIFT;
	current_128_w = boundary;
	offset = (pc_addr & 0x0001FFFF);
	IOOUT(ioaddr,boundary);
	return(offset);
}

#ifdef DEBUG
char *pc_op(op)
{
	switch (op) {
	case CB_SETMAP: return("SETMAP");
	case CB_REBOOT: return("REBOOT");
	case CB_TODREAD: return("TODREAD");
	case CB_TODRESET: return("TODRESET");
	case CB_HALT: return("HALT");
	case CB_RESTART: return("RESTART");
	case CB_QEOI: return("QEOI");
	case CB_EOI3: return("EOI3");
	case CB_EOI4: return("EOI4");
	case CB_HDREQ: return("HDREQ");
	case CB_FDREQ: return("FDREQ");
	case CB_UBREQ: return("UBREQ");
	case CB_BBT: return("BBT");
	case CB_BIOSREQ: return("BIOSREQ");
	case CB_AFIREQ: return("AFIREQ");
	case CB_MSREQ: return("MSREQ");
	case CB_KBREQ: return("KBREQ");
	case CB_SPKREQ: return("SPKREQ");
	case CB_MASKREQ: return("MASKREQ");
	case CB_OPREQ: return("OPREQ");
	case CB_TAPEREQ: return("TAPEREQ");
	default: return("");
	}
}
#endif DEBUG

pc_req(op,unix_cb,cb_ent)	
register u_short op;
register char *unix_cb;
register u_short cb_ent;
{
        register int pcintr = (int)&pcif_reg->romp_ctrl;
	register int old_window,i = 0;      
	register int s;


	old_window = get_512_window(); 		/* Save the current window */
	set_512_window(cbcb_addr);		/* Address the cbcb        */

	s = _spl2();					
	while((cbcb->op_code & 0x00ff) != 0) {  /* wait for the op_code */
	     delay(5);				/* field to clear */
	     if(i++ > cbcb_wait) {
		set_512_window(old_window);
	  	printf("PC REQUEST TIMEOUT. (%x)\n,",cbcb->op_code & 0x00ff);
		splx(s);
		return(-1);
		}
	}

	DEBUGF(pcdebug&0x01, printf("pc_req: op=%x<%s> unix_cb=%x cb_ent=%x\n",
		op, pc_op(op), unix_cb, cb_ent));

	PUT_PC(cbcb->cbcb_ent[cb_ent].unix_cb,(u_long)unix_cb); /* load the unix_cb */

	PUT_PC(cbcb->op_code,op);	/* load the op_code */
	
	SETBIT(pcintr,R_IREQ);		/* ROMP -> PC Inter */

	set_512_window(old_window);	/* restore the 512K window */

	splx(s);
	return(0);
}

#define swaph(value) (((value>>8)&0xff)|((value&0xff)<<8))

/*
 * make a request on another cpu. Of course the cpu must exist
 * for this to make sense. We don't allow our cpu as a recipient
 * of the request.
 */
pc_req_alt(op,unix_cb,cb_ent,cpu)
register u_short op;
register char *unix_cb;
register u_short cb_ent;
int cpu;
{
	register int old_window,i = 0;      
	register int s;
	struct cbcb *cbcb;
	int alt_cbcb_addr;


	if ((unsigned) cpu >= config.maxcpu)
		return(ENODEV);			/* invalid cpu number */

	if (cpu == config.cpu)
		return(EINVAL);			/* invalid cpu number */

	old_window = get_512_window(); 		/* Save the current window */
	cbcb = (struct cbcb *)(set_512_window(cbcb_addr)+pcif_512_fw);
	if ((alt_cbcb_addr = cbcb->cbcb[cpu]) == 0)	/* get that cbcb addr */
		return(EINVAL);			/* not possible */
	cbcb = (struct cbcb *)(set_512_window(alt_cbcb_addr)+pcif_512_fw);
	if (cbcb->state == swaph(STATE_RUNNING))
		return(EBUSY);			/* still running */

	s = _spl2();					
	while((cbcb->op_code & 0x00ff) != 0) {  /* wait for the op_code */
	     delay(5);				/* field to clear */
	     if(i++ > cbcb_wait) {
		set_512_window(old_window);
	  	printf("ALT CPU %d PC REQUEST TIMEOUT. (%x)\n", cpu, cbcb->op_code & 0x00ff);
		splx(s);
		return(-1);
		}
	}

	DEBUGF(pcdebug&0x01, printf("pc_req_alt: op=%x<%s> unix_cb=%x cb_ent=%x\n",
		op, pc_op(op), unix_cb, cb_ent));

	PUT_PC(cbcb->cbcb_ent[cb_ent].unix_cb,(u_long)unix_cb); /* load the unix_cb */

	PUT_PC(cbcb->op_code,op);	/* load the op_code */
	
	SETBIT(swaph(cbcb->port)+R_INTR,R_IREQ);	/* ROMP -> PC Inter */

	set_512_window(old_window);	/* restore the 512K window */

	splx(s);
	return(0);
}


/*
 * if mask is non-zero then we mask off those interrupts on the 
 * PS2 side that are being forwarded to us. This will hold those
 * interrupts pending in the 8259 on the PS2 side until we re-enable
 * the interrupts. If mask is zero then we restore the 8259 mask to
 * its normal state.
 */
maskints(mask)
u_short mask;
{

	struct mask_int mask_int;
	struct mask_int *pcptr;
	int old_window = get_512_window();

	set_512_window(cbcb_addr);		/* Address the cbcb */
	pcptr = (struct mask_int *)(cbcb->cbcb_ent[MASKENT].pc_cb+pcif_512_fw);

	if(mask) {	/* if we are masking anything */
		mask_int.master = (char)((mask & 0xff00) >> 8);
		mask_int.slave =(char)(mask & 0x00ff);
	} else {	/* we are restoring the original */
		mask_int.master = (char) pcptr -> master; 
		mask_int.slave = (char) pcptr -> slave;
	}

	pc_req(CB_MASKREQ,&mask_int,MASKENT);
	set_512_window(old_window);	

}  

/*
 * function to set pcvec_map observing the window protocols 
 * we return the old value in case anyone wants it.
 */

set_pcvec_map(index,value)
{
	int	old_window = get_512_window();
	int	result;
	
	set_512_window(sv_pc_cb);
	result = pcvec_map[index];
	pcvec_map[index] = value;
	set_512_window(old_window);
	return(result);
}

/*
 * get the control block address observing the window protocols
 */
get_pc_cb(index)
{
	int		old_window = get_512_window();
	int		result;

	(void) set_512_window(cbcb_addr);
	result =  cbcb->cbcb_ent[index].pc_cb;
	set_512_window(old_window);
	return(result);
}


int sudebug = 0;		/* suspend debug flag */
#define SUSPEND_TIMEOUT 60
int suspend_timeout = SUSPEND_TIMEOUT;	/* how long to wait for timeout */

/*
 * we suspend unix by sending a pc request and then waiting until 
 * the pc side processes it.
 * then we wait until the suspension is over
 * we call the screen save routines in order to save the screen state
 * and later restore the state (restore state routines must pick up
 * new pointers in case the pc data moved).
 */
suspend_screens()
{
	int i = suspend_timeout;		/* allow 1 minute for the save */
	int n;
	DEBUGF(sudebug,printf("suspend_screens called\n"));
	while ((n = screen_all_save()) && --i > 0)
		sleep((caddr_t)&lbolt,PZERO+1);
	if (n)
		printf("suspend: %d screen(s) didn't complete save\n",n);
}

suspend()
{
	extern struct restart restart_block;
	int	old_window = get_512_window();
	char	old_vecmap[16];
	int	i;
	int	n;
	extern struct kbdata  *kbdata;		/* ptr to keyboard data area in PS/2 memory */
	extern int		kb_pc_cb;	/* actual PS/2 address of keyboard data */
	extern int		ms_pc_cb;	/* actual PS/2 address of mouse data */
	extern int		sp_pc_cb;	/* actual PS/2 address of mouse data */
	
	set_512_window(sv_pc_cb);
	bcopy(pcvec_map, old_vecmap, 16);	/* save pc_vecmap */

	(void) _spl5();				/* allow device interrupts again */

	DEBUGF(sudebug,printf("suspending...\n"););

	suspend_devices(SUSPEND_START);

	(void) _spl2();			/* allow only clock interrupts */

	screen_all_save();		/* redundant, but shouldn't harm anyone */

	pc_req(CB_SUSPEND,0,0);		/* tell PC to suspend  */

	for (i=0; i<1000; ++i) {
		if (restart_block.magic == RESTART_MAGIC)
			break;
		delay(10);		/* wait 10 seconds for pc to do its part */
	}

	if (restart_block.magic != RESTART_MAGIC) {
		(void) spl0();
		DEBUGF(sudebug,printf("PS/2 ignored suspend request\n"););
		u.u_error = EINVAL;
		return;
	}

	while (restart_block.magic == RESTART_MAGIC)
		++restart_block.counter;	/* bump it */
/*
 * pick up new copies of various pc pointers in case they moved
 */
	cbcb = (struct cbcb *)(set_512_window(cbcb_addr)+pcif_512_fw);
	sv_pc_cb =  cbcb->cbcb_ent[SVENT].pc_cb;
	kb_pc_cb = cbcb->cbcb_ent[KBENT].pc_cb;
	ms_pc_cb = cbcb->cbcb_ent[MSENT].pc_cb;
	sp_pc_cb = cbcb->cbcb_ent[SPKENT].pc_cb;
	set_512_window(sv_pc_cb);
	pcvec_map = (char *)(set_512_window(sv_pc_cb) + pcif_512_fw);

	bcopy(old_vecmap, pcvec_map, 16);	/* restore pc_vecmap */

	kbdata = (struct kbdata *) (set_512_window(kb_pc_cb) + pcif_512_fw);

	kbdata->fwd_int = 1;		/* turn on kbd interrupt forwarding from PC */
#if NMS > 0
	{
		extern struct msdata *msdata;
		/* set mouse data ptr also */
		msdata = (struct msdata *) (set_512_window(ms_pc_cb) + pcif_512_fw);
	}
#endif NMS
	{
		extern struct spkdata *spkdata;
		spkdata = (struct spkdata *) (set_512_window(sp_pc_cb) + pcif_512_fw);
	}

	init_ps2_clock();		/* pick up new clock pointers */

#ifdef DEBUG
	pcdebugptr = (ushort *)&cbcb->debug;
#endif DEBUG


	(void) _spl5();				/* allow devices interrupts */
	suspend_devices(SUSPEND_DONE);

	i = suspend_timeout;
	while ((n=screen_all_restore()) && --i > 0)
		sleep((caddr_t)&lbolt,PZERO+1);	/* restore screens */

	DEBUGF(sudebug,printf("suspension over! (%d incomplete)\n",n););
	set_512_window(old_window);
	(void) spl0();				/* allow all interrupts again */
}

suspend_devices(how)
{
	register struct iocc_driver *id;
	register struct iocc_device *iod;

	for (iod = ioccdinit; id = iod->iod_driver; ++iod) {
		if (iod->iod_alive == 0 || (id->idr_flags & DRIVER_SUSPEND) == 0)
			continue;
		DEBUGF(sudebug,
			printf("calling %s suspend(%x,%x,%x)\n",id->idr_dname,iod,id,how));
		(*id->idr_suspend)(iod, id, how);
	}
#if NMS > 0
	mssuspend(how);				/* handle mouse specially */
#endif NMS
}
#endif ATR

#define	DOSWAP			/* change swdevt, argdev, and dumpdev too */
u_long	bootdev;		/* should be dev_t, but not until 32 bits */

static	char devname[][2] = {
	'f','d',	/* 0 = fd */
	'h','d',	/* 1 = hd */
#ifdef ATR
	'o','p',	/* 2 = op */
#endif ATR
};

#define	PARTITIONMASK	0x7
#define	PARTITIONSHIFT	3

/*
 * Attempt to find the device from which we were booted.
 * If we can do so, and not instructed not to do so,
 * change rootdev to correspond to the load device.
 */
setroot()
{
	int  majdev, mindev, unit, part, controller, adaptor;
	dev_t temp, orootdev;
	struct swdevt *swp;

	if (boothowto & RB_DFLTROOT ||
	    (bootdev & B_MAGICMASK) != (u_long)B_DEVMAGIC)
		return;
	majdev = B_TYPE(bootdev);
	if (majdev >= sizeof(devname) / sizeof(devname[0]))
		return;
	adaptor = B_ADAPTOR(bootdev);
	controller = B_CONTROLLER(bootdev);
	part = B_PARTITION(bootdev);
	unit = B_UNIT(bootdev);
	/*
	 * Search iocc devices.
	 *
	 */
	{
		register struct iocc_device *iod;

		for (iod = ioccdinit; iod->iod_driver; iod++)
			if (iod->iod_alive && iod->iod_slave == unit &&
			   iod->iod_ctlr == controller &&
/*			   iod->iod_vbanum == adaptor && /* not used */
			   iod->iod_driver->idr_dname[0] == devname[majdev][0] &&
			   iod->iod_driver->idr_dname[1] == devname[majdev][1])
				break;
		if (iod->iod_driver == 0)
			return;
		mindev = iod->iod_unit;
	}
	mindev = (mindev << PARTITIONSHIFT) + part;
	orootdev = rootdev;
	rootdev = makedev(majdev, mindev);
	/*
	 * If the original rootdev is the same as the one
	 * just calculated, don't need to adjust the swap configuration.
	 */
	if (rootdev == orootdev)
		return;
	printf("changing root device to %c%c%d%c\n",
		devname[majdev][0], devname[majdev][1],
		mindev >> PARTITIONSHIFT, part + 'a');
#ifdef DOSWAP
	mindev &= ~PARTITIONMASK;
	for (swp = swdevt; swp->sw_dev; swp++) {
		if (majdev == major(swp->sw_dev) &&
		    mindev == (minor(swp->sw_dev) & ~PARTITIONMASK)) {
			temp = swdevt[0].sw_dev;
			swdevt[0].sw_dev = swp->sw_dev;
			swp->sw_dev = temp;
			break;
		}
	}
	if (swp->sw_dev == 0)
		return;
	/*
	 * If argdev and dumpdev were the same as the old primary swap
	 * device, move them to the new primary swap device.
	 */
	if (temp == dumpdev)
		dumpdev = swdevt[0].sw_dev;
	if (temp == argdev)
		argdev = swdevt[0].sw_dev;
#endif
}
