]> git.corax.cc Git - corax/commitdiff
Implement methods to allocate and populate page directories
authorMatthias Kruk <m@m10k.eu>
Tue, 17 Sep 2019 07:22:03 +0000 (16:22 +0900)
committerMatthias Kruk <m@m10k.eu>
Tue, 17 Sep 2019 07:22:03 +0000 (16:22 +0900)
kernel/arch/paging.c
kernel/arch/paging.h

index 130b35f69c2f15975113d7fac5fc6367a6a8f918..5277d105b4d4ee63247d082abf1002c57b544e94 100644 (file)
@@ -17,6 +17,8 @@
  */
 
 #include <corax/types.h>
+#include <corax/errno.h>
+#include <corax/heap.h>
 #include <multiboot.h>
 #include <debug.h>
 #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);
+}
index 963c27c0cd200a6c3d13868d5fb571c7ea614a4e..d21a1df51f0e6a8439cddf263a2ff7d0ea281572 100644 (file)
 #include <corax/types.h>
 #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 */