2 * Physical memory allocation
3 * Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
4 * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
7 * This is free software. You are permitted to use,
8 * redistribute, and modify it as specified in the file "COPYING".
11 #include <geekos/defs.h>
12 #include <geekos/ktypes.h>
13 #include <geekos/kassert.h>
14 #include <geekos/bootinfo.h>
15 #include <geekos/gdt.h>
16 #include <geekos/screen.h>
17 #include <geekos/int.h>
18 #include <geekos/malloc.h>
19 #include <geekos/string.h>
20 #include <geekos/mem.h>
22 #include <geekos/vmm_sizes.h>
23 #include <geekos/serial.h>
25 /* ----------------------------------------------------------------------
27 * ---------------------------------------------------------------------- */
30 * List of Page structures representing each page of physical memory.
32 struct Page* g_pageList;
35 * Number of pages currently available on the freelist.
37 uint_t g_freePageCount = 0;
39 /* ----------------------------------------------------------------------
40 * Private data and functions
41 * ---------------------------------------------------------------------- */
46 extern int debugFaults;
47 #define Debug(args...) if (debugFaults) Print(args)
50 * List of pages available for allocation.
52 static struct Page_List s_freeList;
55 * Total number of physical pages.
57 int unsigned s_numPages;
60 * Add a range of pages to the inventory of physical memory.
62 static void Add_Page_Range(ulong_t start, ulong_t end, int flags)
66 //Print("Start: %u, End: %u\n", (unsigned int)start, (unsigned int)end);
68 KASSERT(Is_Page_Multiple(start));
69 KASSERT(Is_Page_Multiple(end));
72 //Print("Adding %lu pages\n", (end - start) / PAGE_SIZE);
74 for (addr = start; addr < end; addr += PAGE_SIZE) {
75 // Print("Adding Page at %u\n", (unsigned int)addr);
76 struct Page *page = Get_Page(addr);
80 if (flags == PAGE_AVAIL) {
81 /* Add the page to the freelist */
82 Add_To_Back_Of_Page_List(&s_freeList, page);
84 /* Update free page count */
87 Set_Next_In_Page_List(page, 0);
88 Set_Prev_In_Page_List(page, 0);
92 // Print("%d pages now in freelist\n", g_freePageCount);
96 /* ----------------------------------------------------------------------
98 * ---------------------------------------------------------------------- */
101 * The linker defines this symbol to indicate the end of
102 * the executable image.
107 * Initialize memory management data structures.
108 * Enables the use of Alloc_Page() and Free_Page() functions.
110 void Init_Mem(struct Boot_Info* bootInfo)
112 ulong_t numPages = bootInfo->memSizeKB >> 2;
113 // ulong_t endOfMem = numPages * PAGE_SIZE;
114 unsigned numPageListBytes = sizeof(struct Page) * numPages;
115 ulong_t pageListAddr;
119 KASSERT(bootInfo->memSizeKB > 0);
121 if (bootInfo->memSizeKB != TOP_OF_MEM/1024) {
122 PrintBoth("Kernel compiled for %d KB machine, but machine claims %d KB\n",TOP_OF_MEM/1024,bootInfo->memSizeKB);
123 if (bootInfo->memSizeKB < TOP_OF_MEM/1024) {
124 PrintBoth("Kernel compiled for more memory than machine has. Panicking\n");
130 // if there is not enough memory between START_OF_VM+VM_SIZE and TOP_OF_MEM
131 // to store the kernel and kernel structures, we need to panick
132 PrintBoth("Kernel is not compiled with sufficient memory above the VM to support the memory\n");
137 bootInfo->memSizeKB = TOP_OF_MEM / 1024;
140 * Before we do anything, switch from setup.asm's temporary GDT
141 * to the kernel's permanent GDT.
146 * We'll put the list of Page objects right after the end
147 * of the kernel, and mark it as "kernel". This will bootstrap
148 * us sufficiently that we can start allocating pages and
149 * keeping track of them.
152 // JRL: This is stupid...
153 // with large mem sizes the page list overruns into the ISA
154 // hole. By blind luck this causes an unrelated assertion failure, otherwise
155 // I might never have caught it...
156 // We fix it by moving the page list after the kernel heap...
157 // For now we'll make our own stupid assumption that the mem size
158 // is large enough to accomodate the list in high mem.
160 PrintBoth("Total Memory Size: %u MBytes\n", bootInfo->memSizeKB/1024);
161 PrintBoth("VM Start: %x\n",START_OF_VM);
162 PrintBoth("VM End: %x\n",START_OF_VM+VM_SIZE-1);
165 PrintBoth("Page struct size: %u bytes\n", sizeof(struct Page));
166 PrintBoth("Page List Size: %u bytes\n", numPageListBytes);
169 //pageListAddr = Round_Up_To_Page((ulong_t) &end);
170 //pageListAddr = Round_Up_To_Page(HIGHMEM_START + KERNEL_HEAP_SIZE);
172 // Note that this is now moved to be just above the kernel heap
173 // see defs.h for layout
174 pageListAddr=Round_Up_To_Page(KERNEL_PAGELIST);
176 pageListEnd = Round_Up_To_Page(pageListAddr + numPageListBytes);
178 g_pageList = (struct Page*) pageListAddr;
179 // kernEnd = Round_Up_To_Page(pageListAddr + numPageListBytes);
181 // PAD - Note: I am changing this so that everything through the end of
182 // the VM boot package (bioses/vmxassist) is off limits
183 //kernEnd = Round_Up_To_Page((ulong_t) &end);
184 kernEnd = Round_Up_To_Page(VM_BOOT_PACKAGE_END);
185 s_numPages = numPages;
187 PrintBoth("Pagelist addr: %p\n", g_pageList);
188 PrintBoth("index: %p\n", &g_pageList[3]);
189 PrintBoth("direct offset: %p\n", g_pageList + (sizeof(struct Page) * 2));
190 PrintBoth("Kernel Size=%lx\n", (kernEnd - KERNEL_START_ADDR));
191 PrintBoth("Kernel Start=%x\n", KERNEL_START_ADDR);
192 PrintBoth("Kernel End=%lx\n", kernEnd);
193 PrintBoth("VM Boot Package Start=%x\n", VM_BOOT_PACKAGE_START);
194 PrintBoth("VM Boot Package End=%x\n", VM_BOOT_PACKAGE_END);
197 * The initial kernel thread and its stack are placed
198 * just beyond the ISA hole.
200 // This is no longer true
201 // KASSERT(ISA_HOLE_END == KERN_THREAD_OBJ);
203 //KASSERT(KERN_THREAD_OBJ==(START_OF_VM+VM_SIZE));
204 //KASSERT(KERN_STACK == KERN_THREAD_OBJ + PAGE_SIZE);
207 * Memory looks like this:
208 * 0 - start: available (might want to preserve BIOS data area)
209 * start - end: kernel
210 * end - ISA_HOLE_START: available
211 * ISA_HOLE_START - ISA_HOLE_END: used by hardware (and ROM BIOS?)
212 * ISA_HOLE_END - HIGHMEM_START: used by initial kernel thread
213 * HIGHMEM_START - end of memory: available
214 * (the kernel heap is located at HIGHMEM_START; any unused memory
215 * beyond that is added to the freelist)
218 // The VM region... 0 .. VM size is out of bounds
219 KASSERT(START_OF_VM==0);
220 Add_Page_Range(START_OF_VM, START_OF_VM+VM_SIZE, PAGE_VM);
221 // The kernel is still in low memory at this point, in the VM region
222 // Thus we will mark it as kernel use
223 // Add_Page_Range(KERNEL_START_ADDR, kernEnd, PAGE_KERN);
226 //Add_Page_Range(kernEnd, ISA_HOLE_START, PAGE_AVAIL);
227 // ISA hole remains closed (no actual memory)
228 // Add_Page_Range(ISA_HOLE_START, ISA_HOLE_END, PAGE_HW);
230 //Add_Page_Range(ISA_HOLE_END, HIGHMEM_START, PAGE_ALLOCATED);
231 // Add_Page_Range(HIGHMEM_START, HIGHMEM_START + KERNEL_HEAP_SIZE, PAGE_HEAP);
232 //Add_Page_Range(HIGHMEM_START + KERNEL_HEAP_SIZE, endOfMem, PAGE_AVAIL);
233 /* JRL: move page list after kernel heap */
235 //Now, above the VM region...
237 // Kernel thread object
238 Add_Page_Range(KERNEL_THREAD_OBJECT,KERNEL_THREAD_OBJECT+KERNEL_THREAD_OBJECT_SIZE,PAGE_ALLOCATED);
240 Add_Page_Range(KERNEL_STACK,KERNEL_STACK+KERNEL_STACK_SIZE,PAGE_ALLOCATED);
242 Add_Page_Range(KERNEL_HEAP,KERNEL_HEAP+KERNEL_HEAP_SIZE,PAGE_HEAP);
244 Add_Page_Range(pageListAddr, pageListEnd, PAGE_KERN);
246 Add_Page_Range(pageListEnd,Round_Down_To_Page(FINAL_KERNEL_START), PAGE_AVAIL);
248 Add_Page_Range(Round_Down_To_Page(FINAL_KERNEL_START),Round_Up_To_Page(FINAL_VMBOOTEND+1),PAGE_KERN);
250 // IDT (this should be one page)
251 Add_Page_Range(IDT_LOCATION,TSS_LOCATION,PAGE_KERN);
252 // TSS (this should be one page)
253 Add_Page_Range(TSS_LOCATION,GDT_LOCATION, PAGE_KERN);
254 // GDT (this should be one page)
255 Add_Page_Range(GDT_LOCATION,TOP_OF_MEM, PAGE_KERN);
257 /* Initialize the kernel heap */
258 Init_Heap(KERNEL_HEAP, KERNEL_HEAP_SIZE);
260 PrintBoth("%uKB memory detected, %u pages in freelist, %d bytes in kernel heap\n",
261 bootInfo->memSizeKB, g_freePageCount, KERNEL_HEAP_SIZE);
263 PrintBoth("Memory Layout:\n");
264 PrintBoth("%x to %x - VM\n",START_OF_VM,START_OF_VM+VM_SIZE-1);
265 PrintBoth("%x to %x - INITIAL THREAD\n",KERNEL_THREAD_OBJECT,KERNEL_THREAD_OBJECT+KERNEL_THREAD_OBJECT_SIZE-1);
266 PrintBoth("%x to %x - KERNEL STACK\n",KERNEL_STACK,KERNEL_STACK+KERNEL_STACK_SIZE-1);
267 PrintBoth("%x to %x - KERNEL HEAP\n",KERNEL_HEAP,KERNEL_HEAP+KERNEL_HEAP_SIZE-1);
268 PrintBoth("%lx to %lx - PAGE LIST\n",pageListAddr,pageListEnd-1);
269 PrintBoth("%lx to %x - FREE\n",pageListEnd,FINAL_KERNEL_START-1);
270 PrintBoth("%x to %x - KERNEL CODE\n",FINAL_KERNEL_START,FINAL_KERNEL_END);
271 PrintBoth("%x to %x - BIOS\n",FINAL_BIOS_START,FINAL_BIOS_END);
272 PrintBoth("%x to %x - VGABIOS\n",FINAL_VGA_BIOS_START,FINAL_VGA_BIOS_END);
273 PrintBoth("%x to %x - VMXASSIST\n",FINAL_VMXASSIST_START,FINAL_VMXASSIST_END);
274 PrintBoth("%x to %x - BIOS (2nd copy)\n",FINAL_BIOS2_START,FINAL_BIOS2_END);
275 PrintBoth("%x to %x - IDT\n",IDT_LOCATION,TSS_LOCATION-1);
276 PrintBoth("%x to %x - TSS\n",TSS_LOCATION,GDT_LOCATION-1);
277 PrintBoth("%x to %x - GDT\n",GDT_LOCATION,TOP_OF_MEM-1);
282 * Initialize the .bss section of the kernel executable image.
286 extern char BSS_START, BSS_END;
288 /* Fill .bss with zeroes */
289 memset(&BSS_START, '\0', &BSS_END - &BSS_START);
290 PrintBoth("BSS Inited, BSS_START=%x, BSS_END=%x\n",BSS_START,BSS_END);
294 * Allocate a page of physical memory.
296 void* Alloc_Page(void)
301 bool iflag = Begin_Int_Atomic();
303 /* See if we have a free page */
304 if (!Is_Page_List_Empty(&s_freeList)) {
305 /* Remove the first page on the freelist. */
306 page = Get_Front_Of_Page_List(&s_freeList);
307 KASSERT((page->flags & PAGE_ALLOCATED) == 0);
308 Remove_From_Front_Of_Page_List(&s_freeList);
310 /* Mark page as having been allocated. */
311 page->flags |= PAGE_ALLOCATED;
313 result = (void*) Get_Page_Address(page);
316 End_Int_Atomic(iflag);
322 * Free a page of physical memory.
324 void Free_Page(void* pageAddr)
326 ulong_t addr = (ulong_t) pageAddr;
330 iflag = Begin_Int_Atomic();
332 KASSERT(Is_Page_Multiple(addr));
334 /* Get the Page object for this page */
335 page = Get_Page(addr);
336 KASSERT((page->flags & PAGE_ALLOCATED) != 0);
338 /* Clear the allocation bit */
339 page->flags &= ~(PAGE_ALLOCATED);
341 /* Put the page back on the freelist */
342 Add_To_Back_Of_Page_List(&s_freeList, page);
345 End_Int_Atomic(iflag);