## 版本及環境
VM 版本:ubuntu-18.04.6-desktop-amd64.iso
Kernel 版本:5.15.137
VM 環境:VMware Workstation 16 Pro
## 環境設置
### VM 下載
https://releases.ubuntu.com/bionic/
找不到 Debian 的舊版本載點,所以還是換回 Ubuntu,選了 ubuntu-18.04.6-desktop-amd64.iso 下載,不確定是否能用 server 版實作。
### 查看目前已安裝的 Kernel 版本

### 下載 Kernel Source Code
選取了跟組員相同的版本下載。
```shell!
$ wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.137.tar.xz
```
```shell!
$ tar Jxvf linux-5.15.137.tar.xz
```
### 下載所需 Package
```shell
$ sudo apt update
$ sudo apt install build-essential libncurses-dev libssl-dev libelf-dev bison flex -y
```
### 清除 Package 暫存檔案及舊 Package
```shell!
$ sudo apt clean && sudo apt autoremove -y
```
#### apt 下載來源
```shell
$ vim /etc/apt/sources.list
// 參考選項:
// http://us.archive.ubuntu.com/ubuntu/
// http://ubuntu.cs.nycu.edu.tw/
// http://free.nchc.org.tw/ubuntu/
// 選擇不同站點影響下載速度,不過也可能因為站點不是最新檔案無法下載
```

## 環境測試
> 參考文章:https://phoenixnap.com/kb/build-linux-kernel
### 清理 Kernel 設定
```shell!
$ sudo make mrproper
```
### 設定 Kernel
```shell!
$ cd linux-5.15.137
// 複製原本的設定
$ cp -v /boot/config-$(uname -r) .config
// GUI 介面
$ make menuconfig
// 文字介面
$ make localmodconfig
```
#### 關閉憑證檢驗
```shell!
$ scripts/config --disable SYSTEM_TRUSTED_KEYS
$ scripts/config --disable SYSTEM_REVOCATION_KEYS
$ scripts/config --set-str CONFIG_SYSTEM_TRUSTED_KEYS ""
$ scripts/config --set-str CONFIG_SYSTEM_REVOCATION_KEYS ""
```
### 編譯 Kernel
```shell!
$ make -j$(分配的核心數量)
$ sudo make modules_install
$ sudo make install
```
### 重新啟動 & Grub
```shell!
$ sudo update-grub
$ sudo reboot
```
重開機過程中按住 shift 可以進入 Grub 頁面
進不了 Grub 可以參考以下文章調整設定 ( `/etc/default/grub` ):https://docs.vmware.com/tw/VMware-NSX-T-Data-Center/3.2/installation/GUID-4630C9D5-71FB-4991-AC1D-9FDBA0B86120.html
進入 Grub 後選擇 `Advanced options for Ubuntu` -> `Ubuntu, with Linux 5.15.137` ( 視選用的 Kernel 版本而定 )。
透過 `uname -r` 可以查看 Kernel 版本是否成功被替換。



### 過程中的問題
#### sed: can't read modules.order: No such file or directory

- 關閉憑證檢驗sudo
#### BTF: .tmp_vmlinux.btf: pahole (pahole) is not available

方法一
```shell!
$ sudo apt install dwarves
```
方法二
```shell!
sudo vim .config
將 CONFIG_DEBUG_INFO_BTF 改為 n
```
#### kernel/built-in.a: member kernel/trace/trace_kdb.o in archive is not an object

#### 編譯過程當機
不要使用虛擬機中所有的核心去編譯
#### VM 當機後無法直接重啟

關掉 Accelerate 3D graphics
## 新增 System Call
### 建立資料夾
```shell!
$ cd linux-5.15.137
$ mkdir mycall
$ cd mycall
```
### 撰寫 System Call
#### 編輯程式
```shell!
$ vim hello.c
```
#### 程式內容
```c=
#include <linux/kernel.h>
#include <linux/syscalls.h>
SYSCALL_DEFINE0(hello){
printk("Hello world!\n");
return 0;
}
```
### 建立 Makefile
> 與 hello.c 同一目錄
#### 編輯
```shell!
$ vim Makefile
```
#### 內容
```c=
obj-y := hello.o
```
### 修改 Linux 原始的 Makefile
```shell!
$ cd ..
$ vim Makefile
```
#### 將新增的資料夾加入編譯清單
```c=
core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ mycall/
```

### 將 System Call 加入 Syscall Table
```shell!
$ vim arch/x86/entry/syscalls/syscall_64.tbl
```

### 將 System Call 加入 syscall 的 header file
```shell!
$ vim include/linux/syscalls.h
```
```c=
asmlinkage long sys_hello(void);
```

### 重新編譯
上述檔案完成修改後重新編譯即可
```shell!
$ make -j2
$ sudo make modules_install
$ sudo make install
```
### 測試 System Call
```c=
#include <unistd.h>
#include <stdio.h>
int main(){
long int sys = syscall(449);
printf("Return value of sys_hello: %ld\n", sys);
return 0;
}
```
```shell!
$ gcc -o hello hello.c
$ ./hello
$ sudo dmesg
```
### 測試結果


## Linux 基本架構
## New Syscall: my_get_physical_addresses
### pgd_offset
```c=
// include/linux/pgtable.h#L132
#define pgd_offset(mm, address) pgd_offset_pgd((mm)->pgd, (address))
// include/linux/pgtable.h#L123
static inline pgd_t *pgd_offset_pgd(pgd_t *pgd, unsigned long address)
{
return (pgd + pgd_index(address));
};
// include/linux/pgtable.h#L85
#define pgd_index(a) (((a) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1))
```
Reference:
https://elixir.bootlin.com/linux/v5.15.167/source/include/linux/pgtable.h#L132
https://elixir.bootlin.com/linux/v5.15.167/source/include/linux/pgtable.h#L123
https://elixir.bootlin.com/linux/v5.15.167/source/include/linux/pgtable.h#L85
### pud_offset
### Content
```c=
#include <linux/kernel.h>
#include <linux/syscalls.h>
SYSCALL_DEFINE1(my_get_physical_addresses, void *vaddr) {
pgd_t *pgd_base = current->mm->pgd;
pgd_t *pgd_entry;
printk("pgd_base = 0x%lx\n",p4d_val(*p4d))
pgd_entry = pgd_offset(current->mm, vaddr);
return 0;
}
```
## Question 1: copy-on-write
### Source code
```c=
#include <stdio.h>
//void * my_get_physical_addresses(void *);
int global_a=123; //global variable
void hello(void)
{
printf("======================================================================================================\n");
}
int main()
{
int loc_a;
void *parent_use, *child_use;
printf("===========================Before Fork==================================\n");
parent_use=my_get_physical_addresses(&global_a);
printf("pid=%d: global variable global_a:\n", getpid());
prinft("Offest of logical address:[%p] Physical address:[%p]\n", &global_a,parent_use);
printf("========================================================================\n");
if(fork())
{ /*parent code*/
printf("vvvvvvvvvvvvvvvvvvvvvvvvvv After Fork by parent vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n");
parent_use=my_get_physical_addresses(&global_a);
printf("pid=%d: global variable global_a:\n", getpid());
prinft("******* Offset of logical address:[%p] Physical address:[%p]\n", &global_a,parent_use);
printf("vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n");
wait();
}
else
{ /*child code*/
printf("llllllllllllllllllllllllll After Fork by child llllllllllllllllllllllllllllllll\n");
child_use=my_get_physical_addresses(&global_a);
printf("******* pid=%d: global variable global_a:\n", getpid());
prinft("******* Offset of logical address:[%p] Physical address:[%p]\n", &global_a, child_use);
printf("llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll\n");
printf("____________________________________________________________________________\n");
/*----------------------- trigger CoW (Copy on Write) -----------------------------------*/
global_a=789;
printf("iiiiiiiiiiiiiiiiiiiiiiiiii Test copy on write in child iiiiiiiiiiiiiiiiiiiiiiii\n");
child_use=my_get_physical_addresses(&global_a);
printf("******* pid=%d: global variable global_a:\n", getpid());
prinft("******* Offset of logical address:[%p] Physical address:[%p]\n", &global_a, child_use);
printf("iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii\n");
printf("____________________________________________________________________________\n");
sleep(1000);
}
}
```
## Question 2:
## 附錄
### Vim 的操作技巧
- `gg` 移動至文件最上方
- `G` 移動至文件最下方
- `/[anything]+` 於文件中搜尋特定字串
- `enter` 後 `n` 查看下一個搜尋到的字串
- `:set number` 顯示行數