# New a System Call in Linux Kernel v5.4.188
###### tags:`OSS` `Linux`
## Environment
*我是用 Proxmox 架的 VM 來編譯跟測試*
kernel version: 5.4.188
cpu: xeon e5-2690 v4 (x86_64 bits)
distribution: ubuntu 20.04
## Tutorial
##### 建議可以先嘗試編譯一次未修改的kernel並install成功,再來試新增system call 跟編譯
```linux=
# 下載kernel
wget wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.4.188.tar.xz
tar -xvf linux-5.4.188.tar.xz
# 安裝 dependencies
sudo apt-get install build-essential libncurses-dev bison flex libssl-dev libelf-dev
```
### 正常編譯
```linux=
cd linux-5.4.188
cp -v /boot/config-$(uname -r) .config
make menuconfig
## 連按兩下Esc離開 (保持預設)
# use 4 core/thread
sudo make -j 4
# get max core/thread numbers using nproc command
sudo make -j $(nproc)
sudo make modules
sudo make modules_install
sudo make install
```
Compile 跟 Install 完後要 [Sign](#Sign-Linux-Kernel-for-Secure-Boot)
### 自訂 system call
```linux=
# 清除不需要的 object file, config, and dependencies
sudo make mrproper
# 在source code 中建立hello資料夾
mkdir hello
cd hello
vim hello.c
```
- hello.c
```c=
#include <linux/kernel.h>
#include <linux/linkage.h>
#include <linux/syscalls.h>
asmlinkage long sys_hello(void)
{
printk("Hello world\n");
return 0;
}
SYSCALL_DEFINE0(hello) {
printk("Hi, nice to meet you.\n");
return sys_hello();
}
```
- 如果是有回傳值,或是有參數傳入的情況,則參考以下的hello.c
```c=
#include <linux/kernel.h>
#include <linux/linkage.h>
#include <linux/syscalls.h>
asmlinkage long sys_hello(int n)
{
printk("Hello World\n");
printk("The input number is: %d\n", n);
return 0;
}
SYSCALL_DEFINE1(hello, int, n) {
printk("Hi, nice to meet you.\n");
return sys_hello(n);
}
```
- 同目錄新增 Makefile
```makefile=
vim Makefile
obj-y := hello.o
```
- 修改 source code 根目錄下的 Makefile,找到
```linux=
core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/
```
新增 ```hello/``` 在後方,變成
```linux=
core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/ hello/
```
![](https://i.imgur.com/wC3onqe.png)
- 修改 ```./linux-5.4.188/arch/x86/entry/syscalls/syscall_64.tbl```,新增編號
```linux=
436 common hello __x64_sys_hello
```
:::success
- From linux kernel v4.17 onwards, x86_64 system calls begin with "__x64_sys"
- [reference](https://github.com/torvalds/linux/commit/d5a00528b58cdb2c71206e18bd021e34c4eab878)
- "hello": name of the system call
- "__x64_sys_hello": name of the function implementing it
:::
![](https://i.imgur.com/Bqu6tBg.png)
- 在 ```./linux-5.4.188/include/linux/syscalls.h``` 找到
```#ifndef CONFIG_ARCH_HAS_SYSCALL_WRAPPER``` 並在下方新增 自訂systemcal call 的 prototype
```linux=
asmlinkage long sys_hello(void);
```
或依照是否有參數傳入情況
```linux=
asmlinkage long sys_hello(int n);
```
![](https://i.imgur.com/9nPio7d.png)
:::info
:bulb: kernel v4.10 後,kernel 會用script自動計算 __NR_syscalls 因此不再需修改```/arch/x86/entry/entry_64.S```及```arch/x86/include/generated/uapi/asm/unistd_64.h```
只需要更改 ```arch/x86/entry/syscalls/syscall_64.tbl``` 和 ```include/linux/syscalls.h``` 就可以了
詳見 [Patch - ARM: convert to generated system call tables](https://patchwork.kernel.org/project/linux-arm-kernel/patch/E1bwa6s-0004ZY-4k@rmk-PC.armlinux.org.uk/)
:::
#### Compile the Kernel
```linux=
make oldconfig/ make menuconfig
sudo make -jN (N: by "nproc")
sudo make modules
sudo make modules_install
sudo make install
```
##### If running into error
```
make[1]: *** 沒有規則可製作目標「debian/canonical-certs.pem」,由「certs/x509_certificate_list」 需求。 停止。
make[1]: *** 正在等待未完成的作業....
# make[1]: *** No rule to make target 'debian/canonical-certs.pem', needed by 'certs/x509_certificate_list'. Stop.
# make: *** [Makefile:1809: certs] Error 2
```
##### Solution:
```linux=
sudo scripts/config --disable SYSTEM_TRUSTED_KEYS
sudo scripts/config --disable SYSTEM_REVOCATION_KEYS
sudo make clean
sudo make -j8
# or sudo make -j $(nproc)
sudo make modules
sudo make modules_install
sudo make install
```
[reference](https://askubuntu.com/questions/1329538/compiling-the-kernel-5-11-11)
### Sign Linux Kernel for Secure Boot
只要 Secure Boot 有打開,GRUB2(2.02+dfsg1-5ubuntu1) 就不會載入未 sign 過的 kernel
Ubuntu 18.04 之後的版本也會禁止升級未簽署的 kernel
理論上把主機板的 Secure Boot 關閉也行 (未實驗過)
```linux=
# 在 source code 的 root 中新增 mokconfig.cnf
vim mokconfig.cnf
```
- mokconfig.cnf
```linux=
HOME = .
[ req ]
distinguished_name = req_distinguished_name
x509_extensions = v3
string_mask = utf8only
prompt = no
[ req_distinguished_name ]
countryName = <YOURcountrycode>
stateOrProvinceName = <YOURstate>
localityName = <YOURcity>
0.organizationName = <YOURorganization>
commonName = Secure Boot Signing Key
emailAddress = <YOURemail>
[ v3 ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical,CA:FALSE
extendedKeyUsage = codeSigning,1.3.6.1.4.1.311.10.3.6
nsComment = "OpenSSL Generated Certificate"
```
- 替換掉 <YOUR...> 等欄位內容,內容隨意
for example
```linux=
HOME = .
[ req ]
distinguished_name = req_distinguished_name
x509_extensions = v3
string_mask = utf8only
prompt = no
[ req_distinguished_name ]
countryName = TW
stateOrProvinceName = Taiwan
localityName = Taipei
0.organizationName = FJCU
commonName = Secure Boot Signing Key
emailAddress = example@example.com
[ v3 ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical,CA:FALSE
extendedKeyUsage = codeSigning,1.3.6.1.4.1.311.10.3.6
nsComment = "OpenSSL Generated Certificate"
```
- 回到bash
```linux=
# 產生private key 跟public key
openssl req -config ./mokconfig.cnf \
-new -x509 -newkey rsa:2048 \
-nodes -days 36500 -outform DER \
-keyout "MOK.priv" \
-out "MOK.der"
# mokutil 要 DER 格式, sbsign 要 PEM 格式
openssl x509 -in MOK.der -inform DER -outform PEM -out MOK.pem
# enroll the key
sudo mokutil --import MOK.der
# 輸入自訂密碼等等要用
# 重啟系統,會進入藍底的 MOKManager
# 選擇 Enroll MOK -> View Key
# 確定這是剛剛建立的key,並enroll -> 輸入密碼
# 繼續 boot
```
```linux=
# 確定有enroll成功
sudo mokutil --list-enrolled
# 簽署已安裝的kernel
# 替換 kernel 版本, e.g. 5.4.188 -> any version
sudo sbsign --key MOK.priv --cert MOK.pem /boot/vmlinuz-5.4.188 --output /boot/vmlinuz-5.4.188.signed
# 將未簽署的 kernel 的 initram 複製到已簽署的 kernel上
sudo cp /boot/initrd.img-5.4.188{,.signed}
# 更新grub
sudo update-grub
# reboot,在grub中選擇已簽署的kernel開機
# 如果開機成功,就可以移除未簽署的 kernel
sudo mv /boot/vmlinuz-5.4.188{.signed,}
sudo mv /boot/initrd.img-5.4.188{.signed,}
sudo update-grub
# check kernel version
uname -mrs
```
### test
- 新增一個 test.c
```c=
#include <unistd.h>
#include <stdio.h>
#include <sys/syscall.h>
#define SYS_hello 436
int main(void) {
syscall(SYS_hello);
puts("Done! Please check dmesg");
return 0;
}
```
- 如果是有參數傳入的 test.c 版本
```c=
#include <unistd.h>
#include <stdio.h>
#include <sys/syscall.h>
#define SYS_hello 436
int main(void) {
int n;
scanf("%d", &n);
syscall(SYS_hello, n);
puts("Done! Please check dmesg");
return 0;
}
```
- compile and execute
```c=
gcc test.c -o test.o
./test.o
```
- check dmesg
```linux=
sudo dmesg
```
## Reference
- [鳥哥: Linux 核心編譯與管理](https://linux.vbird.org/linux_basic/centos7/0540kernel.php#intro)
- [Signing a Linux Kernel for Secure Boot](https://gloveboxes.github.io/Ubuntu-for-Azure-Developers/docs/signing-kernel-for-secure-boot.html)
- [Kernel Hacking - System Calls(3/3)](https://marcocetica.com/posts/kernel_hacking_part3/)
- [Linux 讀書會 - System call](https://hackmd.io/@combo-tw/Linux-%E8%AE%80%E6%9B%B8%E6%9C%83/%2F%40a29654068%2FHyD4Lu_Dr)
- [Build Linux Kernel, Add a New System Call to Linux v2.4](http://osnet.cs.nchu.edu.tw/powpoint/Distributed_OS/Os/system_call.pdf)
- [增加一個 System Call 到 Linux Kernel (v4.x)](https://wenyuangg.github.io/posts/linux/linux-add-system-call.html)
- [How to compile and install Linux Kernel 5.16.9 from source code](https://www.cyberciti.biz/tips/compiling-linux-kernel-26.html)
- [Attempting to compile kernel yields a certification error](https://unix.stackexchange.com/questions/293642/attempting-to-compile-kernel-yields-a-certification-error)
- [Preparing the installer to update Linux kernel in a VirtualBox causes errors connected with CONFIG_X86_X32 and modules.order](https://unix.stackexchange.com/questions/680261/preparing-the-installer-to-update-linux-kernel-in-a-virtualbox-causes-errors-con)
- CONFIG_X86_X32 enabled but no binutils support
- 64 位處理器執行原生 32 位程式,需要開啟核心 CONFIG_X86_X32 選項
- CONFIG_X86_X32 選項需要toolchain支援編譯 elf32_x86_64 目標
- [Sign kernel modules](https://www.twblogs.net/a/5b8a366d2b71775d1ce60496)
- https://wiki.gentoo.org/wiki/Signed_kernel_module_support
- [SYSCALL: 添加零个参数的系统调用](https://biscuitos.github.io/blog/SYSCALL_PARAMENTER_ZERO/)
- [X86_64 架构增加一个系统调用](https://biscuitos.github.io/blog/SYSCALL_ADD_NEW_X86_64/)
- [Linux kernel 5.0 增加 System Call](http://www.cjwind.idv.tw/Add-system-call-to-linux/)
- [關於新增system calls可能遭遇的問題](https://joj4211.pixnet.net/blog/post/133723316)