Could some kind sole explain cvec in autoconf to me.
Chris Torek
chris at mimsy.UUCP
Wed Mar 15 01:27:36 AEST 1989
Well, I am no fish, but ...:
In article <18695 at adm.BRL.MIL> kadmon!jason at mtxinu.com (Jason Venner) writes:
>How is the particular cvec value determined?
>How does the system associate an interupt by device X with the cvex
>computed at autoconf time?
>
>BSD/Ultrix?
As you can see by the date (1985), the instant replay below does not
apply to Ultrix. (As far as I know, however, the only change is that
`br' and `cvec' are global variables instead of registers, and
fixctlrmask() is no more.) As for the assocation process, why, that is
simplicity itself: Initially, all uba interrupts N (for N in [0.127])
vector to the N'th catcher in the sequence
_catcher:
pushr $0x3f # save r0,r1,r2,r3,r4,r5
jsb _Xustray # push pc and branch
# these two together are exactly 8 bytes
pushr $0x3f; jsb _Xustray # do it again
... repeats 126 more times ...
_Xustray:
blbc _cold,outta_here # error if not still configuring
/*
* Autoconfiguration: figure out which vector actually occurred,
* and at what priority.
*/
mfpr $IPL,_br # or r11: interrupt level
subl3 $_catcher+8,(sp)+,r0 # saved pc - catcher - 8
ashl $-1,r0,_cvec # or r10: vector (after divide by 2)
popr $0x3f # restore everyone
rei # and return
so whatever interrupt happens is recorded. unifind() sets cvec to
01000 (0x200) and checks to make sure that, after the probe routine
returns, cvec is not 01000 nor 0. (Note the `extern quad catcher[128]'
at the top of unifind() in /sys/vax/autoconf.c. This was `extern int
catcher[256]' in 4.2BSD; but I changed it, partly to make the compiler
use movaq, but mostly to fit my twisted sense of semantics. I got
lint's Locore.c edition of copyout() changed for the same reason....)
And now, history repeats. . . .
Date: Wed, 14 Aug 85 18:12:58 edt
Newsgroups: net.unix-wizards
Subject: Interrupt vectors (Re: help -- 785 won't boot)
References: <177 at pwa-b.UUCP>
Lesson for the week on 4BSD interrupt vector assignment during
autoconfiguration:
Initially, in unifind(), uba_hd[uban].uh_ivec is set to 0x200
(01000; this is the first address outside of the interrupt vector
space on a UBA). This basically means that on UBA adapter # uban,
all the high vector addresses are unused. When a device with
programmable interrupt vectors is found, its vector is chosen with
the expression
cvec = (uba_hd[uban].uh_ivec -= K * 4);
Where uban is the index # of the UBA, and K is the number of
interrupt vectors on the device. This sets cvec to the base address
of K interrupt vectors on the given UBA (each one being 4 bytes
wide). At the same time it decrements the uh_ivec field so that
the next such device will get its vectors right below the ones
just assigned.
By the way, `br' and `cvec' (the magic variables in every probe
routine) are `global register variables' for the duration of UBA
configuration. They are not saved and restored by probe routines,
due to a little routine called `fixctlrmask', which turns off the
bits in the `calls' register mask that say to save r11 and r10.
A device probe routine is simply supposed to verify that the device
seems to be there, then attempt to cause it to interrupt. If/when
it does interrupt, the handler that is invoked sets r11 and r10
based on the actual interrupt vector used by the hardware. The
probe routine can simply return after the interrupt `should have
happened'; unifind then looks at cvec to see if it has been set (it
is reset to 0x200 before each probe); if so, br and cvec tell which
interrupt vector the device actually uses, and that vector is
subsequently mapped to the device's interrupt routine (through a
bit of assembly code, generated automatically by /etc/config and
written to ubglue.s).
If the device has multiple interrupt vectors, the probe routine
typically attempts to invoke the lowest-address one. If this is
not convenient, one can write, e.g.,
/* device foo has two interrupt vectors, and we can only
make it interrupt at the upper one easily: */
addr->foo_csr = FOO_IE|FOO_INT; /* cause an interrupt */
DELAY(1000); /* wait for it to happen */
if (cvec && cvec != 0x200) /* if it happened, */
cvec -= 4; /* convert cvec to the lower # */
Whenever a device has multiple interrupt vectors, autoconf expects
cvec to hold the address of the `first' (numerically smallest)
of these, and assigns the remainder sequentially by adding 4 each
time. Autoconf can tell how many interrupt vectors there are by
examining the ubdinit `ui_intr' field, which is a pointer to the
first of the set of interrupt vectors; the set is terminated by a
null pointer. For example, a DH, with receive and trasmit vectors,
generates a list that looks like this:
int (*dhint0[])() = { Xdhrint0, Xdhxint0, 0 };
(these lists are found in ioconf.c, which is generated by /etc/config
based on the information found in the configuration file). One of
the elements of ubdinit will then have a ui_intr field whose value
is the address of `dhint0'.
In particular, all of this means that there are only two places
in the kernel that have any knowledge about the form of interrupt
vectors for a particular device: the configuration file (which
lists all the interrupt vectors in order) and the device driver
(which must invoke the first of the vectors, or correct for the
number of vectors between the one invoked and the first). This
makes configuration of new devices relatively simple. All one
needs to do is write the driver, add it to the list of `kernel
source files' (conf/files and conf/files.vax), enter it into the
configuration file, and build a new kernel using config and make.
No assembly code need be written. (If the device is to be accessed
via a character or block special device, then vax/conf.c must also
be augmented as well.)
-----
Next week: the potato chip driver, or how to keep your wizard
properly fed during hack sessions. :-)
--
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain: chris at mimsy.umd.edu Path: uunet!mimsy!chris
More information about the Comp.unix.wizards
mailing list