Synchronous RNG: Enhancing the Driver
Limitations
The driver we wrote in the previous chapter of the exercise is not very robust, and presents a number of limitations:
- The base physical address where the device’s MMIO registers are located is hardcoded in the driver’s code. If that address changes, which can happen for many reasons, e.g., another PCI device is plugged in or removed, the driver will stop working.
- The device file
/dev/edu_rng_synchas to be created manually by a system administrator, and its major number is hardcoded (255) in our example. If another driver initialises before ours and requests 255, our driver won’t be able to load. - The ioctl command definitions are duplicated between the kernel and user-space code: if we update these definitions on the driver side and forget to do so in the application code, things will break.
Addressing the Limitations
Your goal for this part of the exercise is to address all these limitations. This part is less guided, and it is acceptable to use AI to address these issues. Still, please make sure to use it responsibly: you need a good understanding of the code you produce here, as the exam for COMP26020 will include technical questions on that content.
A few hints on how to address the aforementioned limitations:
- BAR: discover it dynamically via the PCI subsystem (
pci_register_driver, apci_device_idtable, aprobecallback,pci_enable_device+pci_iomap) instead of hardcoding it. - Device file / major number: use
alloc_chrdev_region+cdev_init/cdev_addfor a dynamic major, andclass_create+device_createso/dev/edu_rng_syncappears automatically, nomknodneeded. - Shared definitions: put the ioctl macros and device name in a header under
include/uapi/misc/in the kernel tree, included by both the driver and (once exported/copied) the user-space application. To export the kernel’s user-space API headers from the container to the VM, you can use the following command (make sure the VM is running):
root@container:~/workspace/linux-6.6# make headers_install INSTALL_HDR_PATH=/tmp && scp -P 1022 -r /tmp/include/* localhost:/usr/include/
Make sure to run this command every time you update the kernel’s user-space API header file.
Also, study the code of the test application and test suite mentioned below to understand how they interface with the driver.
Testing the Driver
Once you are happy with your code, compile and run the application present in shared-folder/edu-rng-sync-app.c as well as the test suite present in shared-folder/edu-rng-sync-test-suite.c in the VM to check that the driver works fine.
Please study the code of the application and test suite, and note that the definitions of (1) the device file representing the character device, and (2) the ioctl commands, are expected to be present in a header file misc/edu-rng-sync.h shared between the driver and the application (see the third hint above). The name of the device file should be defined by the macro EDU_RNG_SYNC_DEVICE_NAME, and the two ioctl commands should be named EDU_RNG_SYNC_IOCTL_RAND and EDU_RNG_SYNC_IOCTL_SEED.
Make sure your driver’s implementation and the kernel’s user-space API header file adhere to these conventions, as you will only submit your modifications to the kernel (driver code + kernel headers) and the test suite itself will be used unmodified to mark your work.
The output of the application should be similar to the test program we saw previously. A successful run of the test suite looks as follows:
alpine:~/shared-folder# gcc edu-rng-sync-test-suite.c -o edu-rng-sync-test-suite
alpine:~/shared-folder# ./edu-rng-sync-test-suite
[PASS] test_basic_random
[PASS] test_multiple_randoms
[PASS] test_seed_reproducibility
[PASS] test_different_seeds
[PASS] test_seed_zero
[PASS] test_seed_max
[PASS] test_invalid_ioctl
[PASS] test_rand_bad_pointer
[PASS] test_seed_bad_pointer
[PASS] test_stress
Passed: 10
Failed: 0
Another Limitation: Concurrent Accesses
Our device and its driver are not robust against concurrent accesses from multiple processes or threads. For example, assume two processes A and B accessing the device concurrently: B could overwrite the seed set by A and influence the random numbers seen by A. Fixing that issue would require not only updating the driver code, but also the emulation model for the device. Although any production-ready solution would need to support concurrent access, fixing that issue is out of scope for this exercise: we simply assume that the driver and device are accessed by a single program and thread at a time.