From 4c8630e9f5ba0e67446f24779e639fb0a681422c Mon Sep 17 00:00:00 2001 From: Matthias Kruk Date: Tue, 17 Sep 2019 16:22:03 +0900 Subject: [PATCH] Implement methods to allocate and populate page directories --- kernel/arch/paging.c | 201 +++++++++++++++++++++++++++++++++++++++++++ kernel/arch/paging.h | 18 ++-- 2 files changed, 212 insertions(+), 7 deletions(-) diff --git a/kernel/arch/paging.c b/kernel/arch/paging.c index 130b35f..5277d10 100644 --- a/kernel/arch/paging.c +++ b/kernel/arch/paging.c @@ -17,6 +17,8 @@ */ #include +#include +#include #include #include #include "cpu.h" @@ -287,3 +289,202 @@ void* pg_init(struct multiboot_info *info) return((void*)(cr3 | _pg_flags)); } + +int pg_dir_alloc(pg_dir_t **dst) +{ + int ret_val; + pg_dir_t *dir; + + dir = kmalloc(sizeof(*dir)); + ret_val = -ENOMEM; + + if(dir) { + switch(_pg_flags & PG_MODE_MASK) { + case PG_MODE_LEGACY: + case PG_MODE_PAE: + /* + * Allocate a frame for the page directory - frames are always + * page-aligned, so we don't have to worry about alignment + */ + dir->pd_base = pg_frame_alloc_end(); + + if(dir->pd_base) { + memset(dir->pd_base, 0, PAGE_SIZE); + } + + break; + + default: + ret_val = -EFAULT; + break; + } + + /* we have an empty page directory now - populate it */ + + /* map the kernel text into the new pagedir */ + ret_val = pg_dir_map(dir, _kernel_pgdir->pd_regions[REGION_TEXT].reg_base, + _kernel_pgdir->pd_regions[REGION_TEXT].reg_base, + _kernel_pgdir->pd_regions[REGION_TEXT].reg_size, + _kernel_pgdir->pd_regions[REGION_TEXT].reg_attrs); + + if(ret_val) { + /* couldn't create the page dir - free allocations */ + kfree(dir); + dir = NULL; + } + + *dst = dir; + } + + return(ret_val); +} + +static int _pg_dir_map_legacy(page_table_t *pd, u32_t paddr, u32_t vaddr, u32_t size, const u32_t flags) +{ + int ret_val; + + ret_val = -EINVAL; + + while(size > 0) { + struct page_table *pt; + u32_t pde; /* page directory entry */ + u32_t pte; /* page table entry */ + + pde = (u32_t)vaddr >> 22; + pte = ((u32_t)vaddr >> 12) & 0x3ff; + + /* allocate a new page table if not present */ + if(!(pd->pt_entries[pde] & PAGE_ATTR_PRESENT)) { + pd->pt_entries[pde] = (u32_t)pg_frame_alloc_end(); + + if(!pd->pt_entries[pde]) { + ret_val = -ENOMEM; + break; + } else { + /* wipe the page table before using it */ + memset((void*)pd->pt_entries[pde], 0, PAGE_SIZE); + } + + pd->pt_entries[pde] |= PAGE_ATTR_PRESENT | flags; + } + + pt = (page_table_t*)(pd->pt_entries[pde] & 0xfffff000); + + if(pt->pt_entries[pte] & PAGE_ATTR_PRESENT) { + ret_val = -EALREADY; + break; + } + + /* allocate new frames if caller didn't specify a physical address */ + if(!paddr) { + pt->pt_entries[pte] = (u32_t)pg_frame_alloc_end(); + } else { + pt->pt_entries[pte] = paddr; + } + + /* return -ENOMEM if we couldn't allocate a frame */ + if(!pt->pt_entries[pte]) { + ret_val = -ENOMEM; + break; + } + + /* set the flags */ + pt->pt_entries[pte] |= PAGE_ATTR_PRESENT | flags; + + vaddr += PAGE_SIZE; + paddr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return(ret_val); +} + +static int _pg_dir_map_pae(pdpt_t *pd, u64_t paddr, u64_t vaddr, u32_t size, const u32_t flags) +{ + int ret_val; + + while(size > 0) { + pae_page_table_t *pt; + u32_t pde; + u32_t pte; + + pde = (vaddr >> 30) & 0x3; + pte = (vaddr >> 21) & 0x1ff; + + /* allocate a new PAE page table if necessary */ + if(!(pd->pdpt_entries[pde] & PAGE_ATTR_PRESENT)) { + pt = (pae_page_table_t*)pg_frame_alloc_end(); + + if(!pt) { + ret_val = -ENOMEM; + break; + } else { + /* wipe the PAE page table before using it */ + memset(pt, 0, PAGE_SIZE); + } + + pd->pdpt_entries[pde] = (unsigned)pt | PAGE_ATTR_PRESENT; + } else { + pt = (pae_page_table_t*)((u32_t)pd->pdpt_entries[pde] ^ PAGE_ATTR_PRESENT); + } + + /* add the requested physical address, or allocate a new frame */ + if(!paddr) { + pt->ppt_entries[pte] = (unsigned)pg_frame_alloc_end(); + } else { + pt->ppt_entries[pte] = paddr; + } + + if(!paddr) { + /* no more frames */ + ret_val = -ENOMEM; + break; + } + + /* set the page attributes on the entry */ + pt->ppt_entries[pte] |= PAGE_ATTR_PRESENT | flags; + + vaddr += PAGE_SIZE; + paddr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return(ret_val); +} + +int pg_dir_map(pg_dir_t *pd, const void *phys, const void *virt, const u32_t size, const u32_t flags) +{ + int ret_val; + u32_t asize; + + /* + * Make sure the size is a multiple of PAGE_SIZE, otherwise + * the helper functions won't work correctly + */ + asize = ALIGN(size, PAGE_SIZE); + + switch(_pg_flags & PG_MODE_MASK) { + case PG_MODE_LEGACY: + ret_val = _pg_dir_map_legacy((struct page_table*)pd->pd_base, + (u32_t)virt, (u32_t)phys, + asize, flags); + break; + + case PG_MODE_PAE: + ret_val = _pg_dir_map_pae((struct pdpt*)pd->pd_base, + (unsigned)virt, (unsigned)phys, + asize, flags); + break; + + case PG_MODE_IA32E: + case PG_MODE_INTEL64: + ret_val = -ENOSYS; + break; + + default: + ret_val = -EFAULT; + break; + } + + return(ret_val); +} diff --git a/kernel/arch/paging.h b/kernel/arch/paging.h index 963c27c..d21a1df 100644 --- a/kernel/arch/paging.h +++ b/kernel/arch/paging.h @@ -22,9 +22,12 @@ #include #include "defs.h" -#define REGION_TEXT 0 -#define REGION_HEAP 1 -#define REGION_STACK 2 +#define REGION_TEXT 0 +#define REGION_HEAP 1 +#define REGION_STACK 2 +#define REGION_BSS 3 +#define REGION_DATA 4 +#define REGION_RODATA 5 struct region { struct region *next; @@ -36,6 +39,8 @@ struct region { u32_t reg_attrs; }; +typedef struct pagedir pg_dir_t; + struct pagedir { void *pd_base; u32_t pd_flags; @@ -65,7 +70,7 @@ struct pae_page_table { #define PDPT_ALIGN 32 #define PAGE_ALIGN 0x1000 -#define ARCH_ALIGN sizeof(void*) +#define ARCH_ALIGN sizeof(void*) #define PAGE_SIZE 0x1000 #define PAGE_SIZE_BIG 0x200000 @@ -86,8 +91,7 @@ struct pae_page_table { void* pg_frame_alloc_start(void); void* pg_frame_alloc_end(void); -int pagedir_init(struct pagedir*); -void* pagedir_map_pages(struct pagedir*); -int pagedir_free(struct pagedir*); +int pg_dir_alloc(pg_dir_t**); +int pg_dir_map(pg_dir_t*, const void*, const void*, const u32_t, const u32_t); #endif /* __PAGING_H */ -- 2.47.3