Project 3: User-Space Device Driver Framework

Build a user-space driver framework using UIO/VFIO and restart-on-crash supervision.

Quick Reference

Attribute Value
Difficulty Advanced
Time Estimate 2 weeks
Language C (Alternatives: Rust)
Prerequisites C, Linux basics, memory-mapped I/O
Key Topics MMIO, interrupts, isolation, supervision

1. Learning Objectives

By completing this project, you will:

  1. Map device registers into user space safely.
  2. Handle interrupts from a user-space process.
  3. Implement a driver supervisor that restarts crashed drivers.
  4. Explain why microkernels place drivers in user space.

2. Theoretical Foundation

2.1 Core Concepts

  • Memory-Mapped I/O (MMIO): Devices expose control registers in physical memory.
  • User-Space Drivers: UIO/VFIO allow drivers to run outside kernel mode.
  • Interrupt Forwarding: Kernel delivers interrupt events to user space via file descriptors.
  • Fault Isolation: Driver crashes do not crash the kernel.

2.2 Why This Matters

Microkernels rely on user-space drivers to contain faults. Rebuilding this on Linux demonstrates the architecture without writing a kernel.

2.3 Historical Context / Background

MINIX 3 and QNX popularized user-space drivers for reliability. Linux UIO and VFIO were created to support user-mode drivers and virtualization.

2.4 Common Misconceptions

  • “User-space drivers are too slow.” For many devices, they are fast enough and much safer.
  • “Interrupts can’t be delivered to user space.” They can, via kernel mediation.

3. Project Specification

3.1 What You Will Build

A small framework that discovers a UIO device, maps registers into user space, handles interrupts, and is supervised by a watchdog that restarts on failure.

3.2 Functional Requirements

  1. Device discovery: Find /dev/uioX and its sysfs metadata.
  2. MMIO mapping: Map registers into user space.
  3. Interrupt loop: Block on UIO FD and handle interrupts.
  4. Supervisor: Restart driver on crash.
  5. Demo driver: Implement a simple GPIO or timer driver.

3.3 Non-Functional Requirements

  • Reliability: Driver restart restores functionality.
  • Safety: Validate MMIO offsets and bounds.
  • Performance: Avoid busy waiting in interrupt loop.

3.4 Example Usage / Output

uio_device_t *dev = uio_open("/dev/uio0");
volatile uint32_t *regs = uio_map(dev, 0);
regs[GPIO_DIR] |= (1 << 17);

3.5 Real World Outcome

$ ./driver_supervisor
[sup] launching gpio_driver
[driver] mapped 0x3f200000 size=0x1000
[driver] LED on pin 17 blinking

$ kill -SEGV $(pgrep gpio_driver)
[sup] gpio_driver crashed, restarting
[driver] mapped 0x3f200000 size=0x1000
[driver] LED on pin 17 blinking

4. Solution Architecture

4.1 High-Level Design

┌──────────────┐   fork/exec   ┌──────────────┐
│ Supervisor   │ ───────────▶  │  Driver Proc │
└──────┬───────┘               └──────┬───────┘
       │                               │
       │ restart on crash              │ MMIO + IRQ
       ▼                               ▼
  crash detection                /dev/uioX

4.2 Key Components

Component Responsibility Key Decisions
UIO wrapper Open/map/interrupt wait mmap layout and offsets
Driver Device-specific logic Poll vs IRQ
Supervisor Restart policy Backoff and max retries
Sysfs parser Read device metadata Robust parsing

4.3 Data Structures

typedef struct {
    int fd;
    size_t size;
    void *mem;
} uio_device_t;

typedef struct {
    pid_t pid;
    const char *path;
    int restart_count;
} driver_process_t;

4.4 Algorithm Overview

Key Algorithm: Supervised Driver Loop

  1. Supervisor forks driver process.
  2. Driver maps MMIO and waits for interrupts.
  3. Supervisor waits on child exit; if signaled, restarts.

Complexity Analysis:

  • Time: O(1) per interrupt event
  • Space: O(1) overhead per driver

5. Implementation Guide

5.1 Development Environment Setup

# Ensure uio_pdrv_genirq module is available
sudo modprobe uio_pdrv_genirq
cc -O2 -g -o driver_supervisor *.c

5.2 Project Structure

user_driver/
├── src/
│   ├── uio.c
│   ├── driver.c
│   ├── supervisor.c
│   └── main.c
├── include/
│   └── uio.h
└── tests/
    └── test_uio.c

5.3 The Core Question You’re Answering

“How can a driver crash without taking down the whole system?”

5.4 Concepts You Must Understand First

Stop and research these before coding:

  1. MMIO vs Port I/O
  2. Interrupt Handling Basics
  3. Process Supervision (waitpid, exit codes)
  4. Memory Protection (mmap permissions)

5.5 Questions to Guide Your Design

  1. How will you validate register offsets?
  2. What happens if the driver dies while holding state?
  3. Should the supervisor replay pending requests?
  4. How do you throttle restart loops?

5.6 Thinking Exercise

Simulate an Interrupt Path

Draw the path from device interrupt to user-space handler. Which thread blocks? How does the kernel notify it?

5.7 The Interview Questions They’ll Ask

  1. “What is MMIO and how is it used in drivers?”
  2. “Why are user-space drivers safer?”
  3. “What is the overhead of UIO interrupt handling?”

5.8 Hints in Layers

Hint 1: Start with polling Read a register in a loop before adding interrupts.

Hint 2: Use UIO read() for IRQ Blocking read() is your interrupt wait.

Hint 3: Add supervisor restarts Wrap the driver in a fork/wait loop.

5.9 Books That Will Help

Topic Book Chapter
Linux drivers Linux Device Drivers Ch. 9-10
User-space I/O Linux UIO docs Core concepts

5.10 Implementation Phases

Phase 1: Foundation (4 days)

Goals:

  • UIO mapping and simple driver

Tasks:

  1. Open /dev/uio0 and map memory.
  2. Read/write a device register.

Checkpoint: You can toggle a device state.

Phase 2: Core Functionality (5 days)

Goals:

  • Interrupt handling

Tasks:

  1. Block on UIO FD read.
  2. Handle interrupts and clear flags.

Checkpoint: Interrupt counter increments in logs.

Phase 3: Polish & Edge Cases (5 days)

Goals:

  • Supervision and recovery

Tasks:

  1. Add supervisor restart loop.
  2. Test crash and recovery.

Checkpoint: Driver restarts and resumes operation.

5.11 Key Implementation Decisions

Decision Options Recommendation Rationale
Interrupt handling Polling, IRQ IRQ Lower CPU use
Restart policy Immediate, backoff Backoff Avoid restart storms
Driver API C funcs, IPC IPC hooks Aligns with microkernel style

6. Testing Strategy

6.1 Test Categories

Category Purpose Examples
Unit Tests UIO wrapper map/unmap, sysfs parse
Integration Tests Driver behavior IRQ count increments
Fault Tests Crash recovery SIGSEGV restart

6.2 Critical Test Cases

  1. MMIO bounds: Access outside range must fail.
  2. Interrupt loop: IRQ triggers handler.
  3. Crash recovery: Driver restarts and works.

6.3 Test Data

UIO device: /dev/uio0
IRQ counts: 1, 10, 100

7. Common Pitfalls & Debugging

7.1 Frequent Mistakes

Pitfall Symptom Solution
Wrong MMIO offset Device misbehaves Verify datasheet offsets
Missed interrupts No events Clear IRQ status register
Restart loops CPU spikes Add backoff and max retries

7.2 Debugging Strategies

  • Use strace on driver to see blocking reads.
  • Add a register dump command for debugging.

7.3 Performance Traps

Busy waiting in user space will burn CPU. Always block on interrupts when possible.


8. Extensions & Challenges

8.1 Beginner Extensions

  • Add a status CLI for the driver.
  • Add a ring-buffer for debug logs.

8.2 Intermediate Extensions

  • Support multiple devices with a single supervisor.
  • Add IPC to expose driver operations to clients.

8.3 Advanced Extensions

  • Implement state checkpointing before restarts.
  • Integrate with capability security from Project 2.

9. Real-World Connections

9.1 Industry Applications

  • QNX: User-space drivers are standard for safety.
  • MINIX 3: Drivers restart via reincarnation server.
  • MINIX 3: https://www.minix3.org/
  • Linux UIO: https://www.kernel.org/doc/html/latest/driver-api/uio-howto.html

9.3 Interview Relevance

Driver isolation and MMIO knowledge are common in OS interviews.


10. Resources

10.1 Essential Reading

  • Linux Device Drivers, 3rd Ed - MMIO and interrupts.
  • MINIX 3 papers - Fault isolation strategies.

10.2 Video Resources

  • OS lectures on device drivers and interrupts.

10.3 Tools & Documentation

  • UIO: Linux kernel documentation
  • strace: system call tracing
  • Project 13: Fault-tolerant driver supervision.
  • Project 5: Drivers serving a filesystem.

11. Self-Assessment Checklist

11.1 Understanding

  • I can explain how UIO delivers interrupts.
  • I can explain why driver isolation improves reliability.

11.2 Implementation

  • Driver maps MMIO and handles interrupts.
  • Supervisor restarts driver on crash.

11.3 Growth

  • I can list two safety tradeoffs of user-space drivers.

12. Submission / Completion Criteria

Minimum Viable Completion:

  • Driver maps MMIO and performs a simple operation.
  • Interrupts are handled in user space.

Full Completion:

  • Supervisor restarts driver on crash.
  • Driver resumes normal operation after restart.

Excellence (Going Above & Beyond):

  • State checkpointing across restarts.
  • IPC-based client API for driver operations.

This guide was generated from LEARN_MICROKERNELS.md. For the complete learning path, see the parent directory.