.de EX .DS .ft CW .. .de EE .ft .DE .. .de CW \&\\$3\f(CW\\$1\\fP\\$2 .. .de CP .IP "\&\\$3\f(CW\\$1\\fP\\$2" .7i .. .de Bh .NH .. .de Sh .SH .. .Bh What .CW mkconf gets .PP There are three interesting files read: one describing the devices and drivers that may be configured, one describing the system control block, and one listing the pieces to be configured into the particular system being built. .PP General rules: all the files contain lines of text, which are broken into fields separated by white space. .CW # introduces a comment; the remainder of the line is ignored. When a field contains a number, it is interpreted as hexadecimal if introduced by .CW 0x , octal if by .CW 0 , decimal otherwise. .Sh .CW devs .PP The master list of devices and drivers lives in a file called .CW devs . This contains one line for each device, driver, or driver-like entity that may be added to the system. Each line is of the form .EX name tag things .EE .PP .CW Name is the name of the thing described. It need have nothing to do with filenames, driver entry points, or anything else; it is used only by .CW mkconf . .PP .CW Tag is a string known by the driver, prepended to entry points in (e.g.) .CW conf.c . .PP .CW Things are various bits of information: which table the driver should be entered in, what sort of addressing this device requires, and some miscellaneous hacks. They are encoded as words, sometimes followed by other words. .PP Tables are encoded by .CP bdev Block device driver; enter in .CW bdevsw . .CP cdev Character device driver; enter in .CW cdevsw . .CP fs File system driver; enter in .CW fstypsw . .CP ld Stream line discipline; enter in .CW streamtab . .LP Each of these should be followed by a number, which is used as the index in the table; e.g. .CW "bdev 3" means the block device with major number 3. It's perfectly acceptable to enter a driver in several tables (e.g. block and character device) or in no table at all; device addresses and the like are declared anyway if appropriate. .PP Address type words are .CP sbi This is an old-fashioned (SBI or CMI) nexus device. .CP vaxbi This is a VAXBI device. .CP mb This is a MASSBUS device. .CP ub This is a UNIBUS device. .CP sub This is a subdevice (like a disk drive connected to a controller). .CP count This device has no address, but needs to know its count anyway. (Perhaps there are data structures.) .LP Again, it's permissable not to specify any address type at all; this simply means .CW mkconf won't bother to print an address. .PP Some words indicate that the device is an adapter into which other devices (with one of the address types above) are plugged: .CP sbia SBI adapter. .CP bia VAXBI adapter .CP mba MASSBUS adapter. .CP uba UNIBUS adapter. .LP By preference, new adapter types should be named by appending .CW a to the corresponding address name. .PP Other hacks: .CP vec " n" Vector size. Devices are generally assumed to have one interrupt vector (if any, depending on address type). .CW "vec 3" means the device really has three vectors, presumably contiguous. .CP rep " n" Repeat count. .CW "rep 8" means there are really 8 devices per device controller; .CW mkconf should scale counts by 8. .PP The magic word .CW strs introduces a list of data structures associated with this device, named by the remaining words on the line. Obviously, this flag must be last. .Sh scb .PP Annoyingly, different VAXes have different low-level traps in the system control block. Hence, for each kind of VAX (or at least each variant kind of SCB) there is an SCB template file. .PP The file contains lines like .EX offset type name label .EE meaning that traps to the given byte .CW offset are to be vectored to .CW name . The name is just a label in some assembly language routine to which control should be transferred on the trap with some arbitrary trap-dependent stuff on the stack. It should be longword-aligned, as the low-order bits of the vector are meaningful to the VAX. .CW Type is .CW t for traps that should be taken on the kernel stack, .CW i for those that should be on the interrupt stack. .PP It would be nice to eliminate this file entirely, or at least cause there to be only one. There must be something somewhere as most of the system control block consists of I/O vectors, which .CW mkconf must generate, or placeholders for stray interrupts, which are best generated by a program. .Sh conf .PP This is the file that describes what's to be configured into a particular copy of the system. Each real device (individual object with an address) takes up one line. Each driver-like thing (such as a line discipline) takes up a line too. .PP Lines look like .EX name id addr ... .EE .CW Name is one of the ones in the .CW devs file. .CW Id is a sequence number. It is used as an index into data structures (like the address) generated by .CW mkconf . For honest devices (that have entries in .CW /dev ) the .CW id is probably related in some simple way to the minor device number; for devices that exist only as places to plug in other devices (such as MASSBUS adapters and UNIBUS disk controllers), the .CW id is part of the address for devices so plugged. .CW Mkconf uses the .CW id to connect devices to their adapters, to build tables of addresses, and to decide how many devices there are (the highest .CW id plus one, multiplied by the repeat count). .PP Syntactically, the address stuff is just a handful of blank-separated numbers whose interpretation depends on the address type of the device: .IP SBI devices take an adapter number, a nexus (TR) number, and perhaps a vector base. The adapter number is useful when there's more than one bus, is 0 otherwise. The vector base is used for things like UNIBUS adapters. .IP VAXBI devices take an adapter number, a node (TR) number, a vector, and perhaps a vector base. .IP MASSBUS devices take an adapter number (the .CW id of the corresponding RH device) and a unit number (the controller number on the MASSBUS). .IP UNIBUS devices take an adapter number (the .CW id of the UNIBUS adapter), a UNIBUS address, and a vector. Both the address and the vector are relative to that UNIBUS. .IP Subdevices take a controller number and a unit number. .PP Devices that take only a count are a special case: the .CW id is really the count, and there is no address (or more exactly, the address is ignored). .Bh What .CW mkconf puts out .PP .CW Mkconf writes two files: one with interrupt vectors, one with tables and address stuff. .Sh scb.s .PP .CW Mkconf writes one assembly language file containing the SCB, stray interrupt catching hooks, and interface routines to call interrupt handlers written in C. .PP Traps described in the SCB template file are put out as entered. Other interrupts are placed as described by the vectors in the configuration file. .CW Mkconf doesn't know anything about, say, the peculiarities of comet UNIBUS adapters; it's up to the person writing the .CW conf file to get it right (in this case by saying that the UNIBUS adapters have vector bases at 0x200 and 0x400). All I/O interrupts are placed on the interrupt stack. .PP SCB slots with nothing assigned are filled in with stray catchers. In fact, they are vectored into tables of code of the form .EX straycatch: .align 2; bsbw stray # here on stray interrupt from offset 0 .align 2; bsbw stray # offset 4 .align 2; bsbw stray # and so on . . . .EE thus doubling the size of the SCB (each page of vectors requires a page of stray catchers), but making it possible to see where strays come from. .PP .CW mkconf only writes the tables; .CW stray is expected to be provided elsewhere in the system. A plausible implementation might look like .EX stray: pushr $0x3f # save some registers subl3 $straycatch,7*4(sp),-(sp) # get offset of stray calls $1,_strayintr popr $0x3f tstl (sp)+ rei .EE .PP After the SCB, .CW mkconf writes a handful of interface routines of the form .EX Xdz31: pushr $0x3f pushl $3 # dz3 calls $1,_dz1int # call second interrupt popr $3 rei .EE Device interrupt routines should be named .CW xx0int , .CW xx1int , and so on for successive vectors, where .CW xx is the tag for that device driver. Even if there's only one interrupt vector, the interrupt routine is named .CW xx0int . .Sh conf.c .PP There are tables, and per-device data structures. .PP The tables may be summarized as follows: .EX struct bdevsw *bdevsw[]; /* block devices */ struct cdevsw *cdevsw[]; /* character devices */ struct fstypsw *fstypsw[]; /* file systems */ struct streamtab *streamtab[]; /* line disciplines */ int nblkdev, nchrdev, nfstyp, nstreamtab; /* number of entries */ .EE The names need to be slightly straightened out for consistency. .PP Note that the tables contain pointers to entry point tables, not the entry points themselves. Empty slots in the tables contain .CW NULL pointers. .PP A device driver is expected to define an appropriate table entry for each table it is entered in. The table entry should be named .CW xxbdev , .CW xxcdev , .CW xxfs , or .CW xxstream for the driver with tag .CW xx . .PP Most drivers will expect .CW conf.c to define some data for them as well. A count of configured devices will be defined: .EX int xxcnt; .EE The count is scaled by the repeat count, if any; e.g. declaring three DZ11s will probably cause .CW dzcnt to contain 24. .PP If the driver expects some address information, it will be defined with the name .CW xxaddr . The type of the data and the meaning of its contents depends on the kind of device; see the section on addresses. .PP If the driver requested any structure definitions, they will be there as well: .EX struct gumby xxgumby[N]; .EE where .CW N is the same number as in .CW xxcnt . .PP Things to think about: it would be nice to declare, e.g., inode table. inode device with empty tag (never mind the syntax), but how do you get the count out? .Bh How devices are addressed .Sh The hardware model .PP There are buses, to which adapters are attached. Older VAXes have only one bus apiece (the 780's SBI, the 750's CMI); newer ones can have several (two SBIs on the 8600, up to four VAXBIs on 8800-like machines). .PP Each bus contains some number of nexus and some number of address windows. A nexus is a space of 0x2000 bytes, some of which are registers for the device at that nexus. Sometimes the `device' is really a peripheral (especially VAXBI devices, but things like the DR780 as well), but on older machines it is usually an adapter to some other kind of bus (UNIBUS or MASSBUS). UNIBUS adapters (and perhaps other adapters too) need to allow the VAX to get at the address space they control; hence the address windows. .PP The first few registers in a nexus are usually well-defined, but only for devices in the same bus (e.g. SBI and VAXBI differ noticeably). The low-order part of the first longword is generally supposed to contain some sort of nexus type code, but even this is sometimes violated. In general, within a nexus, all rules are off. For example, MASSBUS adapters use part of their nexus space for an array of eight sets of device registers for each of eight possible multiplexed devices; UNIBUS adapters have registers controlling data paths; memory controllers just have error flags. .PP Interrupt vectors are more poorly defined. In some buses (CMI, SBI), nexus interrupts are vectored to well-known places in the system control block, determined by the interrupt priority and the nexus address. In others (VAXBI), the vector is partly or wholly up to the software. UNIBUS adapters steal a whole page somewhere for UNIBUS interrupts; sometimes (CMI) this is in a hard-wired place, sometimes (VAXBI) it must be set by software. .Sh The software model .PP The fundamental address unit is the nexus. There is an array of nexus defined in the system virtual address space. It would be nice if the array could be at a fixed address, but it's too hard to do. Hence there's just a pointer to its base which is initialized at boot time: .EX struct nexus { char nx[0x2000]; char wind[0x40000]; } *nexus; /* who needs counts? */ int nexus_cnt = N; .EE .PP The array bears no necessary resemblance to the layout of nexus spaces in physical I/O space; indices into .CW nexus are dreamt up by .CW mkconf . The appropriate mappings are set up by machine-dependent startup code, according to tables generated by .CW mkconf : .EX /* * example: a typical 780 */ struct nextab { char bus; char nex; }; struct nextab nextab[] = { {0, 1}, /* ms780 */ {0, 8}, /* first rh780 */ {0, 3}, /* unibus adapter */ -1 /* end */ }; .EE Bus numbers are defined in a machine-dependent, presumably straightforward way; nexus numbers are within the relevant bus. It is assumed that the mapping between the bus and nexus numbers and the corresponding address window can be doped out by the machine-dependent startup code. This is utterly straightforward in the VAXBI world. Elsewhere it's a couple of simple special cases (UNIBUS adapters have a known mapping, other devices just don't get windows). .PP Why the configuration-dependent mapping between real nexus numbers and positions in the .CW nexus array? Implementation horrors: each nexus takes up 8kb of address space for its registers, and 256Kb for its window. Address space isn't in short supply on the VAX, but huge amounts of address space in the system take up large quantities of physical memory for system page table entries. Hence it's nicer only to map the nexus that matter. The address windows are the bulk of the issue, which is particularly annoying because relatively few devices use them. In practice, older machines have relatively few nexus (with or without windows), and newer (VAXBI) systems have a little more memory to waste, so always mapping a (perhaps nonexistent) address window isn't that great a burden (yet). .PP Interrupt vectors are harder to understand. In older VAXes, nexus interrupts go through vectors in the second half of the first page of the system control block. UNIBUS interrupts are handled in software (780) or put into specific higher-numbered SCB pages. In the 8600, this is further complicated by having two SBIs, hence two pages of nexus interrupts. In the VAXBI machines, things are looser; the vectors are really set in software, though there are prefixes (to point UNIBUS interrupts or interrupts from a specific BI bus to a particular page) and arcane rules about the prefixes. .PP .CW mkconf knows nothing about this mess. In general, it's necessary to specify the interrupt vector in the configuration file. The fixed relationship between nexus number and vector for SBI devices is understood; for VAXBI devices, the vector must always be explicit. In either case, devices like UNIBUS adapters and NMI-BI adapters that have associated pages of interrupts from the buses they control require that the pages be named explicitly. .Sh What drivers are told .PP Device drivers need to know the address of the I/O registers interesting to them, perhaps their interrupt vector and priority (the latter is set in software on the VAXBI!), and perhaps a cookie identifying the bus or adapter to which their device is connected. Different data structures are used to pass this information for different kinds of bus. .PP Unfortunately, it's hard to specify the address exactly at compile time; in practice it is given as a nexus index and perhaps some kind of offset. I/O adapters like the UNIBUS adapter are expected to provide routines that map address structures into virtual addresses in I/O space; details are elsewhere. .PP SBI devices get .EX struct nxaddr { short nexus; /* nexus number */ short adno; /* adapter number */ }; .EE `Devices' here includes I/O adapters. Interrupt vectors are assumed to be those implied by the nexus number; interrupts for all four BR levels are sent to the same single interrupt routine. If .CW nexus is \-1, the device doesn't exist (e.g. device 1 is configured but device 0 isn't). .PP MASSBUS devices get .EX struct mbaddr { char unit; /* MASSBUS unit number */ char mbno; /* adapter number */ }; .EE .PP The device doesn't exist if .CW unit is \-1. The MASSBUS adapter driver will probably want to supply entry points yielding both the address of the device registers proper (determined by the unit number) and just the MASSBUS registers (to check for errors after a transfer). .PP What do we do about interrupt vectors? A MASSBUS can only have one transfer going at a time, but when that transfer completes there can be several devices needing attention. Pretty clearly the vectors in the SCB need to point to the MASSBUS adapter code, not that for the device on the MASSBUS; the mba driver has to have some way to forward the interrupts on to the correct driver, though. Maybe some dynamic registry is appropriate; consider it to be like mapping. .PP UNIBUS devices get .EX struct ubaddr { long uboff; /* UNIBUS space address */ short vec; /* unibus vector, not scb */ short ubno; /* UNIBUS adapter number; implies nexus */ }; .EE .PP If .CW uboff is zero, the device doesn't exist. There are already devices that need the vector (UDA50); there will doubtless be more. .PP VAXBI devices probably need .EX struct biaddr { short nexus; /* BI device nexus */ short vec; /* error interrupt */ short ovec; /* controlled devices */ short adno; /* adapter, e.g. NBI */ }; .EE If .CW nexus is \-1, the device doesn't exist. .CW Vec is the vector for the BI device itself; .CW ovec is the base of the page of interrupts belonging to the device, if it's a bus adapter like the DWBUA. .CW Vec is always the vector used for BIIC-generated errors. If the device wants to have a separate interrupt for I/O completion (using, say, the UIR), it should be described as having multiple vectors, which will have consecutive addresses. The interrupt priority level is set in software for some VAXBI devices, but this might as well be a manifest in the driver as it's rather inconvenient for it not to be a known constant (e.g. for .CW spl calls). .PP There are also funny `subdevices', which are addressed merely as sub-pieces of some controller. The usual example is disk drives on a UNIBUS controller; one needs to know about the controllers as entities, but also about the individual drives. These get .EX struct subaddr { short ctl; /* controller number */ short unit; /* unit number within the controller */ }; .EE where ctl and unit are in a language known only to the driver. If .CW ctl is \-1, the device doesn't exist.