Drivers and the Kernel

The Kernel

Pithy

The kernel a layer between the user space and hardware.
It has an API (syscalls) that provide useful facilities for interacting with the hardware.

The kernel API interface provides five basic features:

  • Management and abstraction of hardware devices
  • Processes and threads (and ways to communicate among them)
  • Management of memory (virtual memory and memory-space protection)
  • I/O facilities (filesystems, network interfaces, serial interfaces, etc.)
  • Housekeeping functions (startup, shutdown, timers, multitasking, etc.)

Note

Kernel is mostly C, with assembly sprinkled in where C can’t reach low-level CPU features (like atomic read-modify-write instructions).

Kernel versioning

Some distributions choose to emphasize stability and so run the older, more tested kernels.
Other distributions try to offer the most recent device support and features but might be a bit less stable as a result.
Select among these options in a manner that accommodates your users’ needs. No single solution is appropriate for every context.

Linux kernels use semantic versioning: major, minor and patch level

uname # = unix name, prints os name, kernel version, hw, etc.)

# Ubuntu machine
uname -r # see what kerenal the system is running
6.8.0-117-generic
6 # major
8 # mintor
0 # patch
-177-generic # ubuntu specific build number and flavor

# Debian machine
6.12.90+deb13.1-amd64

Tip

If you need a more recent kernel, install a distribution that’s designed around that kernel rather than trying to shoehorn the new kernel into an existing system.

As an example Debian 13 ships with 6.12.X and will stay on that through it’s lifecycle, they won’t change minors or majors for all of Debian 13 Source.

Kernel LTS versions are released around every year: https://www.kernel.org/category/releases.html Your distribution handles kernel updates for you, you just get whatever they ship.

DistributionReleaseKernel SeriesType
Arch LinuxRollingLatestMainline
Debian 13 (Trixie)Testing6.12Longterm
Ubuntu 24.10Regular6.11Stable
Fedora 41Regular6.11Stable
Ubuntu 24.04 LTSStable6.8Longterm
Debian 12 (Bookworm)Stable6.1Longterm
RHEL 9Stable5.14Longterm

Kernel maintainers actively back port security and bug fixes from mainline to each long term series. Which is why Ubuntu is 177, all of the patches they’ve had provided by upstream kernel maintainers. Ubuntu just packages, the kernel maintainers do the hard work.

How to get the latest patch of your kernel

apt list --upgradable
sudo apt update
sudo apt upgrade

Branches

Users: don’t care. Your distribution handles it.

  • Prepatch/RC - test new features kernel developers
  • Mainline - new features released every 9-10 weeks kernel developers
  • Stable - bug fixes from mainline, weekly releases distro maintainers
  • Longterm - backported security fixes for 2+ years distro maintainers & enterprises

Drivers

Pithy

A driver translates between a specific hardware device and the kernel’s standard interface.

A device driver is an abstraction layer that manages the system’s interaction with a particular type of hardware so that the rest of the kernel doesn’t need to know its specifics. The driver translates between the hardware commands understood by the device and a stylized programming interface defined (and used) by the kernel. The driver layer helps keep the majority of the kernel device-independent.

Drivers for other operating systems (e.g., Windows) do not work on UNIX and Linux.

Device files

In most cases, device drivers are part of the kernel; they are not user processes. However, a driver can be accessed both from within the kernel and from user space, usually through “device files” that live in the /dev directory.

The files in /dev each have a major and minor device number associated with them. The kernel uses these numbers to map device-file references to the corresponding driver.

Important

When a program performs an operation on a device file:

  • the kernel intercepts the reference
  • looks up the appropriate function name in a table
  • transfers control to the appropriate part of the driver

Pseudo Devices

It is sometimes convenient to implement an abstraction as a device driver even when it controls no actual device.
Such phantom devices are known as pseudo-devices.

Example, a user who logs in over the network is assigned a pseudo-TTY (PTY) that looks, feels, and smells like a serial port from the perspective of high-level software. This trick allows programs written in the days when everyone used a physical terminal to continue to function in the world of windows and networks. /dev/zero, /dev/null and /dev/urandom are some other examples of pseudo-devices.

Driver Example

Samsung Bar 3.1 plugged in:

  1. Kernel detects device, loads USB mass storage driver
  2. Driver creates /dev/sdb, auto-mounts to /media/samsung
  3. User: cp test.txt /media/samsung/test.txt
  4. The following pseudocode runs:
// User opens file on mounted filesystem
open("/media/samsung/test.txt", O_WRONLY | O_CREAT)

// Kernel creates file descriptor pointing to inode on /dev/sdb
// fd = 3
// write() syscall for the mounted filesystem
write(fd, "hello world", 11)
// We're calling write on /dev/sdb

// Kernel intercepts, translates to block device write
block_write(/dev/sdb, sector_123, data)

// Kernel looks up driver for major:minor 8:0
driver_function = driver_table[8][0]

// Calls driver
// Samsung bar is a generic USB device so it uses a generic USB driver
usb_mass_storage_write(sector_123, data)

// Driver sends USB WRITE command to Samsung Bar
// Device writes to flash, returns success

Device file management

When a new device is detected, both systems automatically create the device’s corresponding device files.
When a device goes away (e.g., a USB thumb drive is unplugged), its device files are removed.

  • /dev: interface to talk to the device
  • /sys: metadata about the device

Example:

/dev/sdb                          → write data to it
/sys/block/sdb/size               → query how many sectors it has
/sys/block/sdb/queue/hw_sector_size → query sector size
/sys/devices/pci0000:00/...       → IRQ, power state, driver info

udev

udev is a daemon that:

  1. listens for when devices appear in /sys
  2. reads its metadata
  3. creates the appropriate /dev/ file with the right permissions
  4. can run scripts/actions (mount, notify, etc.)

Example: plug in Samsung Bar -> udev creates /dev/sdb and auto-mounts it.
Without udev, you’d manually create /dev files for every device.

udevadm

udevadm is a tool that queries device info. It can be used to control the udevd daemon and help admins build and test rules for udev.

udevadm output

The results are layered kernel abstractions.

udevadm info -a -p /block/sdb/sdb1 

Top layers = arbitrary kernel labels
Bottom layers = actual physical/electrical path from your USB port to the PCI bus

Rules and persistent names

udevd relies on a set of rules to guide its management of devices.

  • default rules /lib/udev/rules.d/*.rules
  • local rules /etc/udev/rules.d/*.rules

You never need to edit or delete the default rules; you can ignore or override a default rules file by creating a new file with the same name in the custom rules directory.

Rules are in the form of:

match_clause, [match_clause, ...] assign_clause [assign_clause, ...]
Match clauses

The match clauses define the situations in which the rule is to be applied.
The assignment clauses tell udevd what to do when a device is consistent with all the rule’s match clauses. Most match keys refer to device properties (which udevd obtains from the /sys filesystem), but some refer to other context-dependent attributes, such as the operation being handled (e.g., device addition or removal).

Match keyFunction
ACTIONMatches the event type, e.g., add or remove
ATTR{filename}Matches a device’s sysfs values
DEVPATHMatches a specific device path
DRIVERMatches the driver used by a device
ENV{key}Matches the value of an environment variable
KERNELMatches the kernel’s name for the device
PROGRAMRuns an external command; matches if the return code is 0
RESULTMatches the output of the last call through PROGRAM
SUBSYSTEMMatches a specific subsystem
TEST{omask}Tests whether a file exists; the omask is optional
Assignment clauses

The most important assignment key is NAME, which indicates how udevd should name a new device.

Creating a rule
  1. Insert my USB

    lsusb
    Bus 001 Device 002: ID 413c:2113 Dell Computer Corp. KB216 Wired Keyboard
    Bus 001 Device 004: ID 6557:0021 Emtec USB DISK 2.0 # my USB
    
    # We can also watch the kernel logs to figure this out
    journalctl --dmsg -f 
    Jun 07 13:17:49 main kernel: usb 1-5: Product: USB DISK 2.0
    Jun 07 13:17:50 main kernel: sd 8:0:0:0: [sdb] 60628992 512-byte logical blocks: (31.0 GB/28.9 GiB)
    
    # The drive was recognized as sdb
    # So that means udevd will store the drive metadata in `/sys`
    ls /sys/block
    nvme0n1  sda  sdb
  2. Find unique fields to match

    # Use udevadm to investigate and find the matches we want to use
    udevadm info -a -p /block/sdb/sdb1
    
    # Filter the results and use these to match this device
    ATTRS{model}=="USB DISK 2.0"
    ATTRS{serial}=="070823B935AD4425"
  3. Write a rule for this

    sudo vim /etc/udev/rules.d/10-local.rules
    
    # Simple match for the serial number and write to a file when the drive is added (inserted)
    ACTION=="add", ATTRS{serial}=="070823B935AD4425", KERNEL=="sd[a-z]*", RUN+="/bin/sh -c 'echo matched >> /tmp/ate-flash-test.log'"
  4. Testing

    # Plug in the USB
    
    cat /tmp/ate-flash-test.log              
    matched
    
    # Success, the rule triggered