/* * Copyright (c) 1986 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. * * @(#)do_config.c 2.1 (2.11BSD GTE) 12/30/92 */ /* * Now with all our information, make a configuration * Devices without both attach and probe routines will * not be configured into the system */ #include #include #include #include #include #include #include #include "dtab.h" #include "ivec.h" extern int kmem, verbose, debug, errno, complain; extern NLIST *bad_nl, *good_nl, *int_nl, *end_vector, *trap_nl, *sep_nl; extern NLIST *next_nl; grab(where) u_int where; { int var; lseek(kmem,((long) where) & 0xffffL,0); read(kmem,&var,sizeof(var)); if (debug) printf("Grab %o = %o",where, var); return(var); } stuff(var, where) unsigned int where; { char s[20]; if (debug) { printf("Stuff %o @ %o\n", var, where); return; } lseek(kmem, ((long) where) & 0xffffL, 0); if (write(kmem, &var, sizeof var) < sizeof var) { sprintf(s, "stuff 0%o", where); perror(s); } } prdev(dp) DTAB *dp; { printf("%s ", dp->dt_name); if (dp->dt_unit == -1) putchar('?'); else printf("%d", dp->dt_unit); printf(" csr %o vector %o", dp->dt_addr, dp->dt_vector); } /* * Go through the configuration table and probe all the devices. Call * attach for ones which exist. Probe routines should return * ACP_NXDEV No such device * ACP_IFINTR Device exists if interrupts ok * ACP_EXISTS Device exists * All interrupt vectors are poked to either point to conf_goodvec or * conf_badvec which change the value of conf_int. Values of this * variable are: * ACI_BADINTR Device interrupted through wrong vector * ACI_NOINTR Device didn't interrupt * ACI_GOODINTR Interrupt ok */ auto_config() { register DTAB *dp; int ret; if (intval() != CONF_MAGIC) { fputs(myname,stderr); fputs(": namelist doesn't match running kernel.\n",stderr); exit(AC_SETUP); } init_lowcore(bad_nl->n_value); for (dp = devs; dp != NULL; dp = dp->dt_next) { /* * Make sure we have both a probe and attach routine */ if (!((dp->dt_uprobe || (dp->dt_probe && dp->dt_probe->n_value)) && (dp->dt_attach && dp->dt_attach->n_value))) { if (debug || verbose) { prdev(dp); puts(" skipped: No autoconfig routines."); } continue; } /* Make sure the CSR is there */ errno = 0; grab(dp->dt_addr); if (errno) { if (errno != EFAULT && errno != ENXIO) perror("Reading CSR"); if (debug || verbose) { prdev(dp); puts(" skipped: No CSR."); } continue; } /* Ok, try a probe now */ if (expect_intr(dp)) { if (complain) { prdev(dp); puts(" interrupt vector already in use."); } continue; } ret = do_probe(dp); clear_vec(dp); switch (ret) { case ACP_NXDEV: if (debug || verbose) { prdev(dp); puts(" does not exist."); } break; case ACP_IFINTR: switch (intval()) { case ACI_BADINTR: if (debug || verbose || complain) { prdev(dp); puts(" interrupt vector wrong."); } break; case ACI_NOINTR: if (complain) { prdev(dp); puts(" didn't interrupt."); } break; case ACI_GOODINTR: attach(dp); break; default: prdev(dp); printf(" bad interrupt value %d.\n", intval()); break; } break; case ACP_EXISTS: attach(dp); break; default: prdev(dp); printf(" bad probe value %d.\n", ret); break; } } set_unused(); } /* * Return the current value of the interrupt return flag. * Initial value is the magic number */ static int conf_int = CONF_MAGIC; intval() { if (debug) return conf_int; else return grab(int_nl->n_value); } static int save_vec[9][2], save_p; /* * Fill all interrupt vectors of this device with pointers to * the good interrupt vector routine. Also save values to * later restore them. Since we init_lowcore() everything to * conf_badint, anything not equalling that indicates a vector * which is already in use; unless the vector was initialized in l.s, * this device should be skipped. */ expect_intr(dp) DTAB *dp; { HAND *hp; register int addr = dp->dt_vector; /* * A vector of 0 has special meaning for devices which support programmable * (settable) vectors. If a xxVec() entry point is present in the driver and * /etc/dtab has a value of 0 for the vector then 'autoconfig' will allocate * one by calling the kernel routine 'nextiv'. * * If multiple handlers are declared for a device (at present there are no * progammable vector devices with more than 1 handler) the vector passed * to the driver will be the lowest one (the first handle corresponds to * the lowest vector). */ if (!addr) { if (dp->dt_setvec == 0) { prdev(dp); printf(" vector = 0, %sVec undefined\n", dp->dt_name); return(1); } /* * Now count the number of vectors needed. This has the side effect of * allocating the vectors even if an error occurs later. At the end of * the scan the last vector assigned will be the lowest one. In order to * assure adjacency of multiple vectors BR7 is used in the call to the * kernel and it is assumed that at this point in the system's life * nothing else is allocating vectors (the networking has already grabbed the * ones it needs by the time autoconfig is run). */ for (hp = dp->dt_handlers; hp; hp = hp->s_next) { addr = ucall(PSL_BR7, next_nl->n_value, 0, 0); if (addr <= 0) { printf("'nextiv' error for %s\n", dp->dt_name); return(1); } } /* * Now set the lowest vector allocated into the device entry for further * processing. From this point on the vector will behave just as if it * had been read from /etc/dtab. */ dp->dt_vector = addr; } for (save_p = 0, hp = (HAND *)dp->dt_handlers;hp;hp = hp->s_next) { save_vec[save_p][1] = grab(addr + sizeof(int)); if (((save_vec[save_p][0] = grab(addr)) != bad_nl->n_value) && ((save_vec[save_p][0] != hp->s_nl->n_value) || have_set(addr))) { clear_vec(dp); return 1; } save_p++; write_vector(addr, good_nl->n_value, PSL_BR7); addr += IVSIZE; } return 0; } clear_vec(dp) register DTAB *dp; { register int addr = dp->dt_vector, n; for (n = 0; n < save_p; n++) { write_vector(addr, save_vec[n][0], save_vec[n][1]); addr += IVSIZE; } } init_lowcore(val) { int addr; if (debug) return; for (addr = 0; addr < end_vector->n_value; addr += IVSIZE) { if (grab(addr) || grab(addr + 2)) continue; write_vector(addr, val, PSL_BR7); } } do_probe(dp) register DTAB *dp; { int func; int ret; func = dp->dt_probe->n_value; if (debug) { char line[80]; if (func) printf("ucall %o(PSL_BR0, %o, 0):", func, dp->dt_addr); else printf("probe %s:", dp->dt_name); fputs(" return conf_int:",stdout); gets(line); sscanf(line, "%o%o", &ret, &conf_int); return ret; } stuff(0, int_nl->n_value); /* Clear conf_int */ /* * use the kernel's probe routine if it exists, * otherwise use our internal probe. Pass it the first (lowest) * vector assigned to the device. */ if (func) { errno = 0; ret = ucall(PSL_BR0, func, dp->dt_addr, dp->dt_vector); if (errno) perror("ucall"); return(ret); } return((*(dp->dt_uprobe))(dp->dt_addr, dp->dt_vector)); } set_unused() { int addr; if (debug) return; if (sep_nl->n_value) { /* * On non-separate I/D kernel, attempt to set up catcher * at 0 for both jumps and traps to 0. * On traps, set PS for randomtrap, with the catcher at 0444. * On jumps, branch to 0112, then to 050 (where there's space). * The handler at 50 is already in place. * The funny numbers are due to attempts to find vectors * that are unlikely to be used, and the need for the value * at 0 to serve as both a vector and an instruction * (br 112 is octal 444). */ if ((grab(0110) == bad_nl->n_value) && (grab(0112) == PSL_BR7) && (grab(0444) == bad_nl->n_value) && (grab(0446) == PSL_BR7)) { stuff(0444, 0); /* br 0112 */ stuff(PSL_BR7 + T_ZEROTRAP, 2); stuff(trap_nl->n_value, 0110); /* trap; 756 (br7+14.)*/ stuff(0756, 0112); /* br 050 */ stuff(0137, 0444); /* jmp $*trap */ stuff(trap_nl->n_value, 0446); } } for (addr = 0; addr < end_vector->n_value; addr += IVSIZE) { if (grab(addr) != bad_nl->n_value) continue; write_vector(addr, trap_nl->n_value, PSL_BR7+T_RANDOMTRAP); } }