|
|
1.1 ! root 1: 3.0 Device Driver Autoconfiguration ! 2: dmitch 25-Jun-91 ! 3: ! 4: ! 5: 1. Scope ! 6: ! 7: This document describes the mechanism by which user-level device drivers are bound to specific device registers and DMA channels at system boot time. This mechanism is to be used in the 3.0 release. Many of the RPCs needed to implement this scheme are described elsewhere, in a specification entitled "Kernel interface for user-level device driver support". ! 8: ! 9: ! 10: 2. General Scheme ! 11: ! 12: There exists one user-level task, called Config, which is executed early in the boot sequence. Config is a privileged task; it communicates with the kernel via the device_master port, which it (and no other task) obtains from init via the bootstrap server. Note that drivers themselves are not privileged. It is Config's job to examine various device registers and determine which device drivers (each of which is an executable file) are to be associated with which device registers. ! 13: ! 14: Each logical set of device registers (e.g., all of the SCSI registers) are associated with a port called dev_port, which is created by the kernel. Dev_port is the basic means by which a device driver gains access to a device. Config obtains the dev_port for each device via the dev_port_create() RPC. After obtaining the dev_port for a specific device, Config then exec's the appropriate driver for that device and makes dev_port available to that driver via the bootstrap server, thus enabling the driver to gain access to the registers and other resources associated with the device. ! 15: ! 16: ! 17: 3. Mapping Device Registers to Device Drivers ! 18: ! 19: A logical device is a set of registers which are associated with one device (e.g., all of the registers associated with the SCSI port). Each logical device has associated with it a dev_number by which Config refers to it; dev_number is machine-dependent but is basically an index into an array of logical devices. ! 20: ! 21: Each logical device also has a 32-bit attribute called dev_type. The 16 msb's of dev_type are called dev_index; the 16 lsb's are a revision number. For the purposes of this discussion, there are three basic types of devices. One is the type of device which conforms to the NRW model of 8 devices per NextBus slot; this device is referred to as an NRW DMA Device. Another type of device is a Native m68k Device, which describes all devices implemented internally on an m68k CPU board. The other type of device occupies an entire NextBus slot; this is a Slot Device. Third party boards for the m88k can contain either one Slot Device or up to eight NRW DMA Devices; third party boards for the m68k contain one Slot Device (since no DMA is available for NextBus boards on the m68k). The kernel determines which type of device a NextBus board contains by examining the Slot_ID field of the board. If bits 9 through 7 (inclusive) of the Slot_ID field are 1, then the board is assumed to contain NRW DMA Devices; otherwise it contains one S Slot Device. Native devices on both machines, by convention, are assigned a hard-coded Slot_ID of NATIVE_SLOT_ID. ! 22: ! 23: There is one more significant difference between NRW DMA Devices, Native m68k Devices, and Slot Devices; that is in the type and range of memory which can be mapped in by the driver. The various ranges of memory which can be mapped in by various drivers is described elsewhere. Suffice it to say here that Config neither knows nor cares about any of this memory space; mapping and allocating this memory is peformed by RPCs between the drivers and the kernel. ! 24: ! 25: ! 26: Config obtains dev_type and slot_id for a given dev_number from the kernel via the following RPC: ! 27: ! 28: ! 29: dev_return_t get_dev_type( ! 30: port_t device_master, ! 31: dev_num_t dev_number, ! 32: slot_id_t *slot_id, // returned ! 33: dev_type_t *dev_type); // returned ! 34: ! 35: Note that this is a privileged RPC which can only be executed by a task which has acccess to device_master (i.e., only by Config). ! 36: ! 37: It is the responsibility of machine-dependent code in the kernel to map dev_number to a slot_id and dev_type. This is done in one of three ways: ! 38: ! 39: * The dev_type for NRW DMA Devices is obtained from the hardware. A simple algorithm maps dev_number to the hardware address containing dev_type. ! 40: ! 41: * The dev_type for Native m68k devices is faked by the kernel; the kernel must maintain a hard-coded map of dev_number to dev_type for native devices. ! 42: ! 43: * The dev_type field for Slot Devices is defined as -1; the significant information (as far as device drivers and Config are concerned) is in slot_id. ! 44: ! 45: Once Config determines the dev_type for a given dev_num (and assuming a valid slot_id and dev_type), it obtains a dev_port for that device by the following RPC: ! 46: ! 47: dev_return_t dev_port_create( ! 48: port_t device_master, ! 49: dev_num_t dev_num, ! 50: port_name_t *dev_port); ! 51: ! 52: Where ! 53: ! 54: dev_num is the same dev_num used in get_dev_type(). ! 55: dev_port is the returned dev_port. ! 56: ! 57: ! 58: Config must then locate a device driver which is capable of dealing with this particular dev_type or slot_id. This information is encoded in the filename of every executable driver in the (TBD) device driver namespace (for now, let's assume some Unix directory). The filenames of all of these drivers are of one of the following two formats: ! 59: ! 60: devr_<DEV_INDEX>_<REVISION>_<human_readable_name> ! 61: devs_<SLOT_ID>_<REVISION>_<human_readable_name> ! 62: ! 63: The former is used for drivers for NRW DMA Devices and for Native m68k Devices; the latter is for Slot Devices. DEV_INDEX, SLOT_ID, and REVISION are all in ASCII hex. DEV_INDEX and REVISION are 4 hex characters; SLOT_ID is eight hex characters. <human_readable_name> is something like "LaserPrinter". For example, a printer driver might be named "devr_0002_0011_LaserPrinter". A frame buffer driver might be named "devs_00112233_3456_BigFrame". ! 64: ! 65: When Config is looking for a driver to handle dev_type "abcdABCD" and slot_id "_slot_", it follows the following algorithm (DEV_INDEX, SLOT_ID, and REVISION come from the filename of a prospective driver): ! 66: ! 67: For NRW DMA Devices and for Native m68k Devices (dev_type != -1): ! 68: ! 69: * only filenames starting with "devr_" are eligible. ! 70: * DEV_INDEX must match abcd exactly. ! 71: * The filename with the highest value of REVISION is used. ! 72: ! 73: For Slot Devices: ! 74: ! 75: * only filenames starting with "devs_" are eligible. ! 76: * SLOT_ID must match _slot_ exactly. ! 77: * The filename with the highest value of REVISION is used. ! 78: ! 79: If no driver with a filename which matches the above criteria is found, the device is skipped and the dev_port for that device is deallocated via the dev_port_destroy() RPC. This RPC is described in the Appendix. ! 80: ! 81: Assuming a driver is found the match is recorded by Config in a list of the following: ! 82: ! 83: typedef struct { ! 84: typedef struct { ! 85: port_t dev_port; ! 86: dev_type_t dev_type; ! 87: slot_id_t slot_id; ! 88: dev_num_t dev_num; ! 89: } entry[many]; ! 90: int executable_fid; ! 91: char *executable_file_name; ! 92: /* other cruft here */ ! 93: } dev_entry; ! 94: ! 95: Where ! 96: dev_port is obtained from the kernel by dev_port_create(). ! 97: dev_type is obtained from the kernel by get_dev_type(). ! 98: executable_fid is the File ID (inode number or its moral equivalent). ! 99: executable_file_name is the name located in the algorithm descibed above. ! 100: ! 101: This mechanism allows multiple dev_port's to be associated with a single driver. In this case, the namespace containing the device drivers would contain links to one executable, one link for each additional dev_type which that driver can handle. When config detects that a file which it is examining is a link to a file which it had already recorded in a dev_entry, it merely adds the new dev_port to that dev_entry.entry[]. This allows mapping multiple dev_ports to a single driver. The significance of this will be discussed later. ! 102: ! 103: ! 104: 4. Passing dev_port's to Device Drivers ! 105: ! 106: Once Config has mapped all available devices in the system to their associated executable drivers it performs the following for each driver to be executed: ! 107: ! 108: -- Create a bootstrap subset port which will be unique for this driver. ! 109: -- Advertise each dev_port associated with that driver with the bootstrap server under the bootstrap subset port. The name to be advertised is "dev_port_<dev_num>", where <dev_num> is the device's dev_number value, in ASCII decimal. ! 110: -- Create a unique signature port for this driver; advertise this port with the bootstrap server under the bootstrap subset port with the name "driver_sig_port". This will be used by Config to authenticate the driver in subsequent RPCs between the driver and Config. ! 111: -- Fork and exec (or task_create or...) the driver; the driver task's bootstrap port is the bootstrap subset port under which the dev_ports are advertised. ! 112: ! 113: When a driver starts running, if first obtains its dev_ports by doing a bootstrap_info() on its bootstrap port and then doing bootstrap_look_up() of all services found which have the name "dev_port_*". The result is the set of all dev_ports to which the driver has access. The driver obtains the dev_type and slot_id of each dev_port by performing the following RPC to the kernel: ! 114: ! 115: dev_return_t dev_port_to_type( ! 116: port_t dev_port, ! 117: slot_id_t *slot_id, // returned ! 118: dev_type_t *dev_type); // returned ! 119: ! 120: Unlike get_dev_type(), this can be executed by any task which has send rights to dev_port (i.e., by any device driver, for its own dev_ports). ! 121: ! 122: After this, it is up to the driver to perform the binding and memory mapping of the device registers and interrupt ports for its devices. This mechanism is defined elsewhere. ! 123: ! 124: Since a driver can obtain access to multiple dev_ports, it is possible for one driver task to deal with both multiple device types (e.g., Ethernet and Token Ring) and with multiple instances of the same dev_type (e.g., two SCSI ports). ! 125: ! 126: ! 127: 5. Behavior of Config Subsequent to Boot Time ! 128: ! 129: Once the procedure above is complete, Config enters a server loop in which a number of RPCs are serviced which pertain to the maintenance of dev_ports and further (re-)configuration of the system. Config advertises a port called dev_config_port in some TBD namespace (probably the nmserver) by which various tasks in the system can make requests of the Config server. These RPCs are described below. Note that Config always maintains the state of all running drivers; it is the central housekeeper for all driver tasks. ! 130: ! 131: kern_return_t driver_register( ! 132: port_t dev_config_port, ! 133: port_t driver_sig_port, ! 134: port_t driver_port, ! 135: config_return_t *rtn); ! 136: ! 137: This is performed once by each driver. The purpose is to allow Config to detect the death of a driver via the notification of port death of driver_port. Upon such port death notification, Config is responsible for executing the appropriate kernel RPC(s) such as dev_port_destroy() (described in the Appendix) which instruct the kernel to clean up any queued DMA requests and delete any interrupt bindings on dev_port. Config will then attempt to restart the driver initially associated with dev_port. ! 138: ! 139: ! 140: kern_return_t driver_delete( ! 141: port_t dev_config_port, ! 142: port_t driver_sig_port, ! 143: config_return_t *rtn); ! 144: ! 145: This allows for the graceful, controlled shutdown of a driver without causing Config to restart the driver. Config merely deletes all of its internal state associated with driver_sig_port. This is normally invoked by the driver which is being shut down. ! 146: ! 147: ! 148: kern_return_t device_delete( ! 149: port_t dev_config_port, ! 150: port_t dev_port, ! 151: config_return_t *rtn); ! 152: ! 153: This allows a driver to relinquish control over a single dev_port. This is typically used when a driver has ownership of multiple devices and wishes to give up some subset of them. A driver which wishes to relinquish control of all of its devices would use driver_delete(). ! 154: ! 155: ! 156: kern_return_t driver_rescan( ! 157: port_t dev_config_port, ! 158: config_return_t *rtn); ! 159: ! 160: This causes Config to perform the initial autoconfiguration procedure described in the first part of this document again, skipping dev_numbers for which it currently has valid mappings. <Is this safe for any old non-privileged task to invoke??> ! 161: ! 162: ! 163: kern_return_t driver_config( ! 164: dev_config_port, ! 165: slot_id_t slot_id, ! 166: dev_type_t dev_type, ! 167: config_return_t *rtn); ! 168: ! 169: This causes Config to search for a device matching the slot_id and dev_type arguments by successive calls to get_dev_type(). If such a device is found, an executable driver for the device is located using the same algorithm described in the first part of this document and the driver is exec'd (or whatever). This is the means by which a driver can be launched subsequent to boot time. A combination of driver_delete() and driver_config() is the means by which a new driver can be installed for a given device without rebooting the system. ! 170: ! 171: ! 172: Appendix ! 173: ! 174: Additional Kernel RPCs ! 175: ! 176: dev_return_t dev_port_destroy( ! 177: port_t device_master, ! 178: dev_port_t dev_port); ! 179: ! 180: This is the means by which Config informs the kernel that all resources associated with dev_port (including dev_port itself) are to be freed. This is invoked upon port death notification of a driver's driver_port and when Config cannot find a driver for a given device. ! 181: ! 182: ! 183: dev_return_t dev_slot_map( ! 184: dev_port_t dev_port, ! 185: vm_task_t target_task, ! 186: vm_offset_t slot_offset, ! 187: vm_size_t length, ! 188: vm_offset_t *addr; // in/out ! 189: boolean_t anywhere); ! 190: ! 191: dev_return_t dev_board_map( ! 192: dev_port_t dev_port, ! 193: vm_task_t target_task, ! 194: vm_offset_t slot_offset, ! 195: vm_size_t length, ! 196: vm_offset_t *addr, // in/out ! 197: boolean_t anywhere); ! 198: ! 199: These allows drivers to map in either slot space (up to 16 MB starting at 0xfs000000) or board space (up to 256 MB starting at 0xs000000). It is conceivable that multiple drivers can be active for one Slot Device, all sharing one dev_port, but each with a different portion of the total slot space or board space mapped in. This would be accomplished by one driver being exec'd by Config; that driver would then exec other drivers and give them send rights to dev_port. Each driver would invoke dev_slot_map() or dev_board_map() with different slot_offset and length arguments. Note that in this case, it becomes the responsibility of the driver task exec'd by Config to handle to port death of any drivers which it subsequently exec's and to which it gives send rights to dev_port. Config will only handle the port death of tasks which it execs. ! 200: Revision History ! 201: ! 202: 25-Jun-91 Doug Mitchell ! 203: Changed "reg_device and slot_device" to "NRW DMA Device, Native m68k Device, and Slot Device". Section 3 extensively rewritten to reflect this change. ! 204: ! 205: Modified appendix to include dev_board_map() RPC.
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.