--- /dev/null
+#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);
+}