V10/lsys/mkconf/hum

.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.