CS 0449 - Project 5: /dev/dice
Due: Sunday, April 21, 2024, by 11:59pm
Late Penalty Waived: Tuesday, April 23, 2024, by 11:59pm (no submissions after this date)
Project Description
In this project, we will perform our first alteration of how Linux works by writing a specific implementation of the read() system call for a device which we will make up and emulate in software.
We can see the system calls of our OS as a device-independent interface that userspace programs can use to request the OS do various task we are not permitted to do ourselves. The OS uses the passed file descriptor to determine which implementation of the read system call it should invoke. The multitude of implementations of system calls like read() and write() live in software modules called device drivers.
What we need then is a filename (so we can open it and get a file descriptor) that represents our device. The /dev virtual filesystem holds “fake” files that represent hardware devices attached to our system.
Sometimes these files don’teven represent “real” hardware devices. Standard UNIX and Linux systems come with a few special files already like /dev/zero, which appears to be filled with an infinite number of zeros when it is read, and /dev/random, which yields random bytes. In this project, you will write a device driver to create a new device, /dev/dice which returns randomly selected rolls of a 6-sided die.
How It Will Work
For this project, we will need to create three parts: the device driver, the special file /dev/dice, and a test program to convince ourselves it works
The test program will bean implementation of the game of craps.
Driver Implementation
Our device driver will be a character device that will implement a read function (which is the implementation of the read() syscall) and returns appropriate die rolls (a single die roll is a 1 byte value from 0 to 5 and we must support read()ingan arbitrary count of these). As we discussed in class, the kernel does not have the full C Standard library available to it and so we need to get use a different function to get random numbers. By including
unsigned char get_random_byte(int max) { unsigned char c; get_random_bytes(&c, 1); return c%max; } |
The Game of Craps (30 points)
Using your driver, you will be implementing the game of Craps. For those unfamiliar with the rules, Craps is played with two dice which are rolled together. The sum of these two dice is computed and if the sum is:
- 2, 3, or 12 – the player immediately loses.
- 7 or 11 – the player immediately wins.
- Any other number is referred to as the “point.” The goal then becomes to keep rolling the dice until:
- The player rolls a 7 – the player loses
- The player rolls the “point” – the player wins.
Your job is to make a simple craps game. In this game the player will enter his or her name, the program will display a welcome message, and ask if the player would like to play or quit. The program will then roll two dice, and display them and their total. If the player has won, display a message congratulating them. If the player has lost, report it to them. Otherwise, store this first roll as the “point” roll and keep rolling until the player wins or loses. When the game ends, ask if the player would like to play again. Example:
Welcome to Jon’s Casino! Please enter your name: Jonathan Jonathan, would you like to Play or Quit? play You have rolled 5 + 2 = 7 You Win! Would you like to play again? no Goodbye, Jonathan! |
Hints
- Remember that rolling two 6-sided dice is different than rolling one 12-sided die.
- Use string input, do not make an integer menu or use single characters.
- Gets each die to display by reading a byte from the /dev/dice file.
- You may write the craps program using stdlib’s rand() for testing, but make sure you eventually replace it with your read()s of /dev/dice
Installation and Example
1. Use the Virtual Machine you made in recitation, or follow the instructions from Canvas and set up your VM.
2. Loginto your VM with the username of root and a password of root
3. We need to update the package list before we install some software
apt-get update |
4. Then install the linux development kernel headers:
apt-get install linux-headers-$(uname -r) |
5. Download from thoth this archive file, replacing USERNAME with yourusername:
scp [email protected]:/u/OSLab/original/hello_dev.tar.gz . |
6. Untar/uncompress it:
tar xvfz hello_dev.tar.gz |
7. Change into the hello_dev directory
8. Build the kernel object:
make |
9. Load the driver using insmod:
insmod hello_dev.ko |
10. When we load the driver, the udev system of Linux will automatically create the “fake” /dev/hello file. We can now read the data out of /dev/hello with a simple call to cat:
cat /dev/hello |
11. You should see “Hello, world!” which came from the driver. We can cleanup by unloading the module (removing the device):
rmmod hello_dev.ko |
What to Do Next
The code for the example we went through comes from http://www.linuxdevcenter.com/pub/a/linux/2007/07/05/devhelloworld-a-simple-introduction-to- device-drivers-under-linux.html?page=2 a now dead link which I’ve mirrored on Canvas. Read that while going through the source to get an idea of what the Module is doing. Start with the third section entitled “ Hello, World! Using /dev/hello_world” and read up until the author starts describing udev rules; we will only run our code as root, avoiding this issue.
When you have an idea of what is going on, copy and rename the hello_dev.c from the example, and copy over the Makefile. Edit the Makefile to build your new file. Change all the references of “hello” to “dice_driver” .
Building the Driver
To build any changes you have made simply:
make |
If you want to force arebuild of everything you may want to remove the object files first:
rm *.o |
File Backups
Backup all the files you change under VirtualBox to your ~/private/ directory frequently!
Loss of work not backed up is not grounds for an extension. YOU HAVE BEEN WARNED.
Copying Files In and Out of the VM
Once again, you can usescp (secure copy) to transfer files in and out of our virtual machine.
You can backup a file named hello_dev.c to your private folder with:
scp hello_dev.c [email protected]:private
Loading/Unloading the Driver into the Kernel in the VM
As root to load:
insmod dice_driver.ko |
unloading:
rmmod dice_driver.ko |
Hints and Notes
- The provided helloworld example sometimes assumes too much about the parameters it was given. You should not necessarily enforce all of the constraints it does. The parameters are:
- bufis our output (in the algorithmic sense) – it is where we will place the requested dice rolls
- count is the number of rolls we’ve been asked to produce. buf is promised to beat least count bytes in size.
- *ppos represents the file pointer (the generic concept, not the FILE * type from C). The file pointer is where the next sequential read or write should take place, so we do not reread data we have already. Note that we have a C pointer parameter that is not pointing to some large allocation we’retrying not to copy, so it must be that this is meant to bean algorithmic output as well (i.e., you must update *ppos).
-
Like most of the C Standard Library functions, system calls return an integer whose value is interpreted as successor failure. A negative value always means failure, but positive numbers mean different things for different functions and syscalls. For read, success is defined to be that you were able to read as much data as you were asked for. If you return less than this, the OS interprets your return value as indicating the End Of File (EOF) was reached on that read, and will never call your read function for that open file again.
- printk() is the version of printf() you can use for debugging messages from the kernel. The arguments of a format string and values to interpolate are the same. What is different is the fact that the kernel doesn’t like printing to the screen when other code is running, so by default we will not see the output of our printk()s unless we use the dmesg command to examine a message log file. We can tell the kernel what we are printing is really important by prefixing our format string with various values, the most urgent is KERN_ALERT:
printk(KERN_ALERT “Hello, World!\n”);
Will show our message on the screen immediately. When we are done debugging, our driver should have no printk()s in it, so make sure to remove them.
- In the driver, you can use some standard C functions, but not all. They must be part of the kernel to work.
- In the craps program, you may use any of the C standard library functions.
- As root,typing poweroff in VirtualBox/QEMU will shut it down cleanly.
- If anything goes so wrong that the virtual machine doesn’t work, just replace the disk image with the original from the downloaded zip file and start over. It’swhy we’redeveloping in a virtual machine as opposed to a realone.
Requirements and Submission
You need to submit:
- Your dice_driver.c file and the Makefile
- Your well-commented craps program’s source
Make atar.gz file named USERNAME-project5.tar.gz
Copy your archive to the submit directory:
~jrmst106/submit/449/