xref: /original-bsd/usr.sbin/config/SMM.doc/6.t (revision c6ddb5f9)
Copyright (c) 1983 The Regents of the University of California.
All rights reserved.

%sccs.include.redist.roff%

@(#)6.t 6.3 (Berkeley) 04/17/91

.ds RH "Adding New Devices
ADDING NEW SYSTEM SOFTWARE

This section is not for the novice, it describes some of the inner workings of the configuration process as well as the pertinent parts of the system autoconfiguration process. It is intended to give those people who intend to install new device drivers and/or other system facilities sufficient information to do so in the manner which will allow others to easily share the changes.

This section is broken into four parts:

\(bu 3
general guidelines to be followed in modifying system code,
\(bu 3
how to add non-standard system facilities to 4.3BSD,
\(bu 3
how to add a device driver to 4.3BSD, and
\(bu 3
how UNIBUS device drivers are autoconfigured under 4.3BSD on the VAX. Modifying system code

If you wish to make site-specific modifications to the system it is best to bracket them with #ifdef SITENAME ... #endif to allow your source to be easily distributed to others, and also to simplify diff\|(1) listings. If you choose not to use a source code control system (e.g. SCCS, RCS), and perhaps even if you do, it is recommended that you save the old code with something of the form: #ifndef SITENAME ... #endif We try to isolate our site-dependent code in individual files which may be configured with pseudo-device specifications.

Indicate machine-specific code with ``#ifdef vax'' (or other machine, as appropriate). 4.2BSD underwent extensive work to make it extremely portable to machines with similar architectures- you may someday find yourself trying to use a single copy of the source code on multiple machines.

Use lint periodically if you make changes to the system. The 4.3BSD kernel has only two lines of lint in it. It is very simple to lint the kernel. Use the LINT configuration file, designed to pull in as much of the kernel source code as possible, in the following manner. $ cd /sys/conf $ mkdir ../LINT $ config LINT $ cd ../LINT $ make depend $ make assym.s $ make -k lint > linterrs 2>&1 & (or for users of csh\|(1)) % make -k >& linterrs This takes about an hour on a lightly loaded VAX-11/750, but is well worth it. Adding non-standard system facilities

This section considers the work needed to augment config 's data base files for non-standard system facilities. Config uses a set of files that list the source modules that may be required when building a system. The data bases are taken from the directory in which config is run, normally /sys/conf. Three such files may be used: files , files .machine, and files .ident. The first is common to all systems, the second contains files unique to a single machine type, and the third is an optional list of modules for use on a specific machine. This last file may override specifications in the first two. The format of the files file has grown somewhat complex over time. Entries are normally of the form


dir/source.c type option-list modifiers

for example,


vaxuba/foo.c optional foo device-driver

The type is one of standard or optional . Files marked as standard are included in all system configurations. Optional file specifications include a list of one or more system options that together require the inclusion of this module. The options in the list may be either names of devices that may be in the configuration file, or the names of system options that may be defined. An optional file may be listed multiple times with different options; if all of the options for any of the entries are satisfied, the module is included.

If a file is specified as a device-driver , any special compilation options for device drivers will be invoked. On the VAX this results in the use of the -i option for the C optimizer. This is required when pointer references are made to memory locations in the VAX I/O address space.

Two other optional keywords modify the usage of the file. Config understands that certain files are used especially for kernel profiling. These files are indicated in the files files with a profiling-routine keyword. For example, the current profiling subroutines are sequestered off in a separate file with the following entry:


sys/subr_mcount.c optional profiling-routine

The profiling-routine keyword forces config not to compile the source file with the -pg option.

The second keyword which can be of use is the config-dependent keyword. This causes config to compile the indicated module with the global configuration parameters. This allows certain modules, such as machdep.c to size system data structures based on the maximum number of users configured for the system. Adding device drivers to 4.3BSD

The I/O system and config have been designed to easily allow new device support to be added. The system source directories are organized as follows:

/sys/h machine independent include files
/sys/sys machine-independent system source files
/sys/conf site configuration files and basic templates
/sys/net network-protocol-independent, but network-related code
/sys/netinet DARPA Internet code
/sys/netimp IMP support code
/sys/netns Xerox NS code
/sys/vax VAX-specific mainline code
/sys/vaxif VAX network interface code
/sys/vaxmba VAX MASSBUS device drivers and related code
/sys/vaxuba VAX UNIBUS device drivers and related code

Existing block and character device drivers for the VAX reside in ``/sys/vax'', ``/sys/vaxmba'', and ``/sys/vaxuba''. Network interface drivers reside in ``/sys/vaxif''. Any new device drivers should be placed in the appropriate source code directory and named so as not to conflict with existing devices. Normally, definitions for things like device registers are placed in a separate file in the same directory. For example, the ``dh'' device driver is named ``dh.c'' and its associated include file is named ``dhreg.h''.

Once the source for the device driver has been placed in a directory, the file ``/sys/conf/files.machine'', and possibly ``/sys/conf/devices.machine'' should be modified. The files files in the conf directory contain a line for each C source or binary-only file in the system. Those files which are machine independent are located in ``/sys/conf/files,'' while machine specific files are in ``/sys/conf/files.machine.'' The ``devices.machine'' file is used to map device names to major block device numbers. If the device driver being added provides support for a new disk you will want to modify this file (the format is obvious).

In addition to including the driver in the files file, it must also be added to the device configuration tables. These are located in ``/sys/vax/conf.c'', or similar for machines other than the VAX. If you don't understand what to add to this file, you should study an entry for an existing driver. Remember that the position in the device table specifies the major device number. The block major number is needed in the ``devices.machine'' file if the device is a disk.

With the configuration information in place, your configuration file appropriately modified, and a system reconfigured and rebooted you should incorporate the shell commands needed to install the special files in the file system to the file ``/dev/MAKEDEV'' or ``/dev/MAKEDEV.local''. This is discussed in the document ``Installing and Operating 4.3BSD on the VAX''. Autoconfiguration on the VAX

4.3BSD requires all device drivers to conform to a set of rules which allow the system to:

1)
support multiple UNIBUS and MASSBUS adapters,
2)
support system configuration at boot time, and
3)
manage resources so as not to crash when devices request resources which are unavailable.

In addition, devices such as the RK07 which require everyone else to get off the UNIBUS when they are running need cooperation from other DMA devices if they are to work. Since it is unlikely that you will be writing a device driver for a MASSBUS device, this section is devoted exclusively to describing the I/O system and autoconfiguration process as it applies to UNIBUS devices.

Each UNIBUS on a VAX has a set of resources:

\(bu
496 map registers which are used to convert from the 18-bit UNIBUS addresses into the much larger VAX memory address space.
\(bu
Some number of buffered data paths (3 on an 11/750, 15 on an 11/780, 0 on an 11/730) which are used by high speed devices to transfer data using fewer bus cycles.

There is a structure of type struct uba_hd in the system per UNIBUS adapter used to manage these resources. This structure also contains a linked list where devices waiting for resources to complete DMA UNIBUS activity have requests waiting.

There are three central structures in the writing of drivers for UNIBUS controllers; devices which do not do DMA I/O can often use only two of these structures. The structures are struct uba_ctlr, the UNIBUS controller structure, struct uba_device the UNIBUS device structure, and struct uba_driver, the UNIBUS driver structure. The uba_ctlr and uba_device structures are in one-to-one correspondence with the definitions of controllers and devices in the system configuration. Each driver has a struct uba_driver structure specifying an internal interface to the rest of the system.

Thus a specification controller sc0 at uba0 csr 0176700 vector upintr would cause a struct uba_ctlr to be declared and initialized in the file ioconf.c for the system configured from this description. Similarly specifying disk up0 at sc0 drive 0 would declare a related uba_device in the same file. The up.c driver which implements this driver specifies in its declarations: int upprobe(), upslave(), upattach(), updgo(), upintr(); struct uba_ctlr *upminfo[NSC]; struct uba_device *updinfo[NUP]; u_short upstd[] = { 0776700, 0774400, 0776300, 0 }; struct uba_driver scdriver = { upprobe, upslave, upattach, updgo, upstd, "up", updinfo, "sc", upminfo }; initializing the uba_driver structure. The driver will support some number of controllers named sc0, sc1, etc, and some number of drives named up0, up1, etc. where the drives may be on any of the controllers (that is there is a single linear name space for devices, separate from the controllers.)

We now explain the fields in the various structures. It may help to look at a copy of vaxuba/ubareg.h, vaxuba/ubavar.h and drivers such as up.c and dz.c while reading the descriptions of the various structure fields.

uba_driver structure

One of these structures exists per driver. It is initialized in the driver and contains functions used by the configuration program and by the UNIBUS resource routines. The fields of the structure are:

ud_probe
A routine which, given a caddr_t address as argument, should attempt to determine that the device is present at that address in virtual memory, and should cause an interrupt from the device. When probing controllers, two additional arguments are supplied: the controller index, and a pointer to the uba_ctlr structure. Device probe routines receive a pointer to the uba_device structure as second argument. Both of these structures are described below. Neither is normally used, but devices that must record status or device type information from the probe routine may require them.

The autoconfiguration routine attempts to verify that the specified address responds before calling the probe routine. However, the device may not actually exist or may be of a different type, and therefore the probe routine should use delays (via the DELAY(n) macro which delays for n microseconds) rather than waiting for specific events to occur. The routine must not declare its argument as a register parameter, but must declare register int br, cvec; as local variables. At boot time the system takes special measures that these variables are ``value-result'' parameters. The br is the IPL of the device when it interrupts, and the cvec is the interrupt vector address on the UNIBUS. These registers are actually filled in in the interrupt handler when an interrupt occurs.

As an example, here is the up.c probe routine: upprobe(reg) caddr_t reg; { register int br, cvec; #ifdef lint br = 0; cvec = br; br = cvec; upintr(0); #endif ((struct updevice *)reg)->upcs1 = UP_IE|UP_RDY; DELAY(10); ((struct updevice *)reg)->upcs1 = 0; return (sizeof (struct updevice)); } The definitions for lint serve to indicate to it that the br and cvec variables are value-result. The call to the interrupt routine satisfies lint that the interrupt handler is used. The cod here enable interrupts on the device and write the ready bit UP_RDY. The 10 microsecond delay insures that the interrupt enable will not be canceled before the interrupt can be posted. The return of ``sizeof (struct updevice)'' here indicates that the probe routine is satisfied that the device is present (the value returned is not currently used, but future plans dictate that you should return the amount of space in the device's register bank). A probe routine may use the function ``badaddr'' to see if certain other addresses are accessible on the UNIBUS (without generating a machine check), or look at the contents of locations where certain registers should be. If the registers contents are not acceptable or the addresses don't respond, the probe routine can return 0 and the device will not be considered to be there.
One other thing to note is that the action of different VAXen when illegal addresses are accessed on the UNIBUS may differ. Some of the machines may generate machine checks and some may cause UNIBUS errors. Such considerations are handled by the configuration program and the driver writer need not be concerned with them.
It is also possible to write a very simple probe routine for a one-of-a-kind device if probing is difficult or impossible. Such a routine would include statements of the form: br = 0x15; cvec = 0200; for instance, to declare that the device ran at UNIBUS br5 and interrupted through vector 0200 on the UNIBUS.
ud_slave
This routine is called with a uba_device structure (yet to be described) and the address of the device controller. It should determine whether a particular slave device of a controller is present, returning 1 if it is and 0 if it is not. As an example here is the slave routine for up.c. upslave(ui, reg) struct uba_device *ui; caddr_t reg; { register struct updevice *upaddr = (struct updevice *)reg; upaddr->upcs1 = 0; /* conservative */ upaddr->upcs2 = ui->ui_slave; if (upaddr->upcs2 & UPCS2_NED) { upaddr->upcs1 = UP_DCLR | UP_GO; return (0); } return (1); } Here the code fetches the slave (disk unit) number from the ui_slave field of the uba_device structure, and sees if the controller responds that that is a non-existent driver (NED). If the drive is not present, a drive clear is issued to clean the state of the controller, and 0 is returned indicating that the slave is not there. Otherwise a 1 is returned.
ud_attach
The attach routine is called after the autoconfigure code and the driver concur that a peripheral exists attached to a controller. This is the routine where internal driver state about the peripheral can be initialized. Here is the attach routine from the up.c driver: upattach(ui) register struct uba_device *ui; { register struct updevice *upaddr; if (upwstart == 0) { timeout(upwatch, (caddr_t)0, hz); upwstart++; } if (ui->ui_dk >= 0) dk_mspw[ui->ui_dk] = .0000020345; upip[ui->ui_ctlr][ui->ui_slave] = ui; up_softc[ui->ui_ctlr].sc_ndrive++; ui->ui_type = upmaptype(ui); } The attach routine here performs a number of functions. The first time any drive is attached to the controller it starts the timeout routine which watches the disk drives to make sure that interrupts aren't lost. It also initializes, for devices which have been assigned iostat numbers (when ui->ui_dk >= 0), the transfer rate of the device in the array dk_mspw, the fraction of a second it takes to transfer 16 bit word. It then initializes an inverting pointer in the array upip which will be used later to determine, for a particular up controller and slave number, the corresponding uba_device. It increments the count of the number of devices on this controller, so that search commands can later be avoided if the count is exactly 1. It then attempts to decipher the actual type of drive attached to the controller in a controller-specific way. On the EMULEX SC-21 it may ask for the number of tracks on the device and use this to decide what the drive type is. The drive type is used to setup disk partition mapping tables and other device specific information.
ud_dgo

This is the routine which is called by the UNIBUS resource management routines when an operation is ready to be started (because the required resources have been allocated). The routine in up.c is: updgo(um) struct uba_ctlr *um; { register struct updevice *upaddr = (struct updevice *)um->um_addr; upaddr->upba = um->um_ubinfo; upaddr->upcs1 = um->um_cmd|((um->um_ubinfo>>8)&0x300); } This routine uses the field um_ubinfo of the uba_ctlr structure which is where the UNIBUS routines store the UNIBUS map allocation information. In particular, the low 18 bits of this word give the UNIBUS address assigned to the transfer. The assignment to upba in the go routine places the low 16 bits of the UNIBUS address in the disk UNIBUS address register. The next assignment places the disk operation command and the extended (high 2) address bits in the device control-status register, starting the I/O operation. The field um_cmd was initialized with the command to be stuffed here in the driver code itself before the call to the ubago routine which eventually resulted in the call to updgo.

ud_addr
This is a zero-terminated list of the conventional addresses for the device control registers in UNIBUS space. This information is used by the system to look for instances of the device supported by the driver. When the system probes for the device it first checks for a control-status register located at the address indicated in the configuration file (if supplied), then uses the list of conventional addresses pointed to be ud_addr.
ud_dname
This is the name of a device supported by this controller; thus the disks on a SC-21 controller are called up0, up1, etc. That is because this field contains up.
ud_dinfo
This is an array of back pointers to the uba_device structures for each device attached to the controller. Each driver defines a set of controllers and a set of devices. The device address space is always one-dimensional, so that the presence of extra controllers may be masked away (e.g. by pattern matching) to take advantage of hardware redundancy. This field is filled in by the configuration program, and used by the driver.
ud_mname
The name of a controller, e.g. sc for the up.c driver. The first SC-21 is called sc0, etc.
ud_minfo
The backpointer array to the structures for the controllers.
ud_xclu
If non-zero specifies that the controller requires exclusive use of the UNIBUS when it is running. This is non-zero currently only for the RK611 controller for the RK07 disks to map around a hardware problem. It could also be used if 6250bpi tape drives are to be used on the UNIBUS to insure that they get the bandwidth that they need (basically the whole bus).
ud_ubamem
This is an optional entry point to the driver to configure UNIBUS memory associated with a device. If this field in the driver structure is null, it is ignored. Otherwise, it is called before beginning to probe for devices when configuration of a UNIBUS is begun. The driver must probe for the existence of its memory, and is then responsible for allocating the map registers corresponding to the device memory addresses so that the registers are not used for other purposes. The ud_ubamem returns 0 on success and -1 on failure. A return value of 1 indicates that the memory exists, and that there is no further configuration required for the device.
uba_ctlr structure

One of these structures exists per-controller. The fields link the controller to its UNIBUS adapter and contain the state information about the devices on the controller. The fields are:

um_driver
A pointer to the struct uba_device for this driver, which has fields as defined above.
um_ctlr
The controller number for this controller, e.g. the 0 in sc0.
um_alive
Set to 1 if the controller is considered alive; currently, always set for any structure encountered during normal operation. That is, the driver will have a handle on a uba_ctlr structure only if the configuration routines set this field to a 1 and entered it into the driver tables.
um_intr
The interrupt vector routines for this device. These are generated by config and this field is initialized in the ioconf.c file.
um_hd

A back-pointer to the UNIBUS adapter to which this controller is attached.

um_cmd
A place for the driver to store the command which is to be given to the device before calling the routine ubago with the devices uba_device structure. This information is then retrieved when the device go routine is called and stuffed in the device control status register to start the I/O operation.
um_ubinfo
Information about the UNIBUS resources allocated to the device. This is normally only used in device driver go routine (as updgo above) and occasionally in exceptional condition handling such as ECC correction.
um_tab
This buffer structure is a place where the driver hangs the device structures which are ready to transfer. Each driver allocates a buf structure for each device (e.g. updtab in the up.c driver) for this purpose. You can think of this structure as a device-control-block, and the buf structures linked to it as the unit-control-blocks. The code for dealing with this structure is stylized; see the rk.c or up.c driver for the details. If the ubago routine is to be used, the structure attached to this buf structure must be:

\(bu 3
A chain of buf structures for each waiting device on this controller.
\(bu 3
On each waiting buf structure another buf structure which is the one containing the parameters of the I/O operation.
uba_device structure

One of these structures exist for each device attached to a UNIBUS controller. Devices which are not attached to controllers or which perform no buffered data path DMA I/O may have only a device structure. Thus dz and dh devices have only uba_device structures. The fields are:

ui_driver
A pointer to the struct uba_driver structure for this device type.
ui_unit
The unit number of this device, e.g. 0 in up0, or 1 in dh1.
ui_ctlr

The number of the controller on which this device is attached, or -1 if this device is not on a controller.

ui_ubanum
The number of the UNIBUS on which this device is attached.
ui_slave
The slave number of this device on the controller which it is attached to, or -1 if the device is not a slave. Thus a disk which was unit 2 on a SC-21 would have ui_slave 2; it might or might not be up2, that depends on the system configuration specification.
ui_intr

The interrupt vector entries for this device, copied into the UNIBUS interrupt vector at boot time. The values of these fields are filled in by config to small code segments which it generates in the file ubglue.s.

ui_addr
The control-status register address of this device.
ui_dk

The iostat number assigned to this device. Numbers are assigned to disks only, and are small nonnegative integers which index the various dk_* arrays in <sys/dk.h>.

ui_flags
The optional ``flags xxx'' parameter from the configuration specification was copied to this field, to be interpreted by the driver. If flags was not specified, then this field will contain a 0.
ui_alive
The device is really there. Presently set to 1 when a device is determined to be alive, and left 1.
ui_type
The device type, to be used by the driver internally.
ui_physaddr
The physical memory address of the device control-status register. This is typically used in the device dump routines.
ui_mi

A struct uba_ctlr pointer to the controller (if any) on which this device resides.

ui_hd

A struct uba_hd pointer to the UNIBUS on which this device resides.

UNIBUS resource management routines

UNIBUS drivers are supported by a collection of utility routines which manage UNIBUS resources. If a driver attempts to bypass the UNIBUS routines, other drivers may not operate properly. The major routines are: uballoc to allocate UNIBUS resources, ubarelse to release previously allocated resources, and ubago to initiate DMA. When allocating UNIBUS resources you may request that you

NEEDBDP
if you need a buffered data path,
HAVEBDP
if you already have a buffered data path and just want new mapping registers (and access to the UNIBUS),
CANTWAIT
if you are calling (potentially) from interrupt level, and
NEED16
if the device uses only 16 address bits, and thus requires map registers from the first 64K of UNIBUS address space.

If the presentation here does not answer all the questions you may have, consult the file /sys/vaxuba/uba.c

Autoconfiguration requirements

Basically all you have to do is write a ud_probe and a ud_attach routine for the controller. It suffices to have a ud_probe routine which just initializes br and cvec, and a ud_attach routine which does nothing. Making the device fully configurable requires, of course, more work, but is worth it if you expect the device to be in common usage and want to share it with others.

If you managed to create all the needed hooks, then make sure you include the necessary header files; the ones included by vaxuba/ct.c are nearly minimal. Order is important here, don't be surprised at undefined structure complaints if you order the includes incorrectly. Finally, if you get the device configured in, you can try bootstrapping and see if configuration messages print out about your device. It is a good idea to have some messages in the probe routine so that you can see that it is being called and what is going on. If it is not called, then you probably have the control-status register address wrong in the system configuration. The autoconfigure code notices that the device doesn't exist in this case, and the probe will never be called.

Assuming that your probe routine works and you manage to generate an interrupt, then you are basically back to where you would have been under older versions of UNIX. Just be sure to use the ui_ctlr field of the uba_device structures to address the device; compiling in funny constants will make your driver only work on the CPU type you have (780, 750, or 730).

Other bad things that might happen while you are setting up the configuration stuff:

\(bu 3
You get ``nexus zero vector'' errors from the system. This will happen if you cause a device to interrupt, but take away the interrupt enable so fast that the UNIBUS adapter cancels the interrupt and confuses the processor. The best thing to do it to put a modest delay in the probe code between the instructions which should cause and interrupt and the clearing of the interrupt enable. (You should clear interrupt enable before you leave the probe routine so the device doesn't interrupt more and confuse the system while it is configuring other devices.)
\(bu 3
The device refuses to interrupt or interrupts with a ``zero vector''. This typically indicates a problem with the hardware or, for devices which emulate other devices, that the emulation is incomplete. Devices may fail to present interrupt vectors because they have configuration switches set wrong, or because they are being accessed in inappropriate ways. Incomplete emulation can cause ``maintenance mode'' features to not work properly, and these features are often needed to force device interrupts.