---
# System prepended metadata

title: Linux Kernel Project 1

---

# Linux Kernel Project 1

```
OS: Ubuntu 22.04
ARCH: X86_64
Source Version: 6.6
```

## Build Linux Kernel

```shell
# download the kernel code
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.tar.xz
tar -xvf linux-6.6.tar.xz
cd linux-6.6
# Install required dependencies
sudo apt update && sudo apt install make gcc libncurses-dev flex bison
make allnoconfig
```

```
# Initialize kernel config
make menuconfig
64-bit kernel -> Enable
Executable file formats -> Enable all
Device Drivers > Character devices > Serial drivers and 8250/16550 and compatible serial support -> Enable
Device Drivers > Character devices > Console on 8250/16550 and compatible serial port -> Enable
General Setup > Initial RAM filesystem and RAM disk (initramfs/initrd) support -> Enable
Process type and features -> Linux guest support -> Support for running PVH guests -> Enable
```

![image.png](https://hackmd.io/_uploads/S13PsdmmT.png)
![image.png](https://hackmd.io/_uploads/r1sFiumQp.png)
![image.png](https://hackmd.io/_uploads/SkopsuQQT.png)
![image.png](https://hackmd.io/_uploads/SJz8IA4XT.png)


<!--
Cryptographic API -> Certificates for signature checking

```
File name or PKCS#11 URI of module signing key -> 改為空值
Additional X.509 keys for default system keyring -> 改為空值
Provide system-wide ring of blacklisted keys -> unselect
```

-->

```shell
# Install required dependencies
sudo apt install libssl-dev libelf-dev
make -j <num_cpu>
```

## Build Root FS

```shell
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar -xf busybox-1.36.1.tar.bz2
cd busybox-1.36.1
make menuconfig # Select build static binary
make install
cd _install
mkdir -p lib lib64 proc sys etc etc/init.d
cat > ./etc/init.d/rcS << EOF
#!/bin/sh
 # Mount the /proc and /sys filesystems
mount -t proc none /proc
mount -t sysfs none /sys
 # Populate /dev
/sbin/mdev -s
EOF

chmod +x etc/init.d/rcS
find . | cpio -o --format=newc | gzip > ../../linux-6.6/rootfs.img.gz
```

![image.png](https://hackmd.io/_uploads/ryin_pmQa.png)


## Run Kernel

```shell
sudo apt install qemu qemu-kvm
qemu-system-x86_64 -kernel vmlinux -nographic -initrd rootfs.img.gz -append "root=/dev/ram rdinit=/sbin/init console=ttyS0"
```

## Add System Call

`arch/x86/entry/syscall_64.tbl`

Add line after 377 line(453	64	map_shadow_stack    sys_map_shadow_stack)
```
454 common  my_get_physical_addresses   sys_my_get_physical_addresses   
```

這個檔案會在 compile 階段被讀取後轉為 header file(`arch/x86/include/generated/asm/syscalls_64.h`)


`include/linux/syscalls.h`

Add line after 942(`asmlinkage long sys_map_shadow_stack(unsigned long addr, unsigned long size, unsigned int flags);`)

```c
asmlinkage long sys_my_get_physical_addresses(void *);
```

Create `kernel/project1.c` and add it to makefile

```c
#include <linux/syscalls.h>
// #define DEBUG

SYSCALL_DEFINE1(my_get_physical_addresses, void *, addr_p)
{
	unsigned long vaddr = (unsigned long)addr_p;
	pgd_t *pgd;
	p4d_t *p4d;
	pud_t *pud;
	pmd_t *pmd;
	pte_t *pte;
	unsigned long paddr = 0;
	unsigned long page_addr = 0;
	unsigned long page_offset = 0;

	pgd = pgd_offset(current->mm, vaddr);
#ifdef DEBUG
	printk("pgd_val = 0x%lx\n", pgd_val(*pgd));
	printk("pgd_index = %lu\n", pgd_index(vaddr));
#endif
	if (pgd_none(*pgd)) {
		printk("not mapped in pgd\n");
		return 0;
	}

	p4d = p4d_offset(pgd, vaddr);
#ifdef DEBUG
	printk("p4d_val = 0x%lx\n", p4d_val(*p4d));
	printk("p4d_index = %lu\n", p4d_index(vaddr));
#endif
	if (p4d_none(*p4d)) {
		printk("not mapped in p4d\n");
		return 0;
	}

	pud = pud_offset(p4d, vaddr);
#ifdef DEBUG
	printk("pud_val = 0x%lx\n", pud_val(*pud));
	printk("pud_index = %lu\n", pud_index(vaddr));
#endif
	if (pud_none(*pud)) {
		printk("not mapped in pud\n");
		return 0;
	}

	pmd = pmd_offset(pud, vaddr);
#ifdef DEBUG
	printk("pmd_val = 0x%lx\n", pmd_val(*pmd));
	printk("pmd_index = %lu\n", pmd_index(vaddr));
#endif
	if (pmd_none(*pmd)) {
		printk("not mapped in pmd\n");
		return 0;
	}

	pte = pte_offset_kernel(pmd, vaddr);
#ifdef DEBUG
	printk("pte_val = 0x%lx\n", pte_val(*pte));
	printk("pte_index = %lu\n", pte_index(vaddr));
#endif
	if (pte_none(*pte)) {
		printk("not mapped in pte\n");
		return 0;
	}

	/* Page frame physical address mechanism | offset */
	page_addr = pte_val(*pte) & PAGE_MASK;
	page_offset = vaddr & ~PAGE_MASK;
	paddr = page_addr | page_offset;
#ifdef DEBUG
	printk("page_addr = %lx, page_offset = %lx\n", page_addr, page_offset);
	printk("vaddr = %lx, paddr = %lx\n", vaddr, paddr);
#endif

	return paddr;
}
```

> `virt_to_phys` 只能轉 kernel space 的 virtual address，因此必須從頭用 page table 查找

<!--
> `virt_to_phys` is defined in `asm/io.h`. Actually, it will map to `arch/x86/include/asm/io.h`.
> ```c
> static inline phys_addr_t virt_to_phys(volatile void *address)
> {
> 	return __pa(address);
> }
> ```
> `__pa` is defined in `arch/x86/include/asm/page.h`
> ```c
> #define __pa(x)		__phys_addr((unsigned long)(x))
> ```
> `__phys_addr` is defined in `arch/x86/include/asm/page_64.h`
> ```c
> #define __phys_addr(x)		__phys_addr_nodebug(x)
> static __always_inline unsigned long __phys_addr_nodebug(unsigned long x)
> {
> 	unsigned long y = x - __START_KERNEL_map;
> 
> 	/* use the carry flag to determine if x was < __START_KERNEL_map */
> 	x = y + ((x > y) ? phys_base : > (__START_KERNEL_map - PAGE_OFFSET));
> 
> 	return x;
> }
> ```
-->

`kernel/Makefile`

```
obj-y     = fork.o exec_domain.o panic.o \
	    cpu.o exit.o softirq.o resource.o \
	    sysctl.o capability.o ptrace.o user.o \
	    signal.o sys.o umh.o workqueue.o pid.o task_work.o \
	    extable.o params.o \
	    kthread.o sys_ni.o nsproxy.o \
	    notifier.o ksysfs.o cred.o reboot.o \
	    async.o range.o smpboot.o ucount.o regset.o ksyms_common.o \
		project1.o
```

## Build Test Binary

Add `project1.c` to anywhere

```c
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <sys/syscall.h> /* Definition of SYS_* constants */
#include <unistd.h>
#include <stdlib.h>

extern void *func1(void *);
extern void *func2(void *);
extern int main();

void *my_get_physical_addresses(void *vaddr)
{
  return syscall(454, vaddr);
}

struct data_
{
  int id;
  char name[16];
};
typedef struct data_ sdata;
static __thread sdata tx; // thread local variable

int a = 123; // global variable
int *c;      // heap variable

void hello(int pid)
{
  int b = 10; // local variable

  b = b + pid;
  // global variable
  printf("In thread %d \nthe value of global variable a is %d, the offset of the logical address of a is %p, ", pid, a, &a);
  printf("the physical address of global variable a is %p\n", my_get_physical_addresses(&a));
  // local variable
  printf("the value of local variable b is %d, the offset of the logical address of b is %p, ", b, &b);
  printf("the physical address of local variable b is %p\n", my_get_physical_addresses(&b));
  // heap variable
  printf("the value of heap variable c is %d, the offset of the logical address of c is %p, ", *c, c);
  printf("the physical address of heap variable c is %p\n", my_get_physical_addresses(c));
  // thread local variable
  printf("the offset of the logical address of thread local variable tx is %p, ", &tx);
  printf("the physical address of thread local variable tx is %p\n", my_get_physical_addresses(&tx));
  // function
  printf("the offset of the logical address of function hello is %p, ", hello);
  printf("the physical address of function hello is %p\n", my_get_physical_addresses(hello));
  printf("the offset of the logical address of function func1 is %p, ", func1);
  printf("the physical address of function func1 is %p\n", my_get_physical_addresses(func1));
  printf("the offset of the logical address of function func2 is %p, ", func2);
  printf("the physical address of function func2 is %p\n", my_get_physical_addresses(func2));
  printf("the offset of the logical address of function main is %p, ", main);
  printf("the physical address of function main is %p\n", my_get_physical_addresses(main));
  // library function
  printf("the offset of the logical address of library function printf is %p, ", printf);
  printf("the physical address of library function printf is %p\n", my_get_physical_addresses(printf));
  printf("====================================================================================================================\n");
}

void *func1(void *arg)
{
  char *p = (char *)arg;
  int pid;
  pid = syscall(__NR_gettid);
  tx.id = pid;
  strcpy(tx.name, p);
  printf("I am thread with ID %d executing func1().\n", pid);
  hello(pid);
  /*
  while (1)
  {
    // printf("(%d)(%s)\n",tx.id,tx.name) ;
    sleep(1);
  }
  */
}

void *func2(void *arg)
{
  char *p = (char *)arg;
  int pid;
  pid = syscall(__NR_gettid);
  tx.id = pid;
  strcpy(tx.name, p);
  printf("I am thread with ID %d executing func2().\n", pid);
  hello(pid);
  /*
  while (1)
  {
    // printf("(%d)(%s)\n",tx.id,tx.name) ;
    sleep(2);
  }
  */
}
int main()
{
  pthread_t id[2];
  char p[2][16];
  c = (int *)malloc(sizeof(int));
  *c = 456;
  strcpy(p[0], "Thread1");
  pthread_create(&id[0], NULL, func1, (void *)p[0]);
  strcpy(p[1], "Thread2");
  pthread_create(&id[1], NULL, func2, (void *)p[1]);

  int pid;
  pid = syscall(__NR_gettid);
  tx.id = pid;
  strcpy(tx.name, "MAIN");
  printf("I am main thread with ID %d.\n", pid);
  hello(pid);
  pthread_join(id[0], NULL);
  pthread_join(id[1], NULL);
  free(c);
  /*
  while (1)
  {
    // printf("(%d)(%s)\n",tx.id,tx.name) ;
    sleep(5);
  }
  */
}

```

```shell
gcc -static project1.c -o project1
```

這個檔案 build 完要將其加入 rootfs 中才能被 guest 執行

## References

[Linux kernel on QEMU](https://blog.austint.in/2022/01/16/run-and-debug-linux-kernel-in-qemu-vm.html)
[Minimal kernel on QEMU](https://www.subrat.info/build-kernel-and-userspace/)
[embed linux bootstrap](https://gist.github.com/debuti/43e9104ae9eb59bdbb8b664c4fcf6839)
[initrd](https://docs.kernel.org/admin-guide/initrd.html)
[Page table](https://www.kernel.org/doc/gorman/html/understand/understand006.html)
[Get physical memory](https://zhuanlan.zhihu.com/p/642419727)