]> git.corax.cc Git - corax/commitdiff
kernel/arch: Add vm_* functions for low-level page table handling paging-pae-fix
authorMatthias Kruk <m@m10k.eu>
Wed, 30 Dec 2020 02:33:06 +0000 (11:33 +0900)
committerMatthias Kruk <m@m10k.eu>
Wed, 30 Dec 2020 02:33:06 +0000 (11:33 +0900)
To improve portability and separate the software and hardware sides
of paging more cleanly, move the low-level page table handling into
a separate file.

kernel/arch/vm.c [new file with mode: 0644]
kernel/arch/vm.h [new file with mode: 0644]

diff --git a/kernel/arch/vm.c b/kernel/arch/vm.c
new file mode 100644 (file)
index 0000000..22f5e97
--- /dev/null
@@ -0,0 +1,890 @@
+#include <corax/types.h>
+#include <corax/errno.h>
+#include <arch.h>
+#include <string.h>
+#include "frame.h"
+#include "paging.h"
+#include <debug.h>
+
+static inline u32_t _legacy_entry_to_paddr(u32_t addr)
+{
+       return(addr & 0xfffff000);
+}
+
+static inline u32_t _legacy_addr_to_pde(u32_t addr)
+{
+       return((addr >> 22) & 0x3ff);
+}
+
+static inline u32_t _legacy_pde_to_addr(u32_t pde)
+{
+       return(pde << 22);
+}
+
+static inline u32_t _legacy_pde_pte_to_addr(u32_t pde, u32_t pte)
+{
+       return((pde << 22) | (pte << 12));
+}
+
+static inline u32_t _legacy_addr_to_pte(u32_t addr)
+{
+       return((addr >> 12) & 0x3ff);
+}
+
+static inline u32_t _legacy_addr_to_off(u32_t addr)
+{
+       return(addr & 0xfff);
+}
+
+static inline u32_t _legacy_addr_to_page(u32_t addr)
+{
+       return(addr & 0xfffff000);
+}
+
+static inline u32_t _pae_pdpte_to_addr(u32_t pdpte)
+{
+       return(pdpte << 30);
+}
+
+static inline u32_t _pae_pdpte_pde_to_addr(u32_t pdpte, u32_t pde)
+{
+       return((pdpte << 30) | (pde << 21));
+}
+
+static inline u32_t _pae_pdpte_pde_pte_to_addr(u32_t pdpte, u32_t pde, u32_t pte)
+{
+       return((pdpte << 30) | (pde << 21) | (pte << 12));
+}
+
+static inline u32_t _pae_addr_to_pdpte(u32_t addr)
+{
+       return((addr >> 30) & 0x3);
+}
+
+static inline u32_t _pae_addr_to_pde(u32_t addr)
+{
+       return((addr >> 21) & 0x1ff);
+}
+
+static inline u32_t _pae_addr_to_pte(u32_t addr)
+{
+       return((addr >> 12) & 0x1ff);
+}
+
+static inline u32_t _pae_addr_to_off(u32_t addr)
+{
+       return(_legacy_addr_to_off(addr));
+}
+
+static inline u32_t _pae_addr_to_page(u32_t addr)
+{
+       return(_legacy_addr_to_page(addr));
+}
+
+static inline u64_t _pae_entry_to_paddr(u64_t pdpte)
+{
+       return((pdpte & 0xfffffffffffff000LL) & 0x7fffffffffffffffLL);
+}
+
+static int _legacy_vm_new(void **dst)
+{
+       struct page_table *pdir;
+       int pg_state;
+
+       pdir = (struct page_table*)((u32_t)frame_alloc(sizeof(*pdir), FRAME_ALLOC_4G));
+
+       if(!pdir) {
+               return(-ENOMEM);
+       }
+
+       pg_state = cpu_paging_disable();
+
+       memset(pdir, 0, sizeof(*pdir));
+
+       if(pg_state) {
+               cpu_paging_enable();
+       }
+
+       *dst = pdir;
+       return(0);
+}
+
+static int _pae_vm_new(void **dst)
+{
+       struct pdpt *pdpt;
+       int pg_state;
+
+       pdpt = (struct pdpt*)((u32_t)frame_alloc(sizeof(*pdpt), FRAME_ALLOC_4G));
+
+       if(!pdpt) {
+               return(-ENOMEM);
+       }
+
+       pg_state = cpu_paging_disable();
+
+       memset(pdpt, 0, sizeof(*pdpt));
+
+       if(pg_state) {
+               cpu_paging_enable();
+       }
+
+       *dst = pdpt;
+       return(0);
+}
+
+int vm_new(void **dst)
+{
+       extern u32_t _pg_flags;
+
+       switch(_pg_flags & PG_MODE_MASK) {
+       case PG_MODE_LEGACY:
+               return(_legacy_vm_new(dst));
+
+       case PG_MODE_PAE:
+               return(_pae_vm_new(dst));
+
+       default:
+               return(-EFAULT);
+       }
+}
+
+static int _legacy_table_free(struct page_table *table)
+{
+       int i;
+
+       for(i = 0; i < PAGE_TABLE_ENTRIES; i++) {
+               u32_t entry;
+
+               entry = table->pt_entries[i] & PAGE_TABLE_ADDRESS_MASK;
+
+               if(!entry) {
+                       continue;
+               }
+
+               frame_free(entry, PAGE_SIZE);
+               table->pt_entries[i] = 0;
+       }
+
+       return(-ENOSYS);
+}
+
+static int _legacy_vm_free(struct page_table *pdir)
+{
+       int i;
+       int pg_state;
+
+       pg_state = cpu_paging_disable();
+
+       for(i = 0; i < PAGE_TABLE_ENTRIES; i++) {
+               u32_t entry;
+
+               entry = pdir->pt_entries[i] & PAGE_TABLE_ADDRESS_MASK;
+
+               if(!entry) {
+                       continue;
+               }
+
+               if(pdir->pt_entries[i] & PAGE_ATTR_SIZE) {
+                       /* free the frames occupied by this 4M page */
+                       frame_free(entry, PAGE_SIZE_LARGE);
+               } else {
+                       /* free the second-level paging structure */
+
+                       _legacy_table_free((struct page_table*)entry);
+               }
+
+               pdir->pt_entries[i] = 0;
+       }
+
+       if(pg_state) {
+               cpu_paging_enable();
+       }
+
+       frame_free((u64_t)((u32_t)pdir), PAGE_SIZE);
+
+       return(0);
+}
+
+static int _pae_vm_free(void *pdir)
+{
+       frame_free((u64_t)((u32_t)pdir), PAGE_SIZE);
+
+       return(0);
+}
+
+int vm_free(void *vm)
+{
+       extern u32_t _pg_flags;
+
+       switch(_pg_flags & PG_MODE_MASK) {
+       case PG_MODE_LEGACY:
+               return(_legacy_vm_free(vm));
+
+       case PG_MODE_PAE:
+               return(_pae_vm_free(vm));
+
+       default:
+               return(-EFAULT);
+       }
+}
+
+static int _legacy_vm_map(void *vm, const u32_t paddr, const u32_t vaddr,
+                         const u32_t size, const page_attrs_t attrs)
+{
+       struct page_table *pdir;
+       u32_t mapped_size;
+       u32_t cur_paddr;
+       u32_t cur_vaddr;
+       int pg_state;
+       int ret_val;
+
+       ret_val = 0;
+       mapped_size = 0;
+       cur_paddr = paddr;
+       cur_vaddr = vaddr;
+
+       pdir = (struct page_table*)vm;
+
+       pg_state = cpu_paging_disable();
+
+       while(mapped_size < size) {
+               struct page_table *table;
+               u32_t pde;
+               u32_t pte;
+
+               pde = (u32_t)cur_vaddr >> 22;
+               pte = ((u32_t)cur_vaddr >> 12) & 0x3ff;
+
+               if(!(pdir->pt_entries[pde] & PAGE_ATTR_PRESENT)) {
+                       /*
+                        * Satisfy the mapping with a large page, if possible
+                        */
+                       if((size - mapped_size) >= PAGE_SIZE_LARGE &&
+                          ALIGNED(cur_vaddr, PAGE_SIZE_LARGE) &&
+                          ALIGNED(cur_paddr, PAGE_SIZE_LARGE)) {
+                               pdir->pt_entries[pde] = cur_paddr | PAGE_ATTR_SIZE |
+                                       PAGE_ATTR_PRESENT | attrs;
+                               cur_paddr += PAGE_SIZE_LARGE;
+                               cur_vaddr += PAGE_SIZE_LARGE;
+                               mapped_size += PAGE_SIZE_LARGE;
+
+                               /*
+                                * The rest of the loop would attempt to populate the newly
+                                * allocated page table, which we must skip, since it's not
+                                * necessary in this case.
+                                */
+                               continue;
+                       } else {
+                               table = (struct page_table*)((u32_t)
+                                                            frame_alloc(sizeof(*table),
+                                                                        FRAME_ALLOC_4G));
+
+                               if(!table) {
+                                       ret_val = -ENOMEM;
+                                       break;
+                               }
+
+                               memset(table, 0, sizeof(table));
+                               pdir->pt_entries[pde] = (u32_t)table |
+                                       PAGE_ATTR_PRESENT | attrs;
+                       }
+               } else {
+                       table = (struct page_table*)(pdir->pt_entries[pde] &
+                                                    PAGE_TABLE_ADDRESS_MASK);
+               }
+
+               pdir->pt_entries[pde] |= (attrs & (PAGE_ATTR_USER |
+                                                  PAGE_ATTR_WRITABLE));
+
+               if(pdir->pt_entries[pte] & PAGE_ATTR_PRESENT) {
+                       ret_val = -EALREADY;
+                       break;
+               }
+
+               if(!cur_paddr) {
+                       table->pt_entries[pte] = (u32_t)frame_alloc(1, FRAME_ALLOC_4G);
+               } else {
+                       table->pt_entries[pte] = cur_paddr;
+                       cur_paddr += PAGE_SIZE;
+               }
+
+               if(!table->pt_entries[pte]) {
+                       ret_val = -ENOMEM;
+                       break;
+               }
+
+               table->pt_entries[pte] |= PAGE_ATTR_PRESENT | attrs;
+               cur_vaddr += PAGE_SIZE;
+               mapped_size += PAGE_SIZE;
+       }
+
+       if(pg_state) {
+               cpu_paging_enable();
+       }
+
+       return(ret_val);
+}
+
+static int _pae_vm_map(void *vm, const u64_t paddr, const u64_t vaddr,
+                      const u64_t size, const page_attrs_t attrs)
+{
+       struct pdpt *pdpt;
+       int ret_val;
+       u64_t cur_paddr;
+       u64_t cur_vaddr;
+       u64_t mapped_size;
+
+       mapped_size = 0;
+       ret_val = 0;
+       cur_paddr = paddr;
+       cur_vaddr = vaddr;
+       pdpt = (struct pdpt*)vm;
+
+       while(mapped_size < size) {
+               struct pae_page_table *dir;
+               struct pae_page_table *tbl;
+
+               u32_t pdpte;
+               u32_t pde;
+               u32_t pte;
+
+               pdpte = _pae_addr_to_pdpte(cur_vaddr);
+
+               if(!(pdpt->pdpt_entries[pdpte] & PAGE_ATTR_PRESENT)) {
+                       dir = (struct pae_page_table*)((u32_t)frame_alloc(PAGE_SIZE,
+                                                                         FRAME_ALLOC_4G));
+
+                       if(!dir) {
+                               ret_val = -ENOMEM;
+                               break;
+                       }
+
+                       memset(dir, 0, sizeof(*dir));
+                       pdpt->pdpt_entries[pdpte] = (unsigned)dir | PAGE_ATTR_PRESENT;
+               } else {
+                       dir = (struct pae_page_table*)(u32_t)_pae_entry_to_paddr(pdpt->pdpt_entries[pdpte]);
+               }
+
+               pde = _pae_addr_to_pde(cur_vaddr);
+
+               if(!(dir->ppt_entries[pde] & PAGE_ATTR_PRESENT)) {
+                       if(size - mapped_size > PAGE_SIZE_BIG) {
+                               /* map a big page if we have more than 2M left to map */
+
+                               if(!cur_paddr) {
+                                       dir->ppt_entries[pde] = frame_alloc(PAGE_SIZE_BIG, 0);
+                                       dir->ppt_entries[pde] |=
+                                               PAGE_ATTR_PRESENT | PAGE_ATTR_SIZE;
+
+                                       mapped_size += PAGE_SIZE_BIG;
+                                       cur_vaddr += PAGE_SIZE_BIG;
+                               } else {
+                                       dir->ppt_entries[pde] = cur_paddr |
+                                               PAGE_ATTR_PRESENT | PAGE_ATTR_SIZE;
+                               }
+
+                               continue;
+                       } else {
+                               /*
+                                * We have less than 2M left to map, so we need to add another
+                                * page table to map a more granular size.
+                                */
+                               tbl = (struct pae_page_table*)(unsigned)frame_alloc(PAGE_SIZE,
+                                                                         FRAME_ALLOC_4G);
+
+                               if(!tbl) {
+                                       ret_val = -ENOMEM;
+                                       break;
+                               }
+
+                               dir->ppt_entries[pde] = (unsigned)tbl | PAGE_ATTR_PRESENT |
+                                       attrs;
+                       }
+               } else {
+                       tbl = (struct pae_page_table*)(addr_t)
+                               _pae_entry_to_paddr(dir->ppt_entries[pde]);
+               }
+
+               pte = _pae_addr_to_pte(cur_vaddr);
+
+               if(!(tbl->ppt_entries[pte] & PAGE_ATTR_PRESENT)) {
+                       u64_t page;
+
+                       if(!cur_paddr) {
+                               page = frame_alloc(PAGE_SIZE, 0);
+
+                               if(!page) {
+                                       ret_val = -ENOMEM;
+                                       break;
+                               }
+                       } else {
+                               page = cur_paddr;
+                               cur_paddr += PAGE_SIZE;
+                       }
+
+                       tbl->ppt_entries[pte] = page | PAGE_ATTR_PRESENT | attrs;
+                       mapped_size += PAGE_SIZE;
+               }
+
+               cur_vaddr += PAGE_SIZE;
+       }
+
+       return(ret_val);
+}
+
+int vm_map(void *vm, const u64_t paddr, const u64_t vaddr,
+          const u64_t size, const page_attrs_t attrs)
+{
+       extern u32_t _pg_flags;
+
+       switch(_pg_flags & PG_MODE_MASK) {
+       case PG_MODE_LEGACY:
+               return(_legacy_vm_map(vm, (const u32_t)paddr, (const u32_t)vaddr,
+                                     (const u32_t)size, attrs));
+
+       case PG_MODE_PAE:
+               return(_pae_vm_map(vm, paddr, vaddr, size, attrs));
+
+       default:
+               return(-EFAULT);
+       }
+}
+
+static int _legacy_vm_unmap(void *vm, const u32_t vaddr, const u32_t size)
+{
+       struct page_table *pdir;
+       u32_t unmapped_size;
+       u32_t cur_vaddr;
+       int pg_state;
+
+       pg_state = cpu_paging_disable();
+       pdir = (struct page_table*)vm;
+       unmapped_size = 0;
+       cur_vaddr = vaddr;
+
+       while(unmapped_size < size) {
+               struct page_table *table;
+               u32_t pde;
+               u32_t pte;
+
+               pde = _legacy_addr_to_pde(cur_vaddr);
+
+               /*
+                * Skip until the next 4M alignment if the page table isn't present
+                * or if the page directory entry maps a large page.
+                */
+               if(!(pdir->pt_entries[pde] & PAGE_ATTR_PRESENT) ||
+                  (pdir->pt_entries[pde] & PAGE_ATTR_SIZE)) {
+                       u32_t next_vaddr;
+
+                       next_vaddr = _legacy_pde_to_addr(pde + 1);
+                       unmapped_size += next_vaddr - cur_vaddr;
+                       cur_vaddr = next_vaddr;
+
+                       pdir->pt_entries[pde] = 0;
+                       continue;
+               }
+
+               /*
+                * The page directory entry referenced a present page table. Remove
+                * one page from the page table (we don't care if it was ever present).
+                */
+               table = (struct page_table*)_legacy_entry_to_paddr(pdir->pt_entries[pde]);
+               pte = _legacy_addr_to_pte(cur_vaddr);
+
+               table->pt_entries[pte] = 0;
+               unmapped_size += PAGE_SIZE;
+               cur_vaddr += PAGE_SIZE;
+       }
+
+       if(pg_state) {
+               cpu_paging_enable();
+       }
+
+       return(0);
+}
+
+static int _pae_vm_unmap(void *vm, const u64_t vaddr, const u64_t size)
+{
+       struct pdpt *pdpt;
+       u64_t unmapped_size;
+       u64_t cur_vaddr;
+       int pg_state;
+
+       pdpt = (struct pdpt*)vm;
+       cur_vaddr = vaddr;
+       unmapped_size = 0;
+
+       pg_state = cpu_paging_disable();
+
+       while(unmapped_size < size) {
+               struct pae_page_table *dir;
+               struct pae_page_table *tbl;
+               u32_t pdpte;
+               u32_t pde;
+               u32_t pte;
+
+               pdpte = _pae_addr_to_pdpte(cur_vaddr);
+
+               if(!(pdpt->pdpt_entries[pdpte] & PAGE_ATTR_PRESENT)) {
+                       u64_t next_vaddr;
+
+                       /* PDPT entries cannot map 1G pages */
+
+                       next_vaddr = _pae_pdpte_to_addr(pdpte + 1);
+                       unmapped_size += next_vaddr - cur_vaddr;
+                       cur_vaddr = next_vaddr;
+
+                       pdpt->pdpt_entries[pdpte] = 0;
+                       continue;
+               }
+
+               dir = (struct pae_page_table*)((u32_t)_pae_entry_to_paddr(pdpt->pdpt_entries[pdpte]));
+               pde = _pae_addr_to_pde(cur_vaddr);
+
+               if(!(dir->ppt_entries[pde] & PAGE_ATTR_PRESENT) ||
+                  (dir->ppt_entries[pde] & PAGE_ATTR_SIZE)) {
+                       u64_t next_vaddr;
+
+                       next_vaddr = _pae_pdpte_pde_to_addr(pdpte, pde + 1);
+                       unmapped_size += next_vaddr - cur_vaddr;
+                       cur_vaddr = next_vaddr;
+
+                       dir->ppt_entries[pde] = 0;
+                       continue;
+               }
+
+               tbl = (struct pae_page_table*)((u32_t)_pae_entry_to_paddr(dir->ppt_entries[pde]));
+               pte = _pae_addr_to_pte(cur_vaddr);
+
+               tbl->ppt_entries[pte] = 0;
+               unmapped_size += PAGE_SIZE;
+               cur_vaddr += PAGE_SIZE;
+       }
+
+       if(pg_state) {
+               cpu_paging_enable();
+       }
+
+       return(0);
+}
+
+int vm_unmap(void *vm, const u64_t vaddr, const u64_t size)
+{
+       extern u32_t _pg_flags;
+
+       switch(_pg_flags & PG_MODE_MASK) {
+       case PG_MODE_LEGACY:
+               return(_legacy_vm_unmap(vm, (const u32_t)vaddr, (const u32_t)size));
+
+       case PG_MODE_PAE:
+               return(_pae_vm_unmap(vm, vaddr, size));
+
+       default:
+               return(-EFAULT);
+       }
+}
+
+static int _legacy_vm_debug(void *vm)
+{
+       return(-ENOSYS);
+}
+
+static void _pae_page_table_debug(struct pae_page_table *table)
+{
+       int i;
+
+       for(i = 0; i < sizeof_a(table->ppt_entries); i++) {
+               u64_t ent;
+
+               ent = table->ppt_entries[i];
+
+               if(!ent) {
+                       continue;
+               }
+
+               dbg_printf("    [%02x] %016llx\n", i, ent);
+       }
+
+       return;
+}
+
+static int _pae_vm_debug(void *vm)
+{
+       struct pdpt *pdpt;
+       int pg_state;
+       int pdpte;
+
+       pdpt = (struct pdpt*)vm;
+
+       pg_state = cpu_paging_disable();
+
+       for(pdpte = 0; pdpte < 4; pdpte++) {
+               struct pae_page_table *dir;
+               int pde;
+
+               dbg_printf("PDPT[%x] = %016llx\n", pdpte, pdpt->pdpt_entries[pdpte]);
+               dir = (struct pae_page_table*)(u32_t)_pae_entry_to_paddr(pdpt->pdpt_entries[pdpte]);
+
+               if(!dir) {
+                       continue;
+               }
+
+               for(pde = 0; pde < 512; pde++) {
+                       u64_t pdeent;
+
+                       pdeent = dir->ppt_entries[pde];
+
+                       if(!pdeent) {
+                               continue;
+                       }
+                       dbg_printf("PDPT[%02x][%02x] %016llx\n", pdpte, pde, pdeent);
+
+                       if(!(pdeent & PAGE_ATTR_SIZE)) {
+                               _pae_page_table_debug((struct pae_page_table*)(addr_t)
+                                                     _pae_entry_to_paddr(pdeent));
+                       }
+               }
+       }
+
+       if(pg_state) {
+               cpu_paging_enable();
+       }
+
+       return(0);
+}
+
+int vm_debug(void *vm)
+{
+       extern u32_t _pg_flags;
+
+       switch(_pg_flags & PG_MODE_MASK) {
+       case PG_MODE_LEGACY:
+               return(_legacy_vm_debug(vm));
+
+       case PG_MODE_PAE:
+               return(_pae_vm_debug(vm));
+
+       default:
+               return(-EFAULT);
+       }
+}
+
+static void _legacy_vm_debug_pagefault(void *vm, void *addr)
+{
+       struct page_table *dir;
+       int pg_state;
+       int pde;
+
+       pg_state = cpu_paging_disable();
+
+       dir = (struct page_table*)vm;
+       pde = _legacy_addr_to_pde((u32_t)addr);
+
+       dbg_printf("#PF CR2=0x%08x CR3=0x%08x\n", addr, vm);
+       dbg_printf("PDE 0x%03x = 0x%08x\n", pde, dir->pt_entries[pde]);
+
+       if(dir->pt_entries[pde] & PAGE_ATTR_PRESENT &&
+          !(dir->pt_entries[pde] & PAGE_ATTR_SIZE)) {
+               struct page_table *tbl;
+               int pte;
+
+               tbl = (struct page_table*)_legacy_entry_to_paddr(dir->pt_entries[pde]);
+               pte = _legacy_addr_to_pte((u32_t)addr);
+
+               dbg_printf("PTE 0x%03x = 0x%08x\n", pte, tbl->pt_entries[pte]);
+       }
+
+       if(pg_state) {
+               cpu_paging_enable();
+       }
+
+       return;
+}
+
+static void _pae_vm_debug_pagefault(void *vm, void *addr)
+{
+       struct pdpt *pdpt;
+       int pdpte;
+       int pg_state;
+
+       pg_state = cpu_paging_disable();
+
+       pdpt = (struct pdpt*)vm;
+       pdpte = _pae_addr_to_pdpte((u64_t)(unsigned)addr);
+
+       dbg_printf("#PF CR2=0x%08x CR3=0x%08x\n", addr, vm);
+       dbg_printf("PDPTE 0x%03x = 0x%016llx\n", pdpte, pdpt->pdpt_entries[pdpte]);
+
+       if(pdpt->pdpt_entries[pdpte] & PAGE_ATTR_PRESENT) {
+               struct pae_page_table *dir;
+               int pde;
+
+               dir = (struct pae_page_table*)(addr_t)
+                       _pae_entry_to_paddr(pdpt->pdpt_entries[pdpte]);
+               pde = _pae_addr_to_pde((u64_t)(unsigned)addr);
+
+               dbg_printf("PDE   0x%03x = 0x%016llx\n", pde, dir->ppt_entries[pde]);
+
+               if(dir->ppt_entries[pde] & PAGE_ATTR_PRESENT &&
+                  !(dir->ppt_entries[pde] & PAGE_ATTR_SIZE)) {
+                       struct pae_page_table *tbl;
+                       int pte;
+
+                       tbl = (struct pae_page_table*)(unsigned)
+                               _pae_entry_to_paddr(dir->ppt_entries[pde]);
+                       pte = _pae_addr_to_pte((u64_t)(unsigned)addr);
+
+                       dbg_printf("PTE   0x%03x = 0x%016llx\n", pte, tbl->ppt_entries[pte]);
+               }
+       }
+
+       if(pg_state) {
+               cpu_paging_enable();
+       }
+
+       return;
+}
+
+void vm_debug_pagefault(void *vm, void *addr)
+{
+       extern u32_t _pg_flags;
+
+       switch(_pg_flags & PG_MODE_MASK) {
+       case PG_MODE_LEGACY:
+               _legacy_vm_debug_pagefault(vm, addr);
+               break;
+
+       case PG_MODE_PAE:
+               _pae_vm_debug_pagefault(vm, addr);
+               break;
+
+       default:
+               break;
+       }
+
+       return;
+}
+
+int _legacy_vm_vpxlate(struct page_table *dir, const void *virt, void **phys)
+{
+       struct page_table *tbl;
+       int pde;
+       int pte;
+
+       pde = _legacy_addr_to_pde((unsigned)virt);
+
+       if(!(dir->pt_entries[pde] & PAGE_ATTR_PRESENT)) {
+               return(-EFAULT);
+       }
+
+       tbl = (struct page_table*)(u32_t)_legacy_entry_to_paddr(dir->pt_entries[pde]);
+
+       if(dir->pt_entries[pde] & PAGE_ATTR_SIZE) {
+               *phys = (void*)tbl + ((u32_t)virt & 0x3fffff);
+               return(0);
+       }
+
+       if(!tbl) {
+               return(-EFAULT);
+       }
+
+       pte = _legacy_addr_to_pte((unsigned)virt);
+
+       if(!(tbl->pt_entries[pte] & PAGE_ATTR_PRESENT)) {
+               return(-EFAULT);
+       }
+
+       *phys = (void*)((u32_t)_legacy_entry_to_paddr(tbl->pt_entries[pte]) +
+                       _legacy_addr_to_off((unsigned)virt));
+
+       return(0);
+}
+
+int _pae_vm_vpxlate(struct pdpt *pdpt, const void *virt, void **phys)
+{
+       struct pae_page_table *dir;
+       struct pae_page_table *tbl;
+       u64_t entry;
+       int pdpte;
+       int pde;
+       int pte;
+
+       pdpte = _pae_addr_to_pdpte((u64_t)(addr_t)virt);
+
+       if(!(pdpt->pdpt_entries[pdpte] & PAGE_ATTR_PRESENT)) {
+               return(-EFAULT);
+       }
+
+       dir = (struct pae_page_table*)(addr_t)_pae_addr_to_pde((u64_t)(addr_t)virt);
+
+       if(!dir) {
+               return(-EFAULT);
+       }
+
+       pde = _pae_addr_to_pde((u64_t)(addr_t)virt);
+       entry = dir->ppt_entries[pde];
+
+       if(!(entry & PAGE_ATTR_PRESENT)) {
+               return(-EFAULT);
+       }
+
+       tbl = (struct pae_page_table*)(addr_t)_pae_entry_to_paddr(entry);
+
+       if(!tbl) {
+               return(-EFAULT);
+       }
+
+       if(entry & PAGE_ATTR_SIZE) {
+               *phys = (void*)tbl + ((u64_t)(addr_t)virt & 0x1fffff);
+               return(0);
+       }
+
+       pte = _pae_addr_to_pte((u64_t)(addr_t)virt);
+       entry = tbl->ppt_entries[pte];
+
+       if(!(entry & PAGE_ATTR_PRESENT)) {
+               return(-EFAULT);
+       }
+
+       *phys = (void*)(addr_t)_pae_entry_to_paddr(entry) + _pae_addr_to_off((u64_t)(addr_t)virt);
+       return(0);
+}
+
+/*
+ * vm_vpxlate() - Translate a virtual address to a physical address
+ *
+ * SYNOPSIS
+ *  int vm_vpxlate(void *vm, u64_t virt, u64_t *phys);
+ *
+ * DESCRIPTION
+ *  The vm_vpxlate() function performs a translation of the virtual address specified by `virt'
+ *  within the page directory pointed to by `vm' and stores the result at the address pointed
+ *  to by `phys'. The translation is performed essentially the same way as the MMU would have
+ *  done it, except in software. This operation is costly and should be used sparingly.
+ *
+ * RETURN VALUE
+ *  On success, zero is returned. Otherwise, a negative error number is returned and the memory
+ *  pointed to by `phys' is left untouched.
+ *
+ * ERRORS
+ *  -EFAULT The page pointed to by `virt' is not present in the page directory
+ *  -ENOSYS The current paging mode is not supported
+ */
+int vm_vpxlate(void *vm, const void *virt, void **phys)
+{
+       extern u32_t _pg_flags;
+
+       switch(_pg_flags & PG_MODE_MASK) {
+       case PG_MODE_LEGACY:
+               return(_legacy_vm_vpxlate((struct page_table*)vm, virt, phys));
+
+       case PG_MODE_PAE:
+               return(_pae_vm_vpxlate((struct pdpt*)vm, virt, phys));
+
+       default:
+               break;
+       }
+
+       return(-EFAULT);
+}
diff --git a/kernel/arch/vm.h b/kernel/arch/vm.h
new file mode 100644 (file)
index 0000000..389bb68
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef __VM_H
+#define __VM_H
+
+#include <corax/types.h>
+#include <arch.h>
+
+int  vm_new(void **vm);
+int  vm_free(void *vm);
+
+int  vm_map(void *vm, const u64_t paddr, const u64_t vaddr,
+           const u64_t size, const page_attrs_t attrs);
+int  vm_unmap(void *vm, const u64_t vaddr, const u64_t size);
+
+void vm_debug_pagefault(void *vm, void *addr);
+
+#endif /* __VM_H */