*/
#include <corax/types.h>
+#include <corax/errno.h>
+#include <corax/heap.h>
#include <multiboot.h>
#include <debug.h>
#include "cpu.h"
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);
+}