---
# System prepended metadata

title: '2025 NCU Linux Project 1 [Group 5]'

---

# 2025 NCU Linux Project 1 [Group 5]


### 組長：林緯宸(114525001) 
### 組員：簡文勝(114522008)、林暐智(114522014)、張文瀚(114522087)
##

## Part 1: Source Code & Add a System Call
下載kernel
```
wget -P ~/ https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.137.tar.xz

tar -xvf linux-5.15.137.tar.xz -C /usr/src
```
創建mygetphy資料夾放入以下Source Code
```
cd /usr/src/linux-5.15.137

mkdir mygetphy

cd mygetphy
```
**Source Code**
```c
#include <linux/kernel.h>
#include <linux/syscalls.h>
#include <linux/mm.h>
#include <linux/sched/mm.h>
#include <asm/pgtable.h>   // pgd/p4d/pud/pmd helpers, pmd_large, masks

SYSCALL_DEFINE1(mygetphy, void *, va)
{
    unsigned long vaddr;
    unsigned long paddr;
    struct mm_struct *mm; // 描述 process 的整個 virtual mem. space（哪裡是 code / heap / stack / mmap）
    pgd_t *pgd;
    p4d_t *p4d;
    pud_t *pud;
    pmd_t *pmd;
    pte_t *pte;
    spinlock_t *ptl; // 修改/讀取 PTE 時避免race condition
    phys_addr_t base; // Physical page frame address base(加上 offset 後才是Physical address)

    vaddr = (unsigned long)va;
    paddr = 0;
    mm = current->mm;

    mmap_read_lock(mm); // 對這個 process 的 mem. 資料結構加 read_lock，避免同時有在改 page table

    pgd = pgd_offset(mm, vaddr); // 用 vaddr 算出它在 PGD 裡的那個項目
    if (pgd_none(*pgd) || pgd_bad(*pgd))
        goto out; // 如果空或壞，直接跳到 out，回傳 0

    p4d = p4d_offset(pgd, vaddr); // 從 PGD 找到 P4D
    if (p4d_none(*p4d) || p4d_bad(*p4d))
        goto out;

    pud = pud_offset(p4d, vaddr); // 從 P4D 找到 PUD
    if (pud_none(*pud) || pud_bad(*pud))
        goto out;

    pmd = pmd_offset(pud, vaddr); // 從 PUD 找到 PMD
    if (pmd_none(*pmd) || pmd_bad(*pmd))
        goto out;

/*都沒提早跳，代表這個 VA 所在的「大範圍」在 page table 中是有東西的，接下來要看是 huge page or 4KB page*/

    /* THP(Transparent Huge Page), huge page(2MB) at PMD level, no need PTE anymore */
#ifdef PMD_PAGE_MASK
    if (pmd_large(*pmd)) { // 如果是 huge page
        base = (phys_addr_t)pmd_pfn(*pmd) << PAGE_SHIFT; // 從 PMD entry 拿到 PFN，PAGE_SHIFT 轉成這個 huge page 的 page frame address base
        paddr = base | (vaddr & ~PMD_PAGE_MASK); // 加上 offset 得到最終的 physical address
        goto out;
    }
#endif

    /* Normal 4KiB page 要進到最後一層 PTE */
    pte = pte_offset_map_lock(mm, pmd, vaddr, &ptl); // 算出該 VA 對應哪一個 PTE，把那一小塊 page table 映射到 kernel address space，同時把 ptl（那個 page table 對應的 lock）鎖住。
    if (!pte)
        goto out;

    if (pte_present(*pte)) {
        base = (phys_addr_t)pte_pfn(*pte) << PAGE_SHIFT; // 從 PTE entry 拿到 PFN，PAGE_SHIFT 轉成這個 page 的 page frame address base
        paddr = base | (vaddr & ~PAGE_MASK); // 加上 offset 得到最終的 physical address
    }
    pte_unmap_unlock(pte, ptl);

out:
    mmap_read_unlock(mm);
    return paddr;   /* 0 表示未映射 */
}
```
在mygetphy下創建Makefile
```
obj-y := mygetphy.o
```
回到kernel資料夾並編輯該資料夾下原有的Makefile
```
cd /usr/src/linux-5.15.137
```
在第二個core-y後加上 mygetphy/
```
core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ mygetphy/
```
修改 syscall_64.tbl檔 在最後一行添加上 system call
```
450    common  mygetphy     sys_mygetphy
```
編輯 syscalls.h 檔加上
```c
asmlinkage long sys_mygetphy(void);
```
編譯與安裝kernel 並重啟系統
```
make localmodconfig

make -j4

make modules_install -j4

update-grub

reboot
```

## Part2: Result
### Question 1
在任意資料夾下新增q1.c
```c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>


#define SYS_GET_PHY  450   // <-- 這裡換成你的 syscall number


unsigned long my_get_physical_addresses(void *va) {
   return syscall(SYS_GET_PHY, va);
}


int main(void) {
   unsigned long length = 4 * 4096;


   // malloc
   void *addr = malloc(length);
   if (addr == NULL) {
       perror("mmap");
       return 0;
   }


   printf("malloc() returned address = %p\n\n", addr);
   printf("Check VA -> PA before access:\n");


   for (size_t i = 0; i < length; i += 4096) {
       unsigned long pa = my_get_physical_addresses((void *)addr + i);
       printf("  VA: %p -> PA: %p\n", (char *)addr + i, (void *)pa);
   }


   printf("\nTouching memory (write 1 byte per page)...\n");
   for (size_t i = 0; i < length; i += 4096) {
       ((char *)addr)[i] = 42;
   }


   printf("\nCheck VA -> PA after access:\n");
   for (size_t i = 0; i < length; i += 4096) {
       unsigned long pa = my_get_physical_addresses((void *)addr + i);
       printf("  VA: %p -> PA: %p\n", (char *)addr + i, (void *)pa);
   }


   return 0;
}

```
編譯&執行結果
```
gcc -o q1 q1.c

./q1
```
![Q1_result](https://hackmd.io/_uploads/B14bQL7xbg.png)

### Question 2
在任意資料夾下建立python虛擬環境並執行
```
python -m venv venv

source ./venv/bin/activate
```
新增my_get_phy.c並編譯
```c
//  my_get_phy.c
#include <unistd.h>

#define SYS_mygetphy 450 // 你設定的 syscall number

// Python 會用 ctypes 呼叫這個函數
unsigned long my_get_physical_addresses(void *virtual_addr) {
    return (unsigned long)syscall(SYS_mygetphy, virtual_addr);
}
```
```
gcc -Wall -O2 -fPIC -shared -o lib_my_get_phy.so my_get_phy.c
```

新增q2.py

```python
import ctypes

# -----------------------------
# C syscall wrapper
# -----------------------------
lib = ctypes.CDLL('./lib_my_get_phy.so')
lib.my_get_physical_addresses.argtypes = [ctypes.c_void_p]
lib.my_get_physical_addresses.restype = ctypes.c_ulong

def virt2phys(addr):
    """VA -> PA"""
    return lib.my_get_physical_addresses(ctypes.c_void_p(addr))

# -----------------------------
# sbrk(0) break
# -----------------------------
libc = ctypes.CDLL("libc.so.6")
libc.sbrk.restype = ctypes.c_void_p
libc.sbrk.argtypes = [ctypes.c_long]

def get_heap_break():
    return libc.sbrk(0)

PAGE_SIZE = 4096

print("=== 初始 program break ===")
break_before = get_heap_break()
print(f"program break: {hex(break_before)}\n")

malloc_size = 4096  # 每次 malloc 4KB
buffers = []

# 循環 malloc 觀察 break 變化
for i in range(50):
    buf = ctypes.create_string_buffer(malloc_size)
    buffers.append(buf)
    break_now = get_heap_break()

    # 代表 heap 變大了
    if break_now != break_before:
        print(f"malloc {i+1}: break = {hex(break_now)}")
        print(f"  -> program break 增加了 {break_now - break_before} bytes")

        # 列出新增 break 範圍的 VA -> PA
        start_va = break_before
        end_va = break_now
        print("\n=== 新增 heap VA->PA ===")
        for va in range(start_va, end_va, PAGE_SIZE):
            try:
                pa = virt2phys(va)
                if pa == 0:
                    pa_str = "未分配"
                else:
                    pa_str = f"0x{pa:x}"  # 十六進位格式
            except Exception:
                pa_str = "未分配"
            print(f"VA: 0x{va:x} -> PA: {pa_str}")
        print()
        break_before = break_now

```
執行結果
```
python q2.py
```
![Q2_result](https://hackmd.io/_uploads/ByzGX8mxZl.png)
