User:Turdus/Universal Driver Block Format
If it's okay, I would like to have this wiki page as the primary source for the UDRV Specification. Modify and expand only along the given lines.
Please note that currently it's nothing more than a DRAFT.
Disclaimer
UDRV stands for Universal DRiVer, which is the name of
- a block format,
- a library interface
- and a CLI tool
as well.
This specification aims to provide a way to share drivers among hobby kernels not source but binary level. Inspired by UDI, a dead project sadly, but it's message should not be lost, hence this spec.
UDRV takes less effort to understand and to implement than UDI. Adopting should be straightforward once you have user space libraries and hopefully will spread among hobby kernels eventually. It's needless to say what benefits that would have.
As opposite to UDI, UDRV is quite simple, yet robust and can be used with any existing executable format. To UDRV point of view, driver is a (usually small) binary blob that has to be linked with the rest of the system (or directly with the kernel if you're using monolithic kernel design, just like Linux kernel modules). In order to achieve linkage, UDRV specifies a data block, that has to be inlined in the binary's first 64k on a 16 aligned boundary. It's very similar to Multiboot block. This block describes the driver, and also holds pointers to it's functions.
Terms and phrases
- block - usually about 512 bytes, can be larger, a non-kernel-specific way to describe the driver's interface.
- driver interface - set of pointers to well defined functions using well defined ABI
- device instance - memory area that holds the current state of the device (implementation is kernel specific)
- device path - an UEFI generic device path binary string (see UEFI specification 9.3.1)
- device class - represents class, see PCI Device class. Note that UDRV uses this to categorize drivers, it does not mean UDRV is limited to PCI drivers in any way.
- device subclass - see PCI Device SubClass
- device interface - see PCI Device Interface. Do not confuse with driver interface.
- driver major version - reflects the software version, not mknod's major & minor
- driver minor version - patch level
- offset - pointer to UDRV block's data area, relative to UDRV magic+256
- pointer - pointer to code, relative to the beginning of the binary file
- properties - part of device instance memory, user tunable arguments
- newline - (NL) single ASCII character 10
- string end - single ASCII character 0
- CLI tool - a command to interact with UDRV compatible devices from shell
- UDRV compatible driver - executable binary blob image with UDRV Block in it
- UDRV compatible device - a device driver that implements UDRV Driver Interface
- UDRV compatible OS - can dynamically link UDRV Driver Interface and has at least one UDRV CLI tool
- UDRV compatible software - any program that can read and understand UDRV Blocks. Linkers, configuration tools
Universal Driver Specification DRAFT
UDRV Block Format
Executable driver binary
There's no restriction on it's format (dll, so, kext etc. use whatever you like, see shared library).
A word on function entry points in executable Obviously a kernel that understands the format can use it natively and there's no need for any additional pointers. But for kernels that don't, or formats that do not support other than entry point (such as a.out), the UDRV Block can be used.
Detecting UDRV
An UDRV compatible driver should have an UDRV Block in it's first 65536 bytes aligned on a 16 byte boundary. UDRV compatible software (like a linker or the CLI tool, or even a GUI configuration tool if you implement it) will look for this block and use it's contents.
Header
The block starts with a 256 byte long header, which is followed by initialized data, terminated by a zero.
This block (naturally since it's in a binary executable) holds information in binary format, but some are in human readable format for comfort.
struct udrv_header {
uint32 magic; //characters 'UDRV'
uint16 size; //UDRV Block size in bytes counting from magic
uint8 major_ver; //driver major version
uint8 minor_ver; //driver minor version
uint8 type; //driver type
uint8 flags; //UDRV flags for compatible software
uint8 device_class; //PCI Device Class
uint8 device_subclass;
uint8 device_interface;
uint8 chksum; //add bytes in block and check for zero
uint16 offs_name; //offset to an UTF-8 ASCIIZ string (used by dependency check)
uint16 offs_meta; //Meta Info string
uint16 offs_prop; //property definitions for instance memory
char[16] arch; //architecture, x86_32, x86_64, ARMv8 etc.
uint32 func_reset;
uint32 func_getcapabilty;
uint32 func_cmd;
uint32 func_open;
uint32 func_read;
uint32 func_write;
uint32 func_close;
union {
//character devices
struct {
uint32 func_seek;
} chr;
//block devices
struct {
uint32 func_seek;
uint32 func_read;
uint32 func_write;
uint32 func_getblocksize;
uint32 func_setblocksize;
} block;
//timers
struct {
uint32 func_resetcounter;
uint32 func_oneshot;
uint32 func_periodic;
uint32 func_getquantum;
uint32 func_setquantum;
} timer;
//line buffer devices (like serial)
struct {
uint32 func_seek;
uint32 func_fgets;
uint32 func_fputs;
uint32 func_getbuffsize;
uint32 func_setbuffsize;
} linebuff;
//devices that can hold sub-devices
struct {
uint32 func_list;
uint32 func_add;
uint32 func_remove;
} bus;
//virtual file system driver hooks
struct {
uint32 func_fopen;
uint32 func_fread;
uint32 func_fwrite;
uint32 func_fclose;
uint32 func_fstat;
uint32 func_opendir;
uint32 func_readdir;
uint32 func_closedir;
uint32 func_mkdir;
uint32 func_rmdir;
uint32 func_link;
uint32 func_symlink;
uint32 func_unlink;
uint32 func_mount;
uint32 func_umount;
} vfs;
}
} udrv_header_t;
Data area
It's nothing more than an initialized data area to hold stuff like name, meta info etc.
Default values
If properties offset (offs_prop) is not zero, it selects a Property definitions string within data area. The first column defines size, sum it up to get default values size. Normally you don't need this information, since you convert offsets. By default, all values are zero, unless you define them at compile time (eg.: pskeyboard should have 1 in "irq" property).
If properties offset is zero, then the device is not user tunable and there's no default value after the header at all (meaning other offsets can use zero as valid offset).
Meta Info string
#comment author (author(s)) supports (comma ',' separated list of PCI IDs in hex) depends (comma ',' separated list of other UDRV device drivers this driver relies on conflicts (comma ',' separated list of other UDRV drivers that this driver cannot run with homepage (homepage of the author) hardware (homepage of the device's manufacturer) license (licensing information) download (url to the driver download directory) device (device file path and name regexp string, not including "/dev/", like "disk[0-9](p[0-9])?","net[0-9]","uuid/[0-9a-f]{16}") Example: author Me<[email protected]> depends hpet, pci>=1.2 license LGPL device eth[0-9] #this will look for "http://myproject.tld/public/drivers/" (category) "/" (udrvname) "-" (major ver) "." (minor ver) ".udrv" #which can be a single binary or either cpio or tar (optionally compressed with gzip, bzip2) #you must provide (udrvname) "-latest.udrv" symlink there. Redirects in http responses are likely too #to handle dependencies to foreign drivers without mirroring. download http://myproject.tld/public/drivers/
Property definitions
Each line in property string looks like: (number of bytes occupied) ' ' (name of the property) ' ' (type) NL Ends in a zero byte. Example: 1 irq int(min=1,max=16) 4 ioapic hexint(min=0xF0000000,max=0xFFF00000) 2 language optionlist('en','hu') 1 boolean optionlist('0':'false','1':'true')
UDRV Library Interface
Drivers compiled for x86_32 use stdcall, x86_64 use fastcall calling convention.
#define UDRV_MAGIC "UDRV"
enum { UDRV_TYPE_CHAR, UDRV_TYPE_BLOCK, UDRV_TYPE_TIMER, UDRV_TYPE_LINEBUFF, UDRV_TYPE_BUS, UDRV_TYPE_VFS };
#define UDRV_FLAG_64BIT 1
enum { UDRV_CLASS_UNCLASSIFIED, UDRV_CLASS_STORAGE, UDRV_CLASS_NETWORK... };
int reset (void* instance, void* malloc, void* free, void* sendsignal);
char* getcapability(void * instance);
//similar to ioctl, but suitable for kernels without vargs too
int cmd(void *instance, int cmd, void* arg1, void* arg2, void* arg3, void* arg4, void* arg5, void* arg6);
...soon
Linking the library
For 64 bit systems you'll have to expand pointers to 8 bytes. Once the driver loaded, your linker should adjust the pointers with the memory location it was loaded to (uint32 -> void*).
Instance memory
Device instance memory has a two pointer header. One for the name, one for the path. It's 8 bytes for 32 bit, 16 for 64 bit.
struct udrv_instance { unsigned char* name; //UTF-8 ASCIIZ, without "/dev/", like "disk1p3" unsigned char* path; //UEFI generic device path (note, 0xff terminated!) } udrv_instance_t;
That header is followed by device properties (current configuration) and optionally local variables (current state). On creation device properties are filled in with default values from UDRV Block.
Not counting the header the structure of instance memory is totally up to kernel. It can be placed in kernel memory, user memory (for microkernels, either in fs server or driver address space), even in fs inode, etc.
UDRV CLI tool
Probably most hobby OS is some kind of UN*X, it's safe to assume to have a shell. Therefore it's highly recommended to implement this command on your OS. If you do some extreme stuff without shell, you'll need to provide a way to communicate with the UDRV compatible devices (voice command, GUI whatever).
Commands
udrv reset device udrv (get|set) (device|default device|driver) property [value] device - query/modify device instance driver - query/modify default values in UDRV block default device - get device's driver name and query/modify it's UDRV block udrv path device - print generic device path udrv list device - list sub-devices (only for bus-type devices) udrv update driver - not required for basic compatibility
Reset device
udrv reset /dev/serial0
Resets the device to it's initial state.
Query device
udrv get /dev/eth0 netmask
Configure device
udrv set /dev/eth0 ip 1.2.3.4/24
Query defaults
udrv get default /dev/eth0 mtu udrv get /lib/mykernel/network/ns2k.o mtu
Configure defaults
udrv set default /dev/eth0 mtu 1490 udrv set /lib/mykernel/network/ns2k.o mtu 1490
Query sub-devices
udrv list /dev/pci
Query UEFI generic device path
udrv path /dev/disk0
Returns UEFI generic device path (see UEFI spec 9.3.1) in human readable format. For devices that does not have one (or you are not using UEFI at all), your kernel should create one, it's quite easy to put a byte one after another.
Update driver
udrv update /lib/mykernel/network/ne2k.o
Once you have networking, you can use the download url given in Meta Info string to refresh your drivers on the fly. UDRV does not have a central repository by design, the drivers expected to be distributed on hobby kernel's download sites. For list of available drivers, this page is the primary source.
This function is not required to be fully UDRV compatible, but highly recommended (once you have networking). In lack of native networking, you can use your build system to refresh the driver binaries.
Porting
To make porting this command really easy, it's source is a single file in ANSI C. You should be able to cross-compile (or compile :-) ) it with "cc udrv.c -o udrv", where "cc" is the C compiler of your choice (GCC, TCC, LLVM whatever).
Alternatively if you don't use C at all, you can write your own version of the command expecting the same arguments as this reference implementation.
udrv.c:
#include <stdio.h>
#include <stdlib.h>
#include <udrv.h>
int main(int argc, char** argv)
{
//soon
}
Tested compilers
List of C compilers known to compile the above code correctly:
List of UDRV Device Drivers
List of download urls to driver binaries.
01 Mass storage controller
02 Network controller
03 Display controller
04 Multimedia controller
05 Memory controller
06 Bridge
07 Communication controller
08 Generic system peripheral
09 Input device controller
0a Docking station
0b Processor
It's very unlikely to have such a driver, for performance reasons and hardware issues CPU resource management usually implemented natively.
0c Serial bus controller
0d Wireless controller
0e Intelligent controller
0f Satellite communications controller
10 Encryption controller
11 Signal processing controller
List of UDRV Compatible Operating Systems
List of hobby operating systems that have implemented UDRV.