User:Turdus/Universal Driver Block Format

From OSDev Wiki
Jump to: navigation, search

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.



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.


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

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}")

author Me<a@myproject.tld>
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.
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_FLAG_64BIT 1
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);

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.


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


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

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.


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.


#include <stdio.h>
#include <stdlib.h>
#include <udrv.h>
int main(int argc, char** argv)

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.

Monolithic kernels

Micro kernels

Personal tools