Creating a device-file node An introduction to some privileged Linux system-calls (needed for an upcoming programming exercise) A device-driver example • We want to build a simple device-driver, one that would let ordinary applications read and/or write to the CMOS memory (e.g. to adjust the date or time-of-day) • This will require that we have a so-called device ‘special’ file in the ‘/dev’ directory • Creating such a file normally requires the privileges of a System Administrator – but could be accomplished via a suitable LKM The Linux commands • If a System Administrator wanted to setup a device-file in the ‘/dev’ directory named ‘cmos’ that has read-and-write privileges, the following commands would be used: root# mknod /dev/cmos c 70 0 root# chmod a+rw /dev/cmos • Here the ‘c’ argument indicates that the file-node is for a ‘character-mode’ device • Values 70 and 0 are arbitrary id-numbers Removing a device-file • To delete a file from the ‘/dev’ directory, the SysAdmin uses the ‘unlink’ command: root# unlink /dev/cmos • Because of standard privilege-restrictions on the ‘/dev’ directory, these commands will fail if executed by unprivileged users • Students have not been given sufficient privileges to successfully execute these ‘mknod’, ‘chmod’ and ‘unlink’ commands LKM’s have full privileges • But CS students in this class DO possess privileges that allow them to install, and to remove, Linux kernel modules: user$ /sbin/insmod cmos.ko user$ /sbin/rmmod cmos.ko • While installed, modules execute inside the kernel itself, with no privilege barriers • Hence we should be able to perform the steps necessary to create a device-file! Developers disapprove! • The Linux developers want a ‘secure’ OS, so they have not made it convenient for us to create modules that would bypass the expected privilege-restrictions • For example, they use ‘information hiding’ capabilities of the GNU “C” compiler to conceal the locations of kernel-code that performs ‘mknod’, ‘chmod’, and ‘unlink’ ‘Open Source’ philosophy • At the same time, the Linux developers do adhere to the ‘Open Source’ philosophy – the kernel’s source-files are available in a tree-structured sub-directory: $ cd /usr/src/linux • The protocol for invoking system-services is widely known, and the ID-numbers for system-calls are in this header-file: $ cat include/asm/unistd.h How to invoke ‘unlink’ • This in-line assembly language code, used within an LKM, can ‘unlink’ a device-file: #include <asm/uaccess.h> char pathname[] = “/dev/cmos”; int retval; // for get_fs(), set_ds() // pathname for file to be ‘unlinked’ // for the system-call’s return-value { set_fs( get_ds() ); // allow kernel to address our data asm(“ pushal “); // preserve registers asm(“ mov $10, %eax “); // ID for ‘sys_unlink()’ asm(“ mov $pathname, %ebx “); // address of pathname asm(“ int $0x80 “); // invoke kernel service asm(“ mov %eax, retval “); // save the return-value asm(“ popal “); // recover registers } How to invoke ‘mknod’ #include <asm/uaccess.h> char pathname[] = “/dev/cmos”; int retval; // for get_fs(), set_ds() // pathname for file to be ‘unlinked’ // for the system-call’s return-value { set_fs( get_ds() ); // allow kernel to address our data asm(“ pushal “); // preserve registers asm(“ mov $14, %eax “); // ID for ‘sys_mknod()’ asm(“ mov $pathname, %ebx “); // address of pathname asm(“ mov $020000, %ecx “); // S_IFCHR constant asm(“ mov $70, %edx “); // our driver ID-number asm(“ shl $8, %edx “); // DH=major, DL=minor asm(“ int $0x80 “); // invoke kernel service asm(“ mov %eax, retval “); // save the return-value asm(“ popal “); // recover registers } How to invoke ‘chmod’ #include <asm/uaccess.h> char pathname[] = “/dev/cmos”; int retval; // for get_fs(), set_ds() // pathname for file to be ‘unlinked’ // for the system-call’s return-value { set_fs( get_ds() ); // allow kernel to address our data asm(“ pushal “); // preserve registers asm(“ mov $15, %eax “); // ID for ‘sys_chmod()’ asm(“ mov $pathname, %ebx “); // address of pathname asm(“ mov $000666, %ecx “); // read-and-write by all asm(“ int $0x80 “); // invoke kernel service asm(“ mov %eax, retval “); // save the return-value asm(“ popal “); // recover registers } Use ‘man’ for prototypes • Many of the Linux system-calls use the names of standard library-functions and employ the same function-arguments • So the ‘man’ page for the library-function will often give the system-call’s prototype • Example: $ man 2 mknod • (You need the ‘2’ here -- or you’ll get the manual-page for the ‘mknod’ command) Parameter-passing rule • Assembly language code that invokes a system-call always uses register EAX to pass the system-call’s ID-number • The system-call’s parameters (up to five) are then passed to the kernel in these registers (and in this order): EBX, ECX, EDX, ESI, EDI • Obviously this rule would only apply to a Linux version for the 32-bit x86 platforms In-class exercise #1 • Compile and install our ‘tempcdev.c’ LKM (from the class website), then verify using the ‘ls’ command that the ‘/dev/cmos’ file exists (and look at its access-permissions) • Remove that LKM, and use ‘ls’ to confirm that the ‘/dev/cmos’ file has been deleted • Use the ‘dmesg’ command to display the messages in your machine’s kernel logfile In-class exercise #2 • The file created by our ‘tempcdev.c’ LKM has ‘read-only’ permissions. So can you modify its ‘module_init()’ function so that the ‘/dev/cmos’ file will get created with both ‘read’ and ‘write’ access-privileges? Reminder! • Information presented in tonight’s lesson carries with it the responsibility of ethical behavior on your part – do not misuse it! • You are NOT authorized to look at other users’ files -- nor to erase or modify files which do not belong to you