+/* autoconf.c 4.46 83/01/01 */
+
+/*
+ * Setup the system to run on the current machine.
+ *
+ * Configure() is called at boot time and initializes the uba and mba
+ * device tables and the memory controller monitoring. Available
+ * devices are determined (from possibilities mentioned in ioconf.c),
+ * and the drivers are initialized.
+ *
+ * N.B.: A lot of the conditionals based on processor type say
+ * #if VAX780
+ * and
+ * #if VAX750
+ * which may be incorrect after more processors are introduced if they
+ * are like either of these machines.
+ *
+ * TODO:
+ * use pcpu info about whether a ubasr exists
+ */
+
+#include "mba.h"
+
+#include "../machine/pte.h"
+
+#include "../h/param.h"
+#include "../h/systm.h"
+#include "../h/map.h"
+#include "../h/buf.h"
+#include "../h/dk.h"
+#include "../h/vm.h"
+
+#include "../vax/cpu.h"
+#include "../vax/mem.h"
+#include "../vax/mtpr.h"
+#include "../vax/nexus.h"
+#include "../vax/scb.h"
+#include "../vaxmba/mbareg.h"
+#include "../vaxmba/mbavar.h"
+#include "../vaxuba/ubareg.h"
+#include "../vaxuba/ubavar.h"
+
+/*
+ * The following several variables are related to
+ * the configuration process, and are used in initializing
+ * the machine.
+ */
+int cold; /* if 1, still working on cold-start */
+int nexnum; /* current nexus number */
+int dkn; /* number of iostat dk numbers assigned so far */
+
+/*
+ * Addresses of the (locore) routines which bootstrap us from
+ * hardware traps to C code. Filled into the system control block
+ * as necessary.
+ */
+#if NMBA > 0
+int (*mbaintv[4])() = { Xmba0int, Xmba1int, Xmba2int, Xmba3int };
+#endif
+#if VAX780
+int (*ubaintv[4])() = { Xua0int, Xua1int, Xua2int, Xua3int };
+#endif
+
+/*
+ * This allocates the space for the per-uba information,
+ * such as buffered data path usage.
+ */
+struct uba_hd uba_hd[MAXNUBA];
+
+/*
+ * Determine mass storage and memory configuration for a machine.
+ * Get cpu type, and then switch out to machine specific procedures
+ * which will probe adaptors to see what is out there.
+ */
+configure()
+{
+ union cpusid cpusid;
+ register struct percpu *ocp;
+ register int *ip;
+ extern char Sysbase[];
+
+ cpusid.cpusid = mfpr(SID);
+ for (ocp = percpu; ocp->pc_cputype; ocp++)
+ if (ocp->pc_cputype == cpusid.cpuany.cp_type) {
+ probenexus(ocp);
+ /*
+ * Write protect the scb. It is strange
+ * that this code is here, but this is as soon
+ * as we are done mucking with it, and the
+ * write-enable was done in assembly language
+ * to which we will never return.
+ */
+ ip = (int *)Sysmap; *ip &= ~PG_PROT; *ip |= PG_KR;
+ mtpr(TBIS, Sysbase);
+#if GENERIC
+ setconf();
+#endif
+ cold = 0;
+ memenable();
+ return;
+ }
+ printf("cpu type %d not configured\n", cpusid.cpuany.cp_type);
+ asm("halt");
+}
+
+/*
+ * Probe nexus space, finding the interconnects
+ * and setting up and probing mba's and uba's for devices.
+ */
+/*ARGSUSED*/
+probenexus(pcpu)
+ register struct percpu *pcpu;
+{
+ register struct nexus *nxv;
+ struct nexus *nxp = pcpu->pc_nexbase;
+ union nexcsr nexcsr;
+ int i;
+
+ nexnum = 0, nxv = nexus;
+ for (; nexnum < pcpu->pc_nnexus; nexnum++, nxp++, nxv++) {
+ nxaccess(nxp, Nexmap[nexnum]);
+ if (badaddr((caddr_t)nxv, 4))
+ continue;
+ if (pcpu->pc_nextype && pcpu->pc_nextype[nexnum] != NEX_ANY)
+ nexcsr.nex_csr = pcpu->pc_nextype[nexnum];
+ else
+ nexcsr = nxv->nexcsr;
+ if (nexcsr.nex_csr&NEX_APD)
+ continue;
+ switch (nexcsr.nex_type) {
+
+ case NEX_MBA:
+ printf("mba%d at tr%d\n", nummba, nexnum);
+ if (nummba >= NMBA) {
+ printf("%d mba's", nummba);
+ goto unconfig;
+ }
+#if NMBA > 0
+ mbafind(nxv, nxp);
+ nummba++;
+#endif
+ break;
+
+ case NEX_UBA0:
+ case NEX_UBA1:
+ case NEX_UBA2:
+ case NEX_UBA3:
+ printf("uba%d at tr%d\n", numuba, nexnum);
+ if (numuba >= 4) {
+ printf("5 uba's");
+ goto unsupp;
+ }
+#if VAX780
+ if (cpu == VAX_780)
+ setscbnex(ubaintv[numuba]);
+#endif
+ i = nexcsr.nex_type - NEX_UBA0;
+ unifind((struct uba_regs *)nxv, (struct uba_regs *)nxp,
+ umem[i], pcpu->pc_umaddr[i], UMEMmap[i]);
+#if VAX780
+ if (cpu == VAX_780)
+ ((struct uba_regs *)nxv)->uba_cr =
+ UBACR_IFS|UBACR_BRIE|
+ UBACR_USEFIE|UBACR_SUEFIE|
+ (((struct uba_regs *)nxv)->uba_cr&0x7c000000);
+#endif
+ numuba++;
+ break;
+
+ case NEX_DR32:
+ /* there can be more than one... are there other codes??? */
+ printf("dr32");
+ goto unsupp;
+
+ case NEX_MEM4:
+ case NEX_MEM4I:
+ case NEX_MEM16:
+ case NEX_MEM16I:
+ printf("mcr%d at tr%d\n", nmcr, nexnum);
+ if (nmcr >= 4) {
+ printf("5 mcr's");
+ goto unsupp;
+ }
+ mcraddr[nmcr++] = (struct mcr *)nxv;
+ break;
+
+ case NEX_MPM0:
+ case NEX_MPM1:
+ case NEX_MPM2:
+ case NEX_MPM3:
+ printf("mpm");
+ goto unsupp;
+
+ default:
+ printf("nexus type %x", nexcsr.nex_type);
+unsupp:
+ printf(" unsupported (at tr %d)\n", nexnum);
+ continue;
+unconfig:
+ printf(" not configured\n");
+ continue;
+ }
+ }
+}
+
+#if NMBA > 0
+struct mba_device *mbaconfig();
+/*
+ * Find devices attached to a particular mba
+ * and look for each device found in the massbus
+ * initialization tables.
+ */
+mbafind(nxv, nxp)
+ struct nexus *nxv, *nxp;
+{
+ register struct mba_regs *mdp;
+ register struct mba_drv *mbd;
+ register struct mba_device *mi;
+ register struct mba_slave *ms;
+ int dn, dt, sn;
+ struct mba_device fnd;
+
+ mdp = (struct mba_regs *)nxv;
+ mba_hd[nummba].mh_mba = mdp;
+ mba_hd[nummba].mh_physmba = (struct mba_regs *)nxp;
+ setscbnex(mbaintv[nummba]);
+ fnd.mi_mba = mdp;
+ fnd.mi_mbanum = nummba;
+ for (mbd = mdp->mba_drv, dn = 0; mbd < &mdp->mba_drv[8]; mbd++, dn++) {
+ if ((mbd->mbd_ds&MBDS_DPR) == 0)
+ continue;
+ mdp->mba_sr |= MBSR_NED; /* si kludge */
+ dt = mbd->mbd_dt & 0xffff;
+ if (dt == 0)
+ continue;
+ if (mdp->mba_sr&MBSR_NED)
+ continue; /* si kludge */
+ if (dt == MBDT_MOH)
+ continue;
+ fnd.mi_drive = dn;
+#define qeq(a, b) ( a == b || a == '?' )
+ if ((mi = mbaconfig(&fnd, dt)) && (dt & MBDT_TAP))
+ for (sn = 0; sn < 8; sn++) {
+ mbd->mbd_tc = sn;
+ for (ms = mbsinit; ms->ms_driver; ms++)
+ if (ms->ms_driver == mi->mi_driver &&
+ ms->ms_alive == 0 &&
+ qeq(ms->ms_ctlr, mi->mi_unit) &&
+ qeq(ms->ms_slave, sn) &&
+ (*ms->ms_driver->md_slave)(mi, ms, sn)) {
+ printf("%s%d at %s%d slave %d\n"
+ , ms->ms_driver->md_sname
+ , ms->ms_unit
+ , mi->mi_driver->md_dname
+ , mi->mi_unit
+ , sn
+ );
+ ms->ms_alive = 1;
+ ms->ms_ctlr = mi->mi_unit;
+ ms->ms_slave = sn;
+ }
+ }
+ }
+ mdp->mba_cr = MBCR_INIT;
+ mdp->mba_cr = MBCR_IE;
+}
+
+/*
+ * Have found a massbus device;
+ * see if it is in the configuration table.
+ * If so, fill in its data.
+ */
+struct mba_device *
+mbaconfig(ni, type)
+ register struct mba_device *ni;
+ register int type;
+{
+ register struct mba_device *mi;
+ register short *tp;
+ register struct mba_hd *mh;
+
+ for (mi = mbdinit; mi->mi_driver; mi++) {
+ if (mi->mi_alive)
+ continue;
+ tp = mi->mi_driver->md_type;
+ for (mi->mi_type = 0; *tp; tp++, mi->mi_type++)
+ if (*tp == (type&MBDT_TYPE))
+ goto found;
+ continue;
+found:
+#define match(fld) (ni->fld == mi->fld || mi->fld == '?')
+ if (!match(mi_drive) || !match(mi_mbanum))
+ continue;
+ printf("%s%d at mba%d drive %d\n",
+ mi->mi_driver->md_dname, mi->mi_unit,
+ ni->mi_mbanum, ni->mi_drive);
+ mi->mi_alive = 1;
+ mh = &mba_hd[ni->mi_mbanum];
+ mi->mi_hd = mh;
+ mh->mh_mbip[ni->mi_drive] = mi;
+ mh->mh_ndrive++;
+ mi->mi_mba = ni->mi_mba;
+ mi->mi_drv = &mi->mi_mba->mba_drv[ni->mi_drive];
+ mi->mi_mbanum = ni->mi_mbanum;
+ mi->mi_drive = ni->mi_drive;
+ /*
+ * If drive has never been seen before,
+ * give it a dkn for statistics.
+ */
+ if (mi->mi_driver->md_info[mi->mi_unit] == 0) {
+ mi->mi_driver->md_info[mi->mi_unit] = mi;
+ if (mi->mi_dk && dkn < DK_NDRIVE)
+ mi->mi_dk = dkn++;
+ else
+ mi->mi_dk = -1;
+ }
+ (*mi->mi_driver->md_attach)(mi);
+ return (mi);
+ }
+ return (0);
+}
+#endif
+
+/*
+ * Fixctlrmask fixes the masks of the driver ctlr routines
+ * which otherwise save r10 and r11 where the interrupt and br
+ * level are passed through.
+ */
+fixctlrmask()
+{
+ register struct uba_ctlr *um;
+ register struct uba_device *ui;
+ register struct uba_driver *ud;
+#define phys(a,b) ((b)(((int)(a))&0x7fffffff))
+
+ for (um = ubminit; ud = phys(um->um_driver, struct uba_driver *); um++)
+ *phys(ud->ud_probe, short *) &= ~0xc00;
+ for (ui = ubdinit; ud = phys(ui->ui_driver, struct uba_driver *); ui++)
+ *phys(ud->ud_probe, short *) &= ~0xc00;
+}
+
+/*
+ * Find devices on a UNIBUS.
+ * Uses per-driver routine to set <br,cvec> into <r11,r10>,
+ * and then fills in the tables, with help from a per-driver
+ * slave initialization routine.
+ */
+unifind(vubp, pubp, vumem, pumem, memmap)
+ struct uba_regs *vubp, *pubp;
+ caddr_t vumem, pumem;
+ struct pte *memmap;
+{
+#ifndef lint
+ register int br, cvec; /* MUST BE r11, r10 */
+#else
+ /*
+ * Lint doesn't realize that these
+ * can be initialized asynchronously
+ * when devices interrupt.
+ */
+ register int br = 0, cvec = 0;
+#endif
+ register struct uba_device *ui;
+ register struct uba_ctlr *um;
+ u_short *reg, *ap, addr;
+ struct uba_hd *uhp;
+ struct uba_driver *udp;
+ int i, (**ivec)(), haveubasr;
+ caddr_t ualloc, zmemall();
+ extern int catcher[256];
+
+ /*
+ * Initialize the UNIBUS, by freeing the map
+ * registers and the buffered data path registers
+ */
+ uhp = &uba_hd[numuba];
+ uhp->uh_map = (struct map *)calloc(UAMSIZ * sizeof (struct map));
+ ubainitmaps(uhp);
+ haveubasr = cpu == VAX_780;
+
+ /*
+ * Save virtual and physical addresses
+ * of adaptor, and allocate and initialize
+ * the UNIBUS interrupt vector.
+ */
+ uhp->uh_uba = vubp;
+ uhp->uh_physuba = pubp;
+/* HAVE TO DO SOMETHING SPECIAL FOR SECOND UNIBUS ON COMETS HERE */
+ if (numuba == 0)
+ uhp->uh_vec = UNIvec;
+ else
+ uhp->uh_vec = (int(**)())calloc(512);
+ for (i = 0; i < 128; i++)
+ uhp->uh_vec[i] =
+ scbentry(&catcher[i*2], SCB_ISTACK);
+ /*
+ * Set last free interrupt vector for devices with
+ * programmable interrupt vectors. Use is to decrement
+ * this number and use result as interrupt vector.
+ */
+ uhp->uh_lastiv = 0x200;
+
+ ubaaccess(pumem, memmap);
+#if VAX780
+ if (haveubasr) {
+ vubp->uba_sr = vubp->uba_sr;
+ vubp->uba_cr = UBACR_IFS|UBACR_BRIE;
+ }
+#endif
+ /*
+ * Grab some memory to record the umem address space we allocate,
+ * so we can be sure not to place two devices at the same address.
+ *
+ * We could use just 1/8 of this (we only want a 1 bit flag) but
+ * we are going to give it back anyway, and that would make the
+ * code here bigger (which we can't give back), so ...
+ *
+ * One day, someone will make a unibus with something other than
+ * an 8K i/o address space, & screw this totally.
+ */
+ ualloc = zmemall(memall, 8*1024);
+ if (ualloc == (caddr_t)0)
+ panic("no mem for unifind");
+
+ /*
+ * Map the first page of UNIBUS i/o
+ * space to the first page of memory
+ * for devices which will need to dma
+ * output to produce an interrupt.
+ */
+ *(int *)(&vubp->uba_map[0]) = UBAMR_MRV;
+
+#define ubaoff(off) ((off)&0x1fff)
+#define ubaddr(off) (u_short *)((int)vumem + (ubaoff(off)|0x3e000))
+ /*
+ * Check each unibus mass storage controller.
+ * For each one which is potentially on this uba,
+ * see if it is really there, and if it is record it and
+ * then go looking for slaves.
+ */
+ for (um = ubminit; udp = um->um_driver; um++) {
+ if (um->um_ubanum != numuba && um->um_ubanum != '?')
+ continue;
+ addr = (u_short)um->um_addr;
+ /*
+ * use the particular address specified first,
+ * or if it is given as "0", of there is no device
+ * at that address, try all the standard addresses
+ * in the driver til we find it
+ */
+ for (ap = udp->ud_addr; addr || (addr = *ap++); addr = 0) {
+
+ if (ualloc[ubaoff(addr)])
+ continue;
+ reg = ubaddr(addr);
+ if (badaddr((caddr_t)reg, 2))
+ continue;
+#if VAX780
+ if (haveubasr && vubp->uba_sr) {
+ vubp->uba_sr = vubp->uba_sr;
+ continue;
+ }
+#endif
+ cvec = 0x200;
+ i = (*udp->ud_probe)(reg, um->um_ctlr);
+#if VAX780
+ if (haveubasr && vubp->uba_sr) {
+ vubp->uba_sr = vubp->uba_sr;
+ continue;
+ }
+#endif
+ if (i == 0)
+ continue;
+ printf("%s%d at uba%d csr %o ",
+ udp->ud_mname, um->um_ctlr, numuba, addr);
+ if (cvec == 0) {
+ printf("zero vector\n");
+ continue;
+ }
+ if (cvec == 0x200) {
+ printf("didn't interrupt\n");
+ continue;
+ }
+ printf("vec %o, ipl %x\n", cvec, br);
+ um->um_alive = 1;
+ um->um_ubanum = numuba;
+ um->um_hd = &uba_hd[numuba];
+ um->um_addr = (caddr_t)reg;
+ udp->ud_minfo[um->um_ctlr] = um;
+ for (ivec = um->um_intr; *ivec; ivec++) {
+ um->um_hd->uh_vec[cvec/4] =
+ scbentry(*ivec, SCB_ISTACK);
+ cvec += 4;
+ }
+ for (ui = ubdinit; ui->ui_driver; ui++) {
+ if (ui->ui_driver != udp || ui->ui_alive ||
+ ui->ui_ctlr != um->um_ctlr && ui->ui_ctlr != '?' ||
+ ui->ui_ubanum != numuba && ui->ui_ubanum != '?')
+ continue;
+ if ((*udp->ud_slave)(ui, reg)) {
+ ui->ui_alive = 1;
+ ui->ui_ctlr = um->um_ctlr;
+ ui->ui_ubanum = numuba;
+ ui->ui_hd = &uba_hd[numuba];
+ ui->ui_addr = (caddr_t)reg;
+ ui->ui_physaddr = pumem + ubdevreg(addr);
+ if (ui->ui_dk && dkn < DK_NDRIVE)
+ ui->ui_dk = dkn++;
+ else
+ ui->ui_dk = -1;
+ ui->ui_mi = um;
+ /* ui_type comes from driver */
+ udp->ud_dinfo[ui->ui_unit] = ui;
+ printf("%s%d at %s%d slave %d\n",
+ udp->ud_dname, ui->ui_unit,
+ udp->ud_mname, um->um_ctlr, ui->ui_slave);
+ (*udp->ud_attach)(ui);
+ }
+ }
+ break;
+ }
+ }
+ /*
+ * Now look for non-mass storage peripherals.
+ */
+ for (ui = ubdinit; udp = ui->ui_driver; ui++) {
+ if (ui->ui_ubanum != numuba && ui->ui_ubanum != '?' ||
+ ui->ui_alive || ui->ui_slave != -1)
+ continue;
+ addr = (u_short)ui->ui_addr;
+
+ for (ap = udp->ud_addr; addr || (addr = *ap++); addr = 0) {
+
+ if (ualloc[ubaoff(addr)])
+ continue;
+ reg = ubaddr(addr);
+ if (badaddr((caddr_t)reg, 2))
+ continue;
+#if VAX780
+ if (haveubasr && vubp->uba_sr) {
+ vubp->uba_sr = vubp->uba_sr;
+ continue;
+ }
+#endif
+ cvec = 0x200;
+ i = (*udp->ud_probe)(reg);
+#if VAX780
+ if (haveubasr && vubp->uba_sr) {
+ vubp->uba_sr = vubp->uba_sr;
+ continue;
+ }
+#endif
+ if (i == 0)
+ continue;
+ printf("%s%d at uba%d csr %o ",
+ ui->ui_driver->ud_dname, ui->ui_unit, numuba, addr);
+ if (cvec == 0) {
+ printf("zero vector\n");
+ continue;
+ }
+ if (cvec == 0x200) {
+ printf("didn't interrupt\n");
+ continue;
+ }
+ printf("vec %o, ipl %x\n", cvec, br);
+ while (--i >= 0)
+ ualloc[ubaoff(addr+i)] = 1;
+ ui->ui_hd = &uba_hd[numuba];
+ for (ivec = ui->ui_intr; *ivec; ivec++) {
+ ui->ui_hd->uh_vec[cvec/4] =
+ scbentry(*ivec, SCB_ISTACK);
+ cvec += 4;
+ }
+ ui->ui_alive = 1;
+ ui->ui_ubanum = numuba;
+ ui->ui_addr = (caddr_t)reg;
+ ui->ui_physaddr = pumem + ubdevreg(addr);
+ ui->ui_dk = -1;
+ /* ui_type comes from driver */
+ udp->ud_dinfo[ui->ui_unit] = ui;
+ (*udp->ud_attach)(ui);
+ break;
+ }
+ }
+
+#ifdef AUTO_DEBUG
+ printf("Unibus allocation map");
+ for (i = 0; i < 8*1024; ) {
+ register n, m;
+
+ if ((i % 128) == 0) {
+ printf("\n%6o:", i);
+ for (n = 0; n < 128; n++)
+ if (ualloc[i+n])
+ break;
+ if (n == 128) {
+ i += 128;
+ continue;
+ }
+ }
+
+ for (n = m = 0; n < 16; n++) {
+ m <<= 1;
+ m |= ualloc[i++];
+ }
+
+ printf(" %4x", m);
+ }
+ printf("\n");
+#endif
+
+ wmemfree(ualloc, 8*1024);
+}
+
+setscbnex(fn)
+ int (*fn)();
+{
+ register struct scb *scbp = &scb;
+
+ scbp->scb_ipl14[nexnum] = scbp->scb_ipl15[nexnum] =
+ scbp->scb_ipl16[nexnum] = scbp->scb_ipl17[nexnum] =
+ scbentry(fn, SCB_ISTACK);
+}
+
+/*
+ * Make a nexus accessible at physical address phys
+ * by mapping kernel ptes starting at pte.
+ *
+ * WE LEAVE ALL NEXI MAPPED; THIS IS PERHAPS UNWISE
+ * SINCE MISSING NEXI DONT RESPOND. BUT THEN AGAIN
+ * PRESENT NEXI DONT RESPOND TO ALL OF THEIR ADDRESS SPACE.
+ */
+nxaccess(physa, pte)
+ struct nexus *physa;
+ register struct pte *pte;
+{
+ register int i = btop(sizeof (struct nexus));
+ register unsigned v = btop(physa);
+
+ do
+ *(int *)pte++ = PG_V|PG_KW|v++;
+ while (--i > 0);
+ mtpr(TBIA, 0);
+}
+
+ubaaccess(pumem, pte)
+ caddr_t pumem;
+ register struct pte *pte;
+{
+ register int i = 512;
+ register unsigned v = btop(pumem);
+
+ do
+ *(int *)pte++ = PG_V|PG_KW|v++;
+ while (--i > 0);
+ mtpr(TBIA, 0);
+}