Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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_sync has 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, a pci_device_id table, a probe callback, pci_enable_device + pci_iomap) instead of hardcoding it.
  • Device file / major number: use alloc_chrdev_region + cdev_init/cdev_add for a dynamic major, and class_create + device_create so /dev/edu_rng_sync appears automatically, no mknod needed.
  • 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.