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