Interrupt vectors (Re: help -- 785 won't boot)
Chris Torek
chris at umcp-cs.UUCP
Thu Aug 15 08:13:08 AEST 1985
>There is a 'funny' on bootup -- the vectors it shows are out to
>lunch. The uda (ra81 interface) shows a vector of 774 (it's really
>154), but the disk SEEMS to work fine. The dmf shows a vector of
>740 (it's really 310). What gives ??
This is OK. The UDA50 and the DMF32 both have "programmable
interrupt vectors"; the CPU picks the actual interrupt vector.
(Note that vectors are different from csr addresses; the latter
are indeed fixed). A uda50 requires only one interrupt vector,
and it is probed first, so it gets 0774 (which is really 01000 -
sizeof (int (*)())). A dmf has 7 interrupt vectors, so it gets
0740 (0774 - 7 * sizeof (int (*)())).
(I don't know why your tty ports aren't working, though.)
-----
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's 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 4251)
UUCP: seismo!umcp-cs!chris
CSNet: chris at umcp-cs ARPA: chris at maryland
More information about the Comp.unix.wizards
mailing list