Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Release 1.0
[palacios.git] / misc / test_vm / src / geekos / mem.c
diff --git a/misc/test_vm/src/geekos/mem.c b/misc/test_vm/src/geekos/mem.c
new file mode 100644 (file)
index 0000000..2f44520
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * Physical memory allocation
+ * Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/defs.h>
+#include <geekos/ktypes.h>
+#include <geekos/kassert.h>
+#include <geekos/bootinfo.h>
+#include <geekos/gdt.h>
+#include <geekos/screen.h>
+#include <geekos/int.h>
+#include <geekos/malloc.h>
+#include <geekos/string.h>
+#include <geekos/paging.h>
+#include <geekos/mem.h>
+
+/* ----------------------------------------------------------------------
+ * Global data
+ * ---------------------------------------------------------------------- */
+
+/*
+ * List of Page structures representing each page of physical memory.
+ */
+struct Page* g_pageList;
+
+/*
+ * Number of pages currently available on the freelist.
+ */
+uint_t g_freePageCount = 0;
+
+/* ----------------------------------------------------------------------
+ * Private data and functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Defined in paging.c
+ */
+extern int debugFaults;
+#define Debug(args...) if (debugFaults) Print(args)
+
+/*
+ * List of pages available for allocation.
+ */
+static struct Page_List s_freeList;
+
+/*
+ * Total number of physical pages.
+ */
+int unsigned s_numPages;
+
+/*
+ * Add a range of pages to the inventory of physical memory.
+ */
+static void Add_Page_Range(ulong_t start, ulong_t end, int flags)
+{
+    ulong_t addr;
+
+    KASSERT(Is_Page_Multiple(start));
+    KASSERT(Is_Page_Multiple(end));
+    KASSERT(start < end);
+
+    for (addr = start; addr < end; addr += PAGE_SIZE) {
+       struct Page *page = Get_Page(addr);
+
+       page->flags = flags;
+
+       if (flags == PAGE_AVAIL) {
+           /* Add the page to the freelist */
+           Add_To_Back_Of_Page_List(&s_freeList, page);
+
+           /* Update free page count */
+           ++g_freePageCount;
+       } else {
+           Set_Next_In_Page_List(page, 0);
+           Set_Prev_In_Page_List(page, 0);
+       }
+
+       page->clock = 0;
+       page->vaddr = 0;
+       page->entry = 0;
+    }
+}
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * The linker defines this symbol to indicate the end of
+ * the executable image.
+ */
+extern char end;
+
+/*
+ * Initialize memory management data structures.
+ * Enables the use of Alloc_Page() and Free_Page() functions.
+ */
+void Init_Mem(struct Boot_Info* bootInfo)
+{
+    ulong_t numPages = bootInfo->memSizeKB >> 2;
+    ulong_t endOfMem = numPages * PAGE_SIZE;
+    unsigned numPageListBytes = sizeof(struct Page) * numPages;
+    ulong_t pageListAddr;
+    ulong_t kernEnd;
+
+    KASSERT(bootInfo->memSizeKB > 0);
+
+    /*
+     * Before we do anything, switch from setup.asm's temporary GDT
+     * to the kernel's permanent GDT.
+     */
+    Init_GDT();
+
+    /*
+     * We'll put the list of Page objects right after the end
+     * of the kernel, and mark it as "kernel".  This will bootstrap
+     * us sufficiently that we can start allocating pages and
+     * keeping track of them.
+     */
+    pageListAddr = Round_Up_To_Page((ulong_t) &end);
+    g_pageList = (struct Page*) pageListAddr;
+    kernEnd = Round_Up_To_Page(pageListAddr + numPageListBytes);
+    s_numPages = numPages;
+
+    
+
+    /*
+     * The initial kernel thread and its stack are placed
+     * just beyond the ISA hole.
+     */
+    //KASSERT(ISA_HOLE_END == KERN_THREAD_OBJ);
+    //KASSERT(KERN_STACK == KERN_THREAD_OBJ + PAGE_SIZE);
+
+    /*
+     * Memory looks like this:
+     * 0 - start: available (might want to preserve BIOS data area)
+     * start - end: kernel
+     * end - ISA_HOLE_START: available
+     * ISA_HOLE_START - ISA_HOLE_END: used by hardware (and ROM BIOS?)
+     * ISA_HOLE_END - HIGHMEM_START: used by initial kernel thread
+     * HIGHMEM_START - end of memory: available
+     *    (the kernel heap is located at HIGHMEM_START; any unused memory
+     *    beyond that is added to the freelist)
+     */
+
+    /* JRL CHANGE
+     * 0 - PAGE_SIZE: Unused
+     * PAGE_SIZE - (PAGE_SIZE+8192): init kernel thread stack
+     * (PAGE_SIZE+8192) - ISA_HOLE_START: Available
+     * ISA_HOLE_START - ISA_HOLE_END: HW
+     * ISA_HOLE_END - end: Kernel+PageList
+     * end - (end+1MB): heap
+     * (end+1MB) - end of mem: free
+     * 
+     * 
+     */
+
+
+    Add_Page_Range(0, PAGE_SIZE, PAGE_UNUSED);
+    //    Add_Page_Range(PAGE_SIZE, KERNEL_START_ADDR, PAGE_AVAIL);
+    //Add_Page_Range(KERNEL_START_ADDR, kernEnd, PAGE_KERN);
+    //Add_Page_Range(kernEnd, ISA_HOLE_START, PAGE_AVAIL);
+    Add_Page_Range(PAGE_SIZE, PAGE_SIZE + 8192, PAGE_ALLOCATED);
+    Add_Page_Range(PAGE_SIZE + 8192, ISA_HOLE_START, PAGE_AVAIL);
+    Add_Page_Range(ISA_HOLE_START, ISA_HOLE_END, PAGE_HW);
+    //Add_Page_Range(ISA_HOLE_END, HIGHMEM_START, PAGE_ALLOCATED);
+    Add_Page_Range(HIGHMEM_START, kernEnd, PAGE_KERN);
+    Add_Page_Range(kernEnd, kernEnd + KERNEL_HEAP_SIZE, PAGE_HEAP);
+
+    Add_Page_Range(kernEnd + KERNEL_HEAP_SIZE, endOfMem, PAGE_AVAIL);
+
+    //    Add_Page_Range(HIGHMEM_START, HIGHMEM_START + KERNEL_HEAP_SIZE, PAGE_HEAP);
+    //    Add_Page_Range(HIGHMEM_START + KERNEL_HEAP_SIZE, endOfMem, PAGE_AVAIL);
+
+    /* Initialize the kernel heap */
+    //    Init_Heap(HIGHMEM_START, KERNEL_HEAP_SIZE);
+    Init_Heap(kernEnd, KERNEL_HEAP_SIZE);
+
+    Print("%uKB memory detected, %u pages in freelist, %d bytes in kernel heap\n",
+       bootInfo->memSizeKB, g_freePageCount, KERNEL_HEAP_SIZE);
+}
+
+/*
+ * Initialize the .bss section of the kernel executable image.
+ */
+void Init_BSS(void)
+{
+    extern char BSS_START, BSS_END;
+
+    /* Fill .bss with zeroes */
+    memset(&BSS_START, '\0', &BSS_END - &BSS_START);
+}
+
+/*
+ * Allocate a page of physical memory.
+ */
+void* Alloc_Page(void)
+{
+    struct Page* page;
+    void *result = 0;
+
+    bool iflag = Begin_Int_Atomic();
+
+    /* See if we have a free page */
+    if (!Is_Page_List_Empty(&s_freeList)) {
+       /* Remove the first page on the freelist. */
+       page = Get_Front_Of_Page_List(&s_freeList);
+       KASSERT((page->flags & PAGE_ALLOCATED) == 0);
+       Remove_From_Front_Of_Page_List(&s_freeList);
+
+       /* Mark page as having been allocated. */
+       page->flags |= PAGE_ALLOCATED;
+       g_freePageCount--;
+       result = (void*) Get_Page_Address(page);
+    }
+
+    End_Int_Atomic(iflag);
+
+    return result;
+}
+
+/*
+ * Choose a page to evict.
+ * Returns null if no pages are available.
+ */
+static struct Page *Find_Page_To_Page_Out()
+{
+    int i;
+    struct Page *curr, *best;
+
+    best = NULL;
+
+    for (i=0; i < s_numPages; i++) {
+       if ((g_pageList[i].flags & PAGE_PAGEABLE) &&
+           (g_pageList[i].flags & PAGE_ALLOCATED)) {
+           if (!best) best = &g_pageList[i];
+           curr = &g_pageList[i];
+           if ((curr->clock < best->clock) && (curr->flags & PAGE_PAGEABLE)) {
+               best = curr;
+           }
+       }
+    }
+
+    return best;
+}
+
+/**
+ * Allocate a page of pageable physical memory, to be mapped
+ * into a user address space.
+ *
+ * @param entry pointer to user page table entry which will
+ *   refer to the allocated page
+ * @param vaddr virtual address where page will be mapped
+ *   in user address space
+ */
+void* Alloc_Pageable_Page(pte_t *entry, ulong_t vaddr)
+{
+    bool iflag;
+    void* paddr = 0;
+    struct Page* page = 0;
+
+    iflag = Begin_Int_Atomic();
+
+    KASSERT(!Interrupts_Enabled());
+    KASSERT(Is_Page_Multiple(vaddr));
+
+    paddr = Alloc_Page();
+    if (paddr != 0) {
+       page = Get_Page((ulong_t) paddr);
+       KASSERT((page->flags & PAGE_PAGEABLE) == 0);
+    } else {
+       int pagefileIndex;
+
+        /* Select a page to steal from another process */
+       Debug("About to hunt for a page to page out\n");
+       page = Find_Page_To_Page_Out();
+       KASSERT(page->flags & PAGE_PAGEABLE);
+       paddr = (void*) Get_Page_Address(page);
+       Debug("Selected page at addr %p (age = %d)\n", paddr, page->clock);
+
+       /* Find a place on disk for it */
+       pagefileIndex = Find_Space_On_Paging_File();
+       if (pagefileIndex < 0)
+           /* No space available in paging file. */
+           goto done;
+       Debug("Free disk page at index %d\n", pagefileIndex);
+
+       /* Make the page temporarily unpageable (can't let another process steal it) */
+       page->flags &= ~(PAGE_PAGEABLE);
+
+       /* Lock the page so it cannot be freed while we're writing */
+        page->flags |= PAGE_LOCKED;
+
+       /* Write the page to disk. Interrupts are enabled, since the I/O may block. */
+       Debug("Writing physical frame %p to paging file at %d\n", paddr, pagefileIndex);
+       Enable_Interrupts();
+       Write_To_Paging_File(paddr, page->vaddr, pagefileIndex);
+       Disable_Interrupts();
+
+        /* While we were writing got notification this page isn't even needed anymore */
+        if (page->flags & PAGE_ALLOCATED)
+        {
+           /* The page is still in use update its bookeping info */
+           /* Update page table to reflect the page being on disk */
+           page->entry->present = 0;
+           page->entry->kernelInfo = KINFO_PAGE_ON_DISK;
+           page->entry->pageBaseAddr = pagefileIndex; /* Remember where it is located! */
+        }
+        else
+        {
+           /* The page got freed, don't need bookeeping or it on disk */
+           Free_Space_On_Paging_File(pagefileIndex);
+
+           /* Its still allocated though to us now */
+           page->flags |= PAGE_ALLOCATED;
+        }
+
+        /* Unlock the page */
+        page->flags &= ~(PAGE_LOCKED);
+
+       /* XXX - flush TLB should only flush the one page */
+       Flush_TLB();
+    }
+
+    /* Fill in accounting information for page */
+    page->flags |= PAGE_PAGEABLE;
+    page->entry = entry;
+    page->entry->kernelInfo = 0;
+    page->vaddr = vaddr;
+    KASSERT(page->flags & PAGE_ALLOCATED);
+
+done:
+    End_Int_Atomic(iflag);
+    return paddr;
+}
+
+/*
+ * Free a page of physical memory.
+ */
+void Free_Page(void* pageAddr)
+{
+    ulong_t addr = (ulong_t) pageAddr;
+    struct Page* page;
+    bool iflag;
+
+    iflag = Begin_Int_Atomic();
+
+    KASSERT(Is_Page_Multiple(addr));
+
+    /* Get the Page object for this page */
+    page = Get_Page(addr);
+    KASSERT((page->flags & PAGE_ALLOCATED) != 0);
+
+    /* Clear the allocation bit */
+    page->flags &= ~(PAGE_ALLOCATED);
+
+    /* When a page is locked, don't free it just let other thread know its not needed */
+    if (page->flags & PAGE_LOCKED)
+      return;
+
+    /* Clear the pageable bit */
+    page->flags &= ~(PAGE_PAGEABLE);
+
+    /* Put the page back on the freelist */
+    Add_To_Back_Of_Page_List(&s_freeList, page);
+    g_freePageCount++;
+
+    End_Int_Atomic(iflag);
+}