getcurrenttime
system call returns the system
time value.
getprocinfo
returns information about a
running process specified by an argument value.
You and your partner should run QEMU only on the machine to which you have been assigned, so that we can better distribute QEMU load across the machines. In addition, it is important for correct networking between the host machine and qemu, that you do not run qemu on the same physical machine as any other group. You can always ssh into your machine from any other machine in our labs; you don't need physical access to your machine. See the machine assignments below.
Start by logging into your machine, and follow the "QEMU Start-up" instructions from the QEMU Guide for CS45 to set up QEMU so that you and your partner can share the same virtual machine. Once you have set up your QEMU environment, try running QEMU and booting and shutdown Linux from inside it. Next, try building the kernel from source and installing and booting it.
Qemu Machine Assignments | ||
orange : Steven H. and Kevin P. | cucumber: Stromme and Nick F. | clove : Catie and Niels |
cream : Christina and Kevin Li | fennel : Jonathan and Elliot | dill : Nick R. and Greg T. |
cheese : Peter and Greg R. | parsley : Jacob and Luis | vanilla : Allen and Sam W. |
salt : Sam C. and Steve D. | perilla : Katherine and Becca | sumac : Ben and Phil |
Starting Point Code:
You can copy over lab2 starting point code from my
public/cs45 subdirectory:
$ cp /home/newhall/public/cs45/lab2/* .This contains examples that show you what you need to #include in user-level test programs for your kernel changes. It also includes a makefile for building user-level test programs (you may need to change . If you add new header files, you may need to include them in your user-level test program.
/* * flag: if non-zero print out the time at kernel level * tval: passed by reference to system call, the current time * will be "returned" through this parameter */ long getcurrenttime(int flag, struct timespec *tval);NOTE: as you modify existing kernel modules, first make a copy of of the .c or .h or .s file as orig_filename.c, then modify filename.c. This way if you really break something you can easily go back to the original kernel source.
To get some ideas of how to implement the system call function, you may want to look at other example code in kernel/ that access xtime, and look at the linux/time.h header file for interface functions and type definitions.
Steps for implementing a system call:
There are three general steps to implementing a system call:
Details: In this example, I'm adding a new system call named
getsecretcode
, that takes has one int parameter and one
int passed by reference that the kernel fills with a secret
code. It returns 0 on success and an error value otherwise.
Changes for 64-bit x86 (this is our platform):
linux-2.6.32.44/arch/x86/include/asm/unistd_64.h
add a new entry:
#define __NR_getsecretcode 299 __SYSCALL(__NR_getsecretcode, sys_getsecretcode)If you look in the file
linux-2.6.32.44/arch/x86/kernel/syscall_64.c
, you can
see how the 64-bit system call table is generated.
The change that you make in unistd_64.h will result in header files
being added to include/asm-generic and in include/asm-x86/ with your
new system call information in it.
Changes for 32-bit x86. You DO NOT need to make these changes for building on our system; this is just to show you that this part of the kernel is architecture-specific, and to port your system call to other platforms you would need to modify all the architecture-specific files:
arch/x86/kernel/syscall_table_32.S
add a new entry:
.long sys_rt_tgsigqueueinfo /* 335 */ .long sys_perf_event_open .long sys_getsecretcode
linux-2.6.32.44/arch/x86/include/asm/unistd_32.h
add:
#define __NR_perf_event_open 337 #define NR_syscalls 338
linux-2.6.32.44/include/asm-generic/syscalls.h
add a function prototype for your system call:
#ifndef sys_getsecretcode asmlinkage long sys_getsecretcode(int flag1, int flag2); #endif
linux-2.6.32.44/include/linux/
(you don't need to do this for getcurrenttime):
/* * file: getsecretcode.h with really good comments */ #ifndef __GET_SECRET_CODE_H__ #define __GET_SECRET_CODE_H__ // add any type definitions etc. that may be needed at user-level here // you can look at other .h files in here to see some examples // anything between "#ifdef __KERNEL__" and "#endif" is only visable at kernel-level #endif
linux-source-2.6.32.44/kernel/
. Your
system call function must have "asmlinkage" prepended to
its header and a "sys_" prefix to its name, but you should
use the SYSCALL_DEFINEX macros that will generate this for you.
For example:
/* * file: getsecretcode.c */ #include <linux/kernel.h> // can only be included at kernel-level #include <asm/uaccess.h> #include <linux/syscalls.h> // you also may need to include other header files based on what // types and functions you want to access (linux/time.h might be useful for getcurrenttime) /* getsecretcode system call: * an awesome comment */ SYSCALL_DEFINE2(getsecretcode, int , pflag, int __user *, codeval) { /* to print a debug message use printk, which is similar * to printf: the kernel's stdout goes to the console and * to the files: /var/log/[syslog/kern.log] */ printk("Inside system call getsecretcode\n"); /* copy argument values that are passed by reference from user * space to to kernel space: * * (1) first call access_ok() to check if the space * pointed to by thetime is valid * (2) then call copy_from_user() to copy to kernel space * * note: pflag's value is passed on the stack so it does not need to be * copied to kernel space...its value can be grabed off the stack */ /* If you access any kernel variables that could be * modified by interrupt handlers that interrupt our syscall, * or by other processes simulteneously running in kernel mode, * then you need to put some synchronization around their access. * For existing kernel objects, there should be a lock or semaphore * you can use (in inlcude/linux there are interface files to * spinlock, seqlock, and semaphore) For new kernel state you add, you * need to add spinlock or semaphores (this is likely not necessary * for lab2 where you are accessing existing kernel state) */ /* copy pass by reference "return" values to user-space * * (1) first call access_ok() to check if the space * pointed to by codeval is valid (if you have not already done so) * (2) then call copy_to_user() to copy the value to user space * * You CANNOT directly access the space pointed to by codeval. * Instead you need to copy values to/from kernel space to the * space pointed to by the pass-by-reference parameters. */ /* a successful return from the system call */ return 0; }
obj-y += getsecretcode.o # add this right before the following line obj-y += groups.oThen, re-build the linux kernel.
You can add new user accounts from which to run test code by running 'addusr username', which creates a new user account for a user named username, with a home directory in /home/username.
There
are two ways to call a system call. One way is to make a call to
syscall
passsing the value of your system call number
and its arguments:
int code; ret = syscall( 299, 1, &code) // 299 is the number of your system callThe second is to define a wrapper function or macro around around a call to syscall, and then call the wrapper function in user code. Here is the macro way:
#define getsecretcode(arg1, arg2) syscall(299, arg1, arg2)Then just make calls to
getsecretcode
in user-level code:
// this way, if you "hide" the wrapper function; the user level code just calls // getsecretcode and doesn't need to know the system call number, 299. ret = getsecretcode(1,&code);
Your user-level test programs should include:
#include <unistd.h> #include <errno.h>and any other header files needed for type definitions.
TO COMPILE you need to tell gcc where to find your kernel's header files. Specify the include path using -I in your gcc command. Here is an example, but you should, of course, put this in the makefile that you use:
gcc -g -o tester -I/usr/src/linux-headers-2.6.32.44-lab2/include tester.cSpecific implementation hints:
message using printk
, then
incrementally add more functionality and test.
linux-source-2.6.32.44/include/linux/
.
Header file contents (prototypes and definitions) that
only should be visible inside the kernel (not at user level) should be inside
#ifdef __KERNEL__
, and #endif
preprocessor directives.
(for a new type passed from a user-level program to a system call, this is
not the case, but for later assignments you may need this).
getprocinfo
. It takes
two arguments: a process identifier value; and a reference to a
process information struct that you need to define in a new header file.
getcurrenttime fills in the field values of the second argument
with information about the specified process. If the pid argument is 0,
then getprocinfo
should "return" information about the
calling process, otherwise it should "return" information about the process
with a matching pid value.
The system call returns 0 on success, and one of the following error
values otherwise (feel free to add additional error return values):
Start by defining a the proc_info_struct in a new header file that you
create in include/linux/
. The struct should have the
following fields:
pid_t pid; /* pid of process */ pid_t parn_pid; /* pid of its parent process */ unsigned long user_time; /* total CPU time in user mode*/ unsigned long sys_time; /* total CPU time in system mode*/ long state; /* its current state */ unsigned long long sched_avg_running; /* its scheduled ave running time */ unsigned int time_slice; /* its scheduling time slice */ unsigned int policy; /* its scheduling policy */ unsigned long num_cxs; /* number of context switches it has had (sum of voluntary and involuntary cxs) */ pid_t gid; /* its group id */ int num_children; /* the number of child processes it has */ char prog[16]; /* its exec'ed file name (e.g. a.out) */You can fill in these values by accessing a process'
task_struct
that is defined in include/linux/sched.h
. Field values in the
proc_info_struct
that correspond to null pointer values in
the process' task_struct
should be set to -1. Not all field
values in your struct match the names of fields in the task_struct, so you
may need to read through some code and/or try some things out before you get
the right values for these fields. Additionally, some values may need to
be obtain indirectly via task_struct fields.
Types are defined in types.h files in
archetecture neutral and archetecture specific subdirectories of include.
For example:
linux-source-2.6.32.44/include/linux/types.h
.
Errors are
defined in linux-source-2.6.32.44/linux/asm-generic/errno.h
A few things to help you determine what to do and if your system call returns correct information:
A demo is something that you and your partner should practice before you give it; you want to make sure that it runs correctly and that it demonstrates that your solution is correct and complete. Make sure that you are demo'ing both how your system call works under normal conditions and how you are handling error conditions. Also, be prepared to answer questions during your demo about your implementation and about your test programs.
Often times demonstrating that your solution works means that you will need a way to run a version of your kernel with debugging output enabled, and you may need to show via unix commands or /proc information that your system calls obtain the correct information or do the right thing. In addition, for most demos, you will want to write one or more interactive demo applications (menu driven program), where you choose from a menu of options for invoking your system call(s), execute a system call, examine system state or kernel outuput to verify that it did the right thing, then choose the next system call to execute, and so on; you want to be prepared to discuss and to demonstrate the effects after any single system call.