Kernel Development Learning Pipeline
P1 - Add a new syscall 🤫
Add a new system call to the kernel
Outcomes:
- Get comfortable with the concept of a syscall from both kernelspace and userspace
- Learn how userspace programs make system calls
- Learn how those calls are handled by the kernel
- Understand how C relates to assembly language
- Write a C program to call your system call
- Write the same program in the assembly language of your platform
What to submit:
- A patch which adds a directory named
firstname_lastname to the assignment repo.
- The first patch should add your kernel patch as a .patch file to our repo.
- Generate a single patch from your commit to the Linux kernel using the command
git format-patch -1 within your linux repo
- Take the
.patch file outputted, and put it in your named directory (e.g. cp 0001*.patch ~/ILKD_Assignments/p1/your_name/
- Add the email patch itself to the staged git files and commit this.
- The second patch adds your C program to the named directory
- Include a Makefile that can compile it
- Make sure to have compiler warnings enabled (at least
-Wall but ideally -Wextra and -Wpedantic too, or even -Weverything if you use clang)
- Make sure that your code doesn’t have any warnings or errors.
- The third patch adds your assembly program to your named directory.
- Edit the Makefile to add rules for assembling and linking your assembly program using
as and ld
- The fourth patch adds the output of running
strace on your assembly program as it uses the new syscall.
- Don’t forget a cover letter.
- Submit your patches to programming1@kdlp.underground.software
Procedure:
- Start by adding a new syscall to your copy of the linux kernel.
- Create a new
.c file in the kernel directory within the linux repo named kdlp.c
- Using the appropriately numbered
SYSCALL_DEFINE macro, define a syscall entry function for a new kdlp syscall in the kdlp.c file that behaves as follows:
- Takes two arguments, an appropriately marked pointer to a buffer from userspace of type
char *, an integer specifying the size of the buffer of type size_t.
- Formats a message into a local buffer that includes at least the student’s name and the name of the current task that is running on the CPU (see the
get_task_comm macro).
- Determines the amount of data to send - equal to the lesser of the size of the kernel message and how much space the user provided (see
min macro).
- Copies that many bytes from the local message buffer into the user pointer (see the
copy_to_user function).
- Returns a negative error code of
-EFAULT (errno 14, bad address) if the copy is not successful.
- Otherwise, returns the amount of data that was copied.
- The syscall implementation should take care to prevent any possibility of a buffer overflow.
- Modify the Makefile for the kernel subdirectory in
kernel/Makefile to add kdlp.o to the list of objects that are always compiled (add it to the end of the obj-y variable).
- Add an
asmlinkage declaration of sys_kdlp to the bottom of include/linux/syscalls.h (take inspiration from the numerous examples of other syscalls in that file).
- Add an entry to the syscall table for your architecture.
- On x86-64, this is in a special table format located at
arch/x86/entry/syscalls/syscall_64.tbl for historical reasons.
- You need to pick a number for it, put it in order with the next highest number after the last
common entry in the table
- Your syscall will be
common (i.e. shared between 32 and 64 bit userspace programs transparently by the kernel)
- The name is
kdlp and the entry point is sys_kdlp
- Take inspiration from the other nearby syscalls
- On aarch64 the list comes from the modern shared table located in
include/uapi/asm-generic/unistd.h.
- Find where
__NR_syscalls is defined and increment the value.
- Just above that line, add a
#define for __NR_kdlp with the next syscall number that is free
- add an invocation of the
__SYSCALL macro with the __NR_kdlp number and and the sys_kdlp entry point.
- Take inspiration from the other nearby syscalls.
- Update the extra version tag in the main kernel Makefile so you can tell this kernel that has your syscall apart from the other kernel you compiled.
- Update the config using
make oldconfig (this should only take a few seconds, and shouldn’t require you to answer any questions).
- Compile your new kernel with
make -j $(nproc)
- If the command finishes, make sure it was successful:
echo $? should output 0.
- If it does not, re run
make without -j $(nproc) to get a better error message.
- Fix whatever issue you see, and re run
make -j $(nproc).
- Repeat this process of checking the result and fixing any errors until it compiles successfully.
- Install your new kernel and its modules
sudo make -j $(nproc) modules_install install
- Reboot your vm and pick the new kernel.
- If all is well it should boot successfully.
- Congrats, you just wrote your first kernel code!
- Make a patch out of your kernel code.
- Find all untracked files, and files you modified by looking at the output of
git status within your linux kernel repository.
- Add them to the staging area with
git add and then run git commit -s and provide a message.
- Format the resulting patch as a
.patch file with git format-patch -1.
- You don’t need
--rfc or -v1 or --cover-letter because this .patch is not getting emailed directly
- Copy the
.patch file into your named folder within the p1 folder in the ILKD Assignments Repo.
- You can make your first commit for the assignment at this time.
- Write a C program that invokes the new syscall.
- Use the
syscall(2) function from the C standard library to invoke your syscall.
- Pass the number you gave it in the table as the first argument and then pass the rest of the arguments.
- Check for any errors and print an appropriate message if they occur.
- Otherwise, print the message you received from the kernel using the
write(2) syscall.
- You can now make your second commit for the assignment at this time.
- Write an assembly language program that behaves exactly the same as your C program with the following minor differences because you are not using the C standard library:
- Your program starts at a label named
_start instead of a function named main
- you need to explicitly call the
exit system call to terminate your program (trying to return from _start will segfault)
- You explicitly invoke the system calls by following the conventions of the linux syscall ABI (application binary interface)
- For x86-64:
- Arguments go into specific registers in order (
%rdi, %rsi, %rdx, %r10, %r8, %r9)
- The syscall number goes into
%rax
- The syscall is invoked using the
syscall instruction
- The return value is in
%rax and is either a non-negative return value, or a negative error number (errno variable is a C library concept)
- For aarch64:
- Arguments go into specific registers in order (
x0, x1, x2, x3, x4, x5)
- The syscall number goes into
x8
- The syscall is invoked using the
svc #0 instruction
- The return value is in
x0 and is either a non-negative return value, or or negative error number (errno variable is a C library concept)
- Make sure to update Makefile to assemble and link your program
- You can now make your third commit for the assignment at this time.
- Test your C program and assembly program.
- They should both print a message including your name and the name of executable file when you try them on the kernel you just compiled.
- Try running
strace on your assembly program. Capture the output into a file using 2> strace_log on the end of the command and add it to the repo as your fourth patch.
- You can also test the error case by booting into a kernel without your syscall (i.e. the regular upstream kernel you compiled or the one packaged by fedora)
- the kernel will return
-ENOSYS to indicate that the syscall is not implemented.
- both programs should print some sort of error message and exit early with a nonzero code.
The following pages will be of interest to a student:
Conventions:
-
Assembly files that must be pre-processed use the extension .S
-
Assembly files that are ready to be assembled use the extension .s
Refer to the Linux kernel documentation about adding syscalls for further guidance.
Submission Guidelines