Target Duration: 2–4 minutes (~300–450 spoken words)
Focus: Pointwise verbal delivery covering dynamic character device registration,ioctlcommand dispatch, safe user/kernel boundary copying, and modifying physical RAM directly via the kernel's direct mapping.
What You Mentioned: "Character drivers & physical memory writing"
Why It Was Done (The Motivation): Allow userspace to trigger privileged memory writes without bypassing MMU paging.
Problems Faced & How Solved (The Reality): Translated VA to PA, converted PA to virtual address via kernel direct mapping (phys_to_virt()), and wrote data directly.
Opening & Scope:
"In Part 2.1, I engineered a Linux character device driver (2.1/chardev.c) exposing an ioctl interface that allows user space to query physical page translations and directly manipulate physical memory bytes from kernel space."
Step 1: Dynamic Device Registration and /dev Node Instantiation:
"First, to register the driver cleanly without hardcoding major/minor numbers, I used alloc_chrdev_region() (2.1/chardev.c:127), letting the kernel dynamically assign an unused major number. Next, I initialized and registered the device using cdev_init() & cdev_add() (2.1/chardev.c:134-137). Then, to make the device node automatically appear in user space without requiring manual mknod commands, I created a device class via class_create() & device_create() (2.1/chardev.c:139-142), which signals devtmpfs and udev to automatically instantiate /dev/chardev."
Step 2: Designing a Control-Oriented file_operations Table:
"Next, for the driver's interface, I populated the VFS struct file_operations table. Because this driver operates as an out-of-band control plane rather than a streaming stream of bytes, standard read and write methods were unnecessary. Instead, I routed commands exclusively through the .unlocked_ioctl = device_ioctl (2.1/chardev.c:115) entry point, bound to my custom handler function device_ioctl (2.1/chardev.c:68)."
Step 3: Crossing the User/Kernel Space Boundary Safely:
"Then, inside device_ioctl, I received a user-space pointer to a custom parameter structure (struct query_arg_t in 2.1/chardev.h:5). In Ring 0, dereferencing user pointers directly is dangerous because the pointer might be unmapped, malicious, or swapped out. I used copy_from_user() (2.1/chardev.c:73) to pull the argument into kernel stack memory and copy_to_user() (2.1/chardev.c:84) to return the resolved physical address, checking return codes to guarantee safe fault handling."
Step 4: Direct Physical Memory Modification via phys_to_virt:
"A standout capability I implemented was IOCTL_WRITE_TO_PHYS (2.1/chardev.h:13). After determining a page's physical address, the driver could write arbitrary bytes directly to that physical RAM location. In a 64-bit Linux kernel, all physical memory is identity-mapped into kernel virtual address space (the direct mapping / page offset). By passing the physical frame address to phys_to_virt() (2.1/chardev.c:91), I acquired a writable kernel virtual pointer and mutated the underlying physical byte directly—bypassing user-space page protections entirely."
Step 5: Testing with dev_user.c and Automation:
"Finally, I authored a user-space client (2.1/dev_user.c:11) and an automated test script (2.1/spock.sh). The user process initialized an integer, queried the driver for its physical address, validated that address against /proc/self/pagemap, requested a physical byte modification via ioctl, and verified that the variable's value changed in user space without executing a user-space assignment."
| Step | What Was Done | How It Works | Why This Mechanism / Order | Code Reference |
|---|---|---|---|---|
| 1. Dynamic Chrdev | Registered via alloc_chrdev_region |
Dynamically reserves major number and binds cdev to VFS. |
Eliminates major number collisions with existing drivers. | 2.1/chardev.c:127-142 |
| 2. Auto Node Creation | Called class_create & device_create |
Notifies sysfs/udev to create /dev/chardev. |
Eliminates manual root invocation of mknod in deployment. |
2.1/chardev.c:139-142 |
| 3. Ioctl Dispatch | Routed via .unlocked_ioctl |
Dispatches control commands via switch-case on ioctl numbers. | Optimal design for structured commands and bidirectional parameter structs. | 2.1/chardev.c:68-115 |
| 4. User Copy Boundary | Used copy_from_user / copy_to_user |
Safely copies buffers while catching memory access faults. | Prevents kernel crashes if user passes bad, unmapped, or null pointers. | 2.1/chardev.c:73, 84 |
| 5. Direct Map Write | Translated PA via phys_to_virt() |
Uses kernel direct mapping to access raw physical RAM. | Allows modifying physical memory frames without altering MMU PTE permission bits. | 2.1/chardev.c:91 |
ioctl character device instead of standard VFS read/write or /dev/mem?Answer:
/dev/memis heavily restricted or outright disabled in modern secure kernels (CONFIG_STRICT_DEVMEM), blocking user access to physical memory. Standard VFSread()/write()interfaces operate on unformatted byte streams, requiring ad-hoc packet parsing. Anioctlcharacter driver provides strongly typed, bidirectional command dispatch with custom C structs (struct mem_query), passing virtual addresses, physical offsets, and byte buffers in a single atomic syscall with minimal overhead.
copy_from_user prevent them?Answer: User space can supply invalid pointers, NULL pointers, or unmapped virtual addresses. Directly dereferencing a user pointer (
*user_ptr) in Ring 0 triggers an unhandled page fault and an immediate kernel panic (Oops). Functions likecopy_from_user()andcopy_to_user()rely on kernel fixup exception tables: if the user pointer faults during copy, the MMU exception handler catches it, jumps to kernel fixup code, and safely returns the number of uncopied bytes (-EFAULT), protecting kernel stability.
phys_to_virt() bypass user-space virtual memory protections?Answer: In user space, pages can be mapped read-only via virtual page table permissions (e.g.
PROT_READonconststrings or code segments). Attempting to modify them in user space triggersSIGSEGV. However, the 64-bit Linux kernel maps all physical RAM contiguously into the direct physical map (PAGE_OFFSET) with Ring 0 read-write permissions. By translating the virtual address to its physical page frame and callingphys_to_virt(pa), our driver writes directly through the kernel's alias mapping, successfully modifying the backing RAM without triggering a page fault.