# 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)