---
# System prepended metadata

title: Linux Project 1 ( frostbean )
tags: [有神快拜, 電神開講]

---

## 版本及環境
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 版本
![image](https://hackmd.io/_uploads/ryFzmEvWyx.png)

### 下載 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/
// 選擇不同站點影響下載速度，不過也可能因為站點不是最新檔案無法下載
```

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

## 環境測試
> 參考文章：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 版本是否成功被替換。

![image](https://hackmd.io/_uploads/H1eOVlUo-1g.png)
![image](https://hackmd.io/_uploads/S1VUEIsZke.png)
![image](https://hackmd.io/_uploads/Hy34SIjZkg.png)

### 過程中的問題
#### sed: can't read modules.order: No such file or directory
![image](https://hackmd.io/_uploads/ryTsFlKZyx.png)
- 關閉憑證檢驗sudo 

#### BTF: .tmp_vmlinux.btf: pahole (pahole) is not available
![image](https://hackmd.io/_uploads/SyCfBWFWke.png)
方法一
```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
![image](https://hackmd.io/_uploads/rkCPIJcWkg.png)

#### 編譯過程當機
不要使用虛擬機中所有的核心去編譯

#### VM 當機後無法直接重啟
![image](https://hackmd.io/_uploads/S1xgzBiWJl.png)
關掉 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/
```

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

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

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

### 將 System Call 加入 syscall 的 header file
```shell!
$ vim include/linux/syscalls.h
```

```c=
asmlinkage long sys_hello(void);
```

![image](https://hackmd.io/_uploads/BJFblcj-1e.png)

### 重新編譯
上述檔案完成修改後重新編譯即可

```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
```

### 測試結果
![image](https://hackmd.io/_uploads/Skel3b6W1g.png)
![image](https://hackmd.io/_uploads/SJjlbMaWJe.png)

## 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` 顯示行數


