chris@umcp-cs.UUCP (Chris Torek) (08/15/85)
>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@umcp-cs ARPA: chris@maryland