Individual written problems are to be done individually. Group programming problems are to be done in your assigned programming groups. All homework submissions are to be made via Courseworks. Refer to the homework policy section on the class web site for further details.
Please complete and submit a private group programming assignment evaluation. The evaluation should be in a separate file called evaluation.txt that is submitted with your individual written assignment submission.
Programming problems are to be done in your assigned groups using one of the VMs that has been assigned to your group members. For all programming problems you will be required to submit source code, a README file documenting your files and code, and a test run of your programs. In addition, you should submit a cover sheet using either homework_work.txt or homework_nonwork.txt, depending on whether or not the programming assignment is completely working or not. For source code submissions, you only need to submit new source code files that you created and kernel source code files that you changed. You should clearly indicate your names, email addresses, and assigned group number on your submission. Each group is required to submit one writeup for the programming assignment.
You will build your own Linux kernel for this class. For each homework assignment, you will build a new Linux kernel. The image of the kernel you build for this assignment should be vmlinuz.hmwk2. Grading for this assignment will be done based on vmlinuz.hmwk2.
Note that for this and subsequent kernel programming assignments, you will be using a VMware VM to test and debug your kernels. To use VMware, you need an MRL account . If you setup this account for the first time, or reset your password, you must then change your password in the MRL lab at least once. Each student has already been allocated a VM, which can be accessed at http://vm.cs.columbia.edu. Use your MRL username and password to login to the VMware infrastructure. Once you have powered on your VM, use the username student and password given to you in class to login to the guest OS in the VM.
$ cd /usr/srcWe recommend that you keep a backup of everything that you write or modify outside your assigned VM, e.g. in your home directory. The data in the VMs is
$ tar -xzvf [path-to-tar-file]/linux-2.6.11.12.tar.gz
The Linux version you
are using utilizes a newer
assembler than the one originally
available to compile the Linux Kernel 2.6.11. Because of this you must
apply a patch to the linux source code before compiling. Please be
aware that this is not typically the case!!
|
A little care is needed to make sure the linked files are copied when written. If you use emacs to edit, you should have no problem: when you first make a change to a file in emacs, it moves the original file link to a backup (the ~ file), and makes a copy to work on. Likewise, the patch program moves the original link out of the way and makes a copy before patching a file. However, IF YOU USE VI, YOU WILL HAVE TO RUN ``mv file file~; cp file~ file'' BEFORE YOU START EDITING. Yes, this may seem like a pain in the neck right now, but you'll be glad for the time saved:
$ time cp -a linux-2.6.11.12 linux-2.6.11.12-realcopy
real 0m41.476s user 0m0.140s sys 0m3.340s
$ time cp -al linux-2.6.11.12 linux-2.6.11.12-linkcopy
real 0m0.428s user 0m0.030s sys 0m0.400s
Then, say I make some changes to the copies, and want to make a diff with the original sources (more about this later):
$ time diff -Naur linux-2.6.11.12 linux-2.6.11.12-realcopy > /tmp/patch1
real 0m2.615s user 0m1.250s sys 0m1.360s
$ time diff -Naur linux-2.6.11.12 linux-2.6.11.12-linkcopy > /tmp/patch2
real 0m0.278s user 0m0.160s sys 0m0.120s
$ make installOn the first time that you install a new kernel, and everytime you make changes that may affect modules (e.g. modifying an important include file) you also need to install the modules. It is also recommended to update the vmware-tools to match the new kernel. Assuming that your kernel version suffix is ".hmwk2", you can do these by:
$ make modules_installTo let the bootloader, grub, know where to find your kernel, be sure to enter the appropriate information into your /boot/grub/menu.lst file. You can create a new entry by copying an existing one (e.g. that of linux-2.6.11.12-base) and changing all instances of the kernel version to 2.6.11.12.hmwk2, for example:
[edit /boot/grub/menu.lst]
[reboot with new kernel]
$ vmware-config-tools.pl
int setrecording(int enable, int max_entries);Be aware that kernel resources are limited and memory should not be plainly allocated as in user-mode. So, we will pre-define a global maximum of 4000 entries in the system you should verify this global maximum against int max_entries in the setrecording system call. You will only record up to the first 'max_entries'.
int getrecording(struct reclog *log, int nr_logs);Once this function finishes, struct reclog *log will have an array of ordered system calls. NOTE that you are still recording until you call setrecording to disabled the syscall-logger. For example, if max_entries is 5, 5 system calls were recorded, and nr_logs was 3, you should now have room to record 3 more system calls.
struct reclog
asstruct reclog {
long syscall_nr; /* system call number */
long arg1; /* arguments of system call */
long arg2;
long arg3;
long arg4;
long arg5;
long arg6;
};
in include/linux/recinfo.h as part of your solution. You should also be able to #include it as part of a user-land application (see prob. 4).
Your code should handle all errors that can occur. At a minimum, your system call should detect and report the following error conditions:
Syscall-logger: System
calls are implemented as traps or software-interrupts that transfer
control to a specific kernel code, Linux has registered software
interrupt 0x80 in the interrupt descriptor table for invoking system
calls. Utilizing the %eax register as the index into the sys_call_table
in arch/i386/kernel/entry.S the system
call handler jumps to the specific code in the kernel for that
system function.
You will add to arch/i386/kernel/entry.S the following
assembly code
provided to create the system call logger. Line numbers are clearly
indicated where they should be added (251-267 or with KDB patch 263-279):
250 syscall_call:
251 pushl %eax
# pushad is the ISA standard
252 pushl %esp
253 pushl %ebp
254 pushl %edi
255 pushl %esi
256 pushl %edx
257 pushl %ecx
258 pushl %ebx
259 call
record_syscall #
HW2: SYSCALL LOGGER FUNCTION
260 popl %ebx
261 popl %ecx
262 popl %edx
263 popl %esi
264 popl %edi
265 popl %ebp
266 popl %esp
267 popl %eax
268 call
*sys_call_table(,%eax,4)
269 movl
%eax,EAX(%esp)
# store the return value
270 syscall_exit:
Create the function record_syscall() store it
in arch/i386/kernel/sysrecord.c
you must add sysrecord.c
to the correct compilation directive in the
Makefile.
asmlinkage void record_syscall(long arg1, long arg2, long arg3, long arg4, long arg5, long arg6);The above call will record information on the arguments given to the original system call, but be aware that the system call number is not in the parameters it must be fetched from the %eax register with the following code (must be the first instruction executed in function):
long index;
__asm__("movl %%eax, %0;" : "=r"(index) ); /* Save EAX into 'index' */
Additionally record_syscall() should maintain struct
kern_reclog in a linked list constructed using the kernel list
structures in include/linux/list.h:
struct kern_reclog {
long syscall_nr; /* system call number */
long arg1; /* arguments of system call */
long arg2;
long arg3;
long arg4;
long arg5;
long arg6;
... /* free to use structures to maintain this information */
};
This structure will be dynamically
allocated when record_syscall() is called and will be free'd
in getrecording() once
they've been copied over to the user buffer.
Your submission should be a kernel patch off of the Linux kernel with
KDB that you created in the previous problem. A
patch is used to store changes you've made to the kernel and to submit
your
changes for this problem. For example, if you've changed 50 lines of
code, a patch
will be about 50 lines long. This is much easier to store, email, or
move around than the full 283,000-line source tree. To create a
patch, first back up your .config file, then do a ``make distclean''
in your changed source tree (you don't want object files, config
files, etc. in your patch). Then, from the directory above your source
tree (e.g. /vmware1/src), run
diff -ruN linux-2.6.11.12 linux-2.6.11.12-changed > changed.patch
Notice that the original source tree is first, and the one containing
your modifications is second.
There is a particular ``algebra'' to patches. You may find it
helpful to think of diff as subtracting two source trees. The
result of this subtraction is a patch. To apply a patch, then,
is to add this difference back. If you supply the -R
option to the patch program, you subtract the difference,
instead of adding. If you thus ``subtract the difference'' from
your changed tree, you end up with the original tree. So, to
contain all the information of n kernel trees, all
you need is the n - 1 patches between them, and only
one full kernel tree (and it can be any one of the n).
Big Hint: In order to learn about system calls, you may also find
it helpful to
search the Linux Kernel for other system calls and see how they are
defined. The file kernel/timer.c
might give some useful
examples of this. The getpid
and getuid
system calls might be useful starting points. The system call
sys_getpid defined in kernel/timer.c uses current
macro and
provides a good reference point to how you should define
your
system call.
user> test-record
Calling setrecording(1)...
Calling some system calls to log:
Testing printf()
Testing open()
Testing close()
Calling setrecording(0)...
Calling getrecording()... SUCCESS
-------------------------------
Here is the list of calls made:
...
N. __NR_[289] (0, 5, 123, -1207956256, 0, -831438848)
...
...
...
...
...
printk
statements in system calls will print
their
output to the console whenever they are invoked. To request printk
messages be sent to a log file, insert the
following line into the /etc/syslog.conf file:
kern.* /var/kern.logThis will cause
printk
messages to be written to
/var/kern.log.
You can send a signal to request the syslog daemon re-read the updated /etc/syslog.conf
file, with the command "kill -1 pid" where pid is the pid of
the syslogd process.