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: Driver Access from User Space

With the kernel, now including our driver, running in the VM, we can write a simple user-space application that makes use of the device through the driver. After it has booted, we need to create a device file in the VM that we will use to communicate with the driver:

alpine:~# mknod /dev/edu_rng_sync c 255 0

Make sure to use the same major number (here, 255) that you hardcoded earlier in the driver code. In the VM, create the following C source file (you can do it directly in the VM with a command-line text editor such as vim or nano, or edit the file from VSCode in the container after placing it in the shared-folder, which is also accessible from the VM):

// test.c

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define EDU_RNG_SYNC_IOCTL_RAND _IOR('q', 1, unsigned int)
#define EDU_RNG_SYNC_IOCTL_SEED _IOW('q', 1, unsigned int)

int main() {
    int fd = open("/dev/edu_rng_sync", O_RDWR);
    if (fd < 0) {
        perror("Failed to open the device file");
        return -1;
    }

    unsigned int seed = 0x0;
    unsigned int random_number = 0;

    for(int i=0; i<2; i++) {

        // seed the generator
        if(ioctl(fd, EDU_RNG_SYNC_IOCTL_SEED, &seed)) {
            perror("ioctl seed");
            return -1;
        }

        printf("Device seeded with %d\n", seed);

        // get 5 random numbers
        for (int j=0; j<5; j++) {
            if(ioctl(fd, EDU_RNG_SYNC_IOCTL_RAND, &random_number)) {
                perror("ioctl rand");
                return -1;
            }

            printf("Round %d number %d: %u\n", i, j, random_number);
        }
    }

    close(fd);
    return 0;
}

This user-space application, when executed in the VM, calls the driver in the OS to make use of our device. Study its code and note the following:

  • We have the exact same calls to the macros _IOR and _IOW as in the driver code to define the ioctl commands. This will let the driver recognise the commands sent from user space.
  • The device pseudo-file we created represents the interface between user space and the driver code in the kernel. It is opened by the application in read-write mode; the application then sends ioctl commands to seed the generator and obtain random numbers.

If everything works well, you should see the same behaviour as our test integrated in the boot process: twice the same series of random numbers. Assuming the application’s code lives in the VM in /root/shared-folder/test.c:

alpine:~# cd shared-folder
alpine:~/shared-folder# gcc test.c -o test
alpine:~/shared-folder# ./test
Device seeded with 0
Round 0 number 0: 1804289383
Round 0 number 1: 846930886
Round 0 number 2: 1681692777
Round 0 number 3: 1714636915
Round 0 number 4: 1957747793
Device seeded with 0
Round 1 number 0: 1804289383
Round 1 number 1: 846930886
Round 1 number 2: 1681692777
Round 1 number 3: 1714636915
Round 1 number 4: 1957747793

If you run into problems, you should debug your kernel and application code. The application can be debugged with printf calls and gdb, and the kernel code with printk calls.