Implementing a Linux syscall

Why

Honestly, no good reason at all. I am neither a C programmer, nor a kernel hacker. But that said I am curious and this sounds like fun and I get to build a kernel again. Last time must have been version 2.something ;)

Spoiler: Moores Law is not true. It took like 20 minutes to build a kernel 20 years ago, and it still takes 20 minutes ;)

Disclaimer

This is not my own work, I just searched a little bit on the internet. There is good documentation on kernel.org and I got the idea from Stephen Brennan. I've just searched where to put the things for a linux version 5 arm build.

Let's start

First I'd recommend to setup a virtual Debian install. I know there are a bunch of nice distributions out there but I used Debian for a long time so that's what I prefer. Furthermore I need something with good aarch64 support since I'm going to play with this on a Mac.

I tried both Parallels and UTM and for this purpose both should work. You can install UTM via brew: brew install utm. Download a Debian installer and make the ISO available to the virtual machine. Please use more than 10GB for the virtual harddrive, you'll need it.

Tools

It is advisable to install the Parallels Tools for better performance if you go with Paralles. You can do that as root with

mount /dev/cdrom /media/cdrom0

and then start the cli installer.

While you're at it enter

/sbin/adduser <your_username> sudo

for later. Then run sync && init 6.

The requirements

After updating install the following packages:

sudo apt-get install build-essential linux-source bc kmod cpio flex libncurses5-dev libelf-dev libssl-dev dwarves rsync

Build the kernel

Now you can unpack the kernel sources in your home directory:

tar xavf /usr/src/linux-source-5.10.tar.xz

Since we don't want to change the kernel-config we can just copy the running config.

cd linux-source-5.10 cp /boot/config-5.10.0-9-arm64 .config

Edit the .config file and enter this

CONFIG_SYSTEM_TRUSTED_KEYS = ""

And then run

make oldconfig

To build the kernel packages run

nice make -j`nproc` bindeb-pkg

Implement the syscall

In include/uapi/asm-generic/unistd.h

#define __NR_demo 441 __SYSCALL(__NR_demo, sys_demo) #undef __NR_syscalls #define __NR_syscalls 442

In kernel/sys.c

/* * https://brennan.io/2016/11/14/kernel-dev-ep3/ */ SYSCALL_DEFINE1(demo, char *, msg) { char buf[256]; long copied = strncpy_from_user(buf, msg, sizeof(buf)); if (copied < 0 || copied == sizeof(buf)) return -EFAULT; printk(KERN_INFO "demo called with '%s'\n", buf); return 0; }

and the userspace programm

/* * https://brennan.io/2016/11/14/kernel-dev-ep3/ */ #define _GNU_SOURCE #include <unistd.h> #include <sys/syscall.h> #include <stdio.h> #define SYS_demo 441 int main(int argc, char **argv) { if (argc <= 1) { printf("Must provide a log string\n"); return -1; } char *arg = argv[1]; printf("Making call with '%s'\n", arg); long res = syscall(SYS_demo, arg); printf("System call returned %ld.\n", res); return res; }

Install the kernel

If you're using Parallels this would be a good time to take a snapshot, just in case ;)

sudo dpkg --install linux-image-5.10.70-dbg_5.10.70-1_arm64.deb sync sudo init 6

Test the thing

gcc -o demo demo.c ./demo "but why?" sudo dmesg ./demo "For the fun of it ;)"
[ 55.833906] demo called with 'but why?' [ 71.208406] demo called with 'For the fun of it ;)'