Translation Function
Kernel Virtual Address, Physical Address, Page Frame Number 之間的轉換函式
include/mm.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #ifndef _MM_H #define _MM_H
#define KERNEL_VIRT_BASE 0xFFFF000000000000 #define PAGE_TABLE_SIZE 4096 #define PAGE_SHIFT 12 #define PAGE_SIZE (1UL << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE - 1)) #define PAGE_NUM (0x40000000 / PAGE_SIZE)
#ifndef __ASSEMBLER__
#include <include/types.h>
#define KVA_TO_PA(addr) ((uint64_t) addr << 16 >> 16) #define PA_TO_KVA(addr) ((uint64_t) addr | KERNEL_VIRT_BASE) #define PA_TO_PFN(addr) ((uint64_t) addr >> PAGE_SHIFT) #define PFN_TO_PA(idx) ((uint64_t) idx << PAGE_SHIFT)
#endif #endif
|
Bookkeeping
include/mm.h
1 2 3 4 5 6 7 8
| enum page_flag { PAGE_USED = 1 << 0 };
typedef struct { uint32_t flag; void *physical; } page_t;
extern page_t page[PAGE_NUM];
|
kernel/mm.c
kernel image 所佔據的 page、kernel stack 所佔據的 page (0x8000 前)必須標示成使用中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| page_t page[PAGE_NUM];
bool is_set(uint32_t target, uint32_t val) { return !!(target & val); }
void set(uint32_t *target, uint32_t val) { *target |= val; }
void unset(uint32_t *target, uint32_t val) { *target &= ~val; }
void page_init() { extern char _kernel_end; int i = 0; for (; i < PA_TO_PFN(KVA_TO_PA(&_kernel_end)); ++i) { page[i].physical = (void *) ((physaddr_t) i << PAGE_SHIFT); page[i].flag = 0; set(&page[i].flag, PAGE_USED); } for (; i < PA_TO_PFN(KVA_TO_PA(MMIO_BASE)); ++i) { page[i].flag = 0; } for (; i < PAGE_NUM; ++i) { page[i].physical = (void *) ((physaddr_t) i << PAGE_SHIFT); page[i].flag = 0; set(&page[i].flag, PAGE_USED); } }
|
kernel/start.S
稍微試算page_t page[PAGE_NUM]的佔用空間
PAGE_NUM=1G/4K
sizeof(page_t)=8 Bytes
sizeof(page[PAGE_NUM])=16 MB
發現已經遠超過 adr 的定址範圍:+-1MB,需要將 .bss 初始化程式更改一下
1 2 3 4 5 6 7 8 9 10
| // clear bss - adr x0, __bss_start - adr x1, __bss_end + ldr x0, =__bss_start + ldr x1, =__bss_end sub x1, x1, x0 bl memzero
+ bl page_init bl main
|
page_alloc, page_free
kernel/mm.h
1 2 3 4
| page_t *get_free_page(); void *page_alloc_kernel(); void *page_alloc_user(); void page_free(void *virt_addr);
|
kernel/mm.c
page_alloc_kernel 和 page_alloc_user 取得閒置的 page 並返回 page 的 virtual address 而非 physical address,原因在於一開始我們已啟用 virtual memory。
一旦分配 page,會在 task_struct 紀錄。page_alloc_kernel 和 page_alloc_user 的差別僅紀錄該 page 是分配給 kernel space 或是 user space,但這部份尚未實作,因此兩個函式目前一樣。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| page_t *get_free_page() { extern char _kernel_end; int pfn_start = PA_TO_PFN(KVA_TO_PA(&_kernel_end)), pfn_end = PA_TO_PFN(KVA_TO_PA(MMIO_BASE));
for (; pfn_start < pfn_end && is_set(page[pfn_start].flag, PAGE_USED); ++pfn_start) ; if (pfn_start == pfn_end) return NULL;
physaddr_t phy_addr = (physaddr_t) pfn_start << PAGE_SHIFT; uintptr_t virt_addr = phy_addr | KERNEL_VIRT_BASE; memset((void *) virt_addr, 0, PAGE_SIZE);
page[pfn_start].physical = (void *) phy_addr; set(&page[pfn_start].flag, PAGE_USED);
return &page[pfn_start]; }
void *page_alloc_kernel() { page_t *page_ptr = get_free_page(); if (!page_ptr) return NULL;
physaddr_t phy_addr = (physaddr_t) page_ptr->physical; uintptr_t virt_addr = phy_addr | KERNEL_VIRT_BASE;
return (void *) virt_addr; }
void *page_alloc_user() { page_t *page_ptr = get_free_page(); if (!page_ptr) return NULL;
physaddr_t phy_addr = (physaddr_t) page_ptr->physical; uintptr_t virt_addr = phy_addr | KERNEL_VIRT_BASE;
return (void *) virt_addr; }
void page_free(void *virt_addr) { int pfn = PA_TO_PFN(KVA_TO_PA((uintptr_t) virt_addr)); unset(&page[pfn].flag, PAGE_USED); page[pfn].physical = NULL; }
|