/* * Physical memory allocation * Copyright (c) 2001,2003,2004 David H. Hovemeyer * Copyright (c) 2003, Jeffrey K. Hollingsworth * $Revision: 1.1 $ * * This is free software. You are permitted to use, * redistribute, and modify it as specified in the file "COPYING". */ #include #include #include #include #include #include #include #include #include #include #include /* ---------------------------------------------------------------------- * 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); }