System Calls in UML ------------------- This is a short synopsis of how to add a new system call in UML. The example will be to print a message "Hello World!" every time a user types "hi" as a command inside UML. Find a computer on which to work, and initialize your UML environment by running the /usr/uml/457-init script. Then run the ./startuml script to make sure UML works correctly. Then exit gracefully from UML. % /usr/uml/457-init % cd /tmp/yourname/uml % ./startuml login: root # shutdown -h now Go to the kernel source file directory. Choose a file in which to add your new code. I chose user.c in the example. % cd linux/kernel % cp user.c olduser.c % emacs user.c At a convenient place in the file, add code that looks something like this: asmlinkage long sys_hellothere() { printk("Hello World!\n"); return 1; } Save the modified version of the kernel source file. Now define the system call in the appropriate include files. If you are working with a 32-bit Fedora Linux version of UML, then there are two steps needed. The first step involves defining the system call number. It will look like this: % cd /tmp/yourname/uml/linux/include/asm/arch % cp unistd_32.h oldunistd_32.h % emacs unistd_32.h To the end of the list of system call names, add a new one such as: #define __NR_hellothere 325 Also make sure to update the count of the number of system calls: #define NR_syscalls 326 Save the modified version of the unistd_32.h file. The second step is to manually update the system call table. On the 32-bit Fedora Linux version of UML, it looks like this: % cd /tmp/yourname/uml/linux/arch/x86/kernel % cp syscall_table_32.S oldsyscall_table_32.S % emacs syscall_table_32.S To the end of the list of system calls, add your new one such as: .long sys_hellothere Save the modified version of the system call table file. If you are using the 64-bit CentOS Linux version of UML, then there is only one step. It looks like this: % cd /tmp/yourname/uml/linux/include/asm/arch % cp unistd_64.h oldunistd_64.h % emacs unistd_64.h Add your new system call near the end of the file #define __NR_hellothere 286 __SYSCALL(__NR_hellothere, sys_hellothere) Save the modified version of the unistd_64.h file. When the kernel builds, it will automatically determine the number of system calls and construct the system call table dynamically using the code in the file /tmp/yourname/uml/linux/arch/um/sys-x86_64/syscall_table.c so you do not have to worry about this! Regardless of the Linux version used above, it is now time to compile the new version of your kernel. % cd /tmp/yourname/uml/linux % make linux ARCH=um CC=gcc CXX=g++ With luck, your kernel will compile successfully, and you will have a fresh new kernel image file. If not, then fix the compile errors until it works. % ls -l vmlinux % cd .. % ./startuml You are now in UML. You need to write a program hi.c that invokes the new system call in the kernel. If you are working in 32-bit Fedora Linux UML version, then put the following code into the file hi.c #include #include #include #include _syscall0(int, hellothere); int main() { printf("Calling the new system call...\n"); hellothere(); } If you are working in 64-bit CentOS Linux UML version, then change the line _syscall0(int, hellothere); into the line #define hellothere() syscall(__NR_hellothere) Try to compile this program inside UML. # cc -o hi hi.c (If you get an error message that the C compiler "cc" does not exist, then it means the copy of the UML distribution that you grabbed did not have cc in it. This was fixed on February 11, 2010. You will need to grab a clean new version of the UML distribution and start over from the beginning. Before doing so, make sure to remove any old UML stuff you have in your /tmp directory.) If the C compiler is present, as it should be, then you should get a compiler error message because UML does not know about the new system call yet. In the Fedora Linux UML version, you fix this in unistd.h # cd /usr/include/asm # ex unistd.h Add the following to unistd.h, making sure to use the same system call number as you did when you were outside UML. #define __NR_hellothere 325 In the CentOS Linux UML version, you fix this in unistd.h # cd /usr/include/asm-x86_64 # ex unistd.h Add the following lines to unistd.h, making sure to use the same system call number as you did when you were outside UML. #define __NR_hellothere 286 __SYSCALL(__NR_hellothere, sys_hellothere) Now compile this program inside UML. It should work this time. # cc -o hi hi.c Run it to see the result of your new system call! # ./hi Calling the new system call... Hello World! Shut down UML when you are all done. # shutdown -h now Optional: Create a user account for yourself inside UML if you wish. This can provide a convenient place for storing files and running code. Creating an account can be done with the "adduser" command. You can set the password in the /etc/passwd file to be empty (null) if you wish, or let the new user create their own password using "passwd". # adduser yourname # ex /etc/passwd /yourname/ s/!!//p wq # cd /home # ls From any of the UML terminal windows, you should be able to log in as yourname, and end up in your new home directory /home/yourname Bonus Extension: Modify the hellothere system call to print either "Hello World!" or "Howdy Calgary!" depending on whether the PID of the invoking process is odd or even. Another Bonus Extension: Modify the hellothere system call to take a single integer value passed as a parameter to the system call. Inside the kernel, your system call should print either "Hello World!" or "Hey Dude!" depending on whether the integer value passed is positive or negative.