00001 /* ndk - [ memory.c ] 00002 * 00003 * ndk maintains "page stacks" in order to manage memory in the physical 00004 * address space. This code relies heavily on the paging hardware and 00005 * interface described in the pager source. 00006 * 00007 * If done correctly, it should be able to create an rZero implementation 00008 * of this memory management code, and the pager, to allow for 00009 * interchangeable methods of managing memory. The two modules, however, 00010 * would probably have to be combined into one rZero module, due to 00011 * their reliance on one another. 00012 * 00013 * Please see: 00014 * /doc/memoryManagement.kwd 00015 * /doc/memoryMap.kwd 00016 * /src/pager.{c,h} 00017 * 00018 * (c)2002 dcipher / neuraldk 00019 * www.neuraldk.org 00020 */ 00021 00022 #include "memory.h" 00023 00024 // global variables describing memory size 00025 long totalMemory, totalPages; 00026 long dmaMemorySize, appMemorySize; 00027 long dmaMemoryPages, appMemoryPages; 00028 00029 // staring and ending pages which the kernel occupies 00030 long kernelStart; 00031 long kernelEnd; 00032 00033 pageStack appStack; // memory available for apps (and kernel...) 00034 pageStack dmaStack; // memory below 16MB, used for DMA 00035 00036 void memoryInit(void) { 00037 /* currently ndk only reserves 4MB for DMA... and it 00038 * should be noted the OS itself is withen the first 4MB. 00039 * I might port this to an rZero module and allow it to 00040 * be run time configureable (through rZero module's 00041 * "init" routine). 00042 */ 00043 00044 consoleOut("Usable memory: [Lower: %dkb, Upper %dkb]\n", 00045 multibootInfo->mem_lower, 00046 multibootInfo->mem_upper); 00047 00048 // don't use mem_lower to calculate total memory... it'll return 00049 // 640k (ie, it doesn't include the bios). Use 1024k (ie, 1MB) 00050 totalMemory = (multibootInfo->mem_upper + 1024) * 1024; 00051 totalPages = totalMemory / PAGE_SIZE; 00052 00053 // calculate the size (in bytes and pages) of the dma memory range 00054 dmaMemorySize = 4*1024*1024; 00055 dmaMemoryPages = dmaMemorySize / PAGE_SIZE; 00056 00057 // calculate the size (in bytes and pages) of the app memory range 00058 appMemorySize = totalMemory - dmaMemorySize; 00059 appMemoryPages = totalPages - dmaMemoryPages; 00060 00061 // output some info about or stacks 00062 //consoleOut("bssEnd: 0x%x\n", bssEnd); 00063 consoleOut("DMA Stack: %d pages\n", dmaMemoryPages); 00064 consoleOut("App Stack: %d pages\n", appMemoryPages); 00065 00066 // kernel is currently always fixed @ 1MB mark, but could be any length 00067 // Use bssEnd to calculate total space used by kernel, remember to round 00068 // kernelEnd _UP_ to the nearest page. 00069 kernelStart = (1024 * 1024) / PAGE_SIZE; 00070 kernelEnd = ((long)&bssEnd + PAGE_SIZE - ((long)&bssEnd & (PAGE_SIZE-1))) / PAGE_SIZE; 00071 00072 // create both the dma and app stacks in memory above the kernel and 00073 // rZero modules 00074 memoryCreateStacks(); 00075 //memoryAllocateKernel(); 00076 00077 return; 00078 } 00079 00080 /* find a place to put the memory stack, above the kernel 00081 * and rZero modules 00082 * the kernel loads itself @1MB, with rZero modules afterwards 00083 * therefore, to find the end of the kernel, find the end of 00084 * the last rZero module 00085 */ 00086 void memoryCreateStacks(void) { 00087 module_t *mod; 00088 long addr; 00089 int i; 00090 00091 // get the end address of the last module 00092 mod = (module_t *) multibootInfo->mods_addr; 00093 addr = (long)mod[multibootInfo->mods_count-1].mod_end; 00094 00095 // make sure it's at least dword aligned (rounding up!) 00096 addr += 4 - (addr & 3); 00097 consoleOut("addr: 0x%8x\n", addr); 00098 00099 /* The following calculations gave me much grief when 00100 * initially developing this code. I had replaced addr 00101 * with dmaStack.base, or appStack.base when calculating 00102 * the used base pointer... although the two are exactly 00103 * equivalent (consoleOut confirms), the calculation came 00104 * out wrong! Just a word of warning... 00105 */ 00106 00107 // first put the dma stack, then the app stack 00108 consoleOut("Creating DMA stack at: 0x%8x\n", addr); 00109 dmaStack.total = dmaMemoryPages; 00110 dmaStack.avail.flags = STACK_EXPAND_DOWN; 00111 dmaStack.avail.pointer = 0; //dmaStack.total - 1; 00112 dmaStack.avail.base = addr; 00113 dmaStack.used.flags = STACK_EXPAND_UP; 00114 dmaStack.used.base = (addr + 4 * dmaMemoryPages); 00115 dmaStack.used.pointer = 0; 00116 //memoryStackSpecs(&dmaStack); 00117 00118 addr = (long)dmaStack.used.base; // NOTE: + 4 needed? 00119 consoleOut("Creating Application stack at: 0x%8x\n", addr); 00120 appStack.total = appMemoryPages; 00121 appStack.avail.flags = STACK_EXPAND_DOWN; 00122 appStack.avail.pointer = 0; //appStack.total - 1; 00123 appStack.avail.base = addr; 00124 appStack.used.flags = STACK_EXPAND_UP; 00125 appStack.used.base = (addr + 4 * appMemoryPages); 00126 appStack.used.pointer = 0; 00127 //memoryStackSpecs(&appStack); 00128 00129 // fill the dma stack with all pages (unless they're already in 00130 // use, by the kernel) 00131 for(i = 0; i < dmaStack.total; i++) { 00132 if(!memoryPageUsedByKernel(i)) 00133 stackPush(&dmaStack.avail, i * PAGE_SIZE); 00134 } 00135 00136 // fill the app stack with all pages (unless in use by kernel) 00137 for(i = dmaMemoryPages; i < (appStack.total + dmaMemoryPages); i++) { 00138 if(!memoryPageUsedByKernel(i)) 00139 stackPush(&appStack.avail, i * PAGE_SIZE); 00140 } 00141 00142 // print some info 'bout kernel and stacks 00143 consoleOut("Kernel start %d, end %d\n", kernelStart, kernelEnd); 00144 00145 // what about bss of last module? 00146 // its dynamically allocated by this memory manager, and 00147 // linked in by the rZero code... it's all good! :) 00148 } 00149 00150 /* Quick and dirty method to tell memoryCreateStacks wether a 00151 * certain page is already in use by the kernel (ie, it was used 00152 * before memory management was introduced). If it's in use, 00153 * the page isn't included into the page stack. 00154 * 00155 * It should also be noted that it currently isn't included in 00156 * the "used" portion of the stack... there really isn't any 00157 * need for this, either... we aren't going to deallocate the 00158 * kernel! 00159 */ 00160 long memoryPageUsedByKernel(long page) { 00161 // if page is withen 0xA0000 and 0xF0000, inclusive, it's the 00162 // bios... don't allow this to be allocated! :) 00163 if(page >= 0xA0000/PAGE_SIZE && page <= 0xF0000/PAGE_SIZE) 00164 return 1; 00165 00166 // if page is withen 0x90000 and 0x93000 (inc), it's being used for 00167 // system and vm86 page directory + first table, allocate it 00168 if(page >= 0x90000/PAGE_SIZE && page <= 0x93000/PAGE_SIZE) 00169 return 1; 00170 00171 // if page is withen kernel address range, allocate it 00172 if(page >= kernelStart && page <= kernelEnd) 00173 return 1; 00174 00175 // first two pages used by pmode idt + gdt and real mode idt 00176 if(page == 0 || page == 1) return 1; 00177 00178 // we could also provide provisions to allocate the rZero modules 00179 // at this point... but this is already pretty slow... doing this 00180 // check on every single page in the system? Eh... 00181 return 0; 00182 } 00183 00184 /* Allocates one (4096) page, returning the physical address. 00185 * The function makes no attempt at mapping into the linear 00186 * address space! 00187 */ 00188 void *memoryAllocUnmappedPage(void) { 00189 long physAddr; 00190 00191 if(appStack.avail.pointer == 0) { 00192 consoleOut("Not enough physical memory!\n"); 00193 return 0; 00194 } 00195 00196 // place the physical address onto the used stack, and 00197 // return it 00198 physAddr = stackPop(&appStack.avail); 00199 stackPush(&appStack.used, physAddr); 00200 00201 return (void *)physAddr; 00202 } 00203 00204 void *memoryAllocPage(void) { 00205 long physAddr; 00206 long linAddr; 00207 00208 physAddr = (long)memoryAllocUnmappedPage(); 00209 //consoleOut("Physical Addr: 0x%x\n", physAddr); 00210 linAddr = (long)memoryMapPage((void *)physAddr); 00211 //consoleOut("Linear Addr: 0x%x\n", linAddr); 00212 00213 return (void *)linAddr; 00214 } 00215 00216 // NOTE: untested!!! 00217 void *memoryAllocPages(long total) { 00218 long firstPagePhys; 00219 long firstPageLin; 00220 long pagePhys; 00221 long pageLin; 00222 00223 if(total == 0) return NULL; 00224 if(total == 1) return memoryAllocPage(); 00225 00226 // we now know we want at least two pages, exactly enough to 00227 // fit a "multi-page" entry in the page stack 00228 00229 // grab those two pages 00230 firstPagePhys = (long)stackPop(&appStack.avail); 00231 pagePhys = (long)stackPop(&appStack.avail); 00232 00233 // convert the first page to a linear address (and map it) 00234 firstPageLin = (long)memoryMapPage((void *)firstPagePhys); 00235 00236 // and add the multi-page entry into the used stack 00237 stackPush(&appStack.used, firstPageLin & MULTI_PAGE_ENTRY); 00238 stackPush(&appStack.used, total); 00239 00240 // we've just allocated two pages... account for them... 00241 total -= 2; 00242 00243 // allocate all other pages 00244 while(total--) { 00245 // get a new page 00246 pagePhys = (long)stackPop(&appStack.avail); 00247 // and map it (we don't care about the return value) 00248 memoryMapPage((void *)pagePhys); 00249 } 00250 00251 return (void *)firstPageLin; 00252 } 00253 00254 // NOTE: This is not MULTI_PAGE_ENTRY aware yet!!! 00255 void memoryFreePage(void *linAddr) { 00256 long *pages = (long *)0xffc00000; 00257 long physAddr; 00258 pageStack *pStack; 00259 long i = 0; 00260 long pTable, pTableIndex; 00261 unsigned long pageOffset; 00262 00263 // convert the linear address to a physical one 00264 // NOTE: because of the "and" mask, this function will 00265 // deallocate addresses that aren't page aligned. 00266 // ie, 0x1005 will remove 0x1000 00267 linAddr = (void *)((long)linAddr & 0xfffff000); 00268 physAddr = (long)pagerLinToPhys(linAddr); 00269 consoleOut("removing physical address 0x%8x\n", physAddr); 00270 00271 // select the correct page stack, based on where the physical 00272 // address resides 00273 if((long)physAddr < TOP_OF_DMA_STACK) pStack = &dmaStack; 00274 else pStack = &appStack; 00275 00276 // find the physical address in the used stack 00277 while(stackPeek(&pStack->used, i) != physAddr) { 00278 i++; 00279 if(i == pStack->used.pointer) { 00280 consoleOut("Cannot deallocate 0x%8x; Not allocated!\n", physAddr); 00281 return; 00282 } 00283 } 00284 00285 consoleOut("Copying from used to available stack\n"); 00286 // copy address from used stack, to available stack 00287 // and "increment" pointers accordingly. 00288 stackRemove(&pStack->used, i, 1); 00289 stackPush(&pStack->avail, physAddr); 00290 00291 00292 // now remove page from page directory (include in pager.c?!) 00293 pageOffset = (unsigned long)linAddr >> 12; 00294 consoleOut("Removing from page directory (%d)\n", pageOffset); 00295 00296 consoleOut("Invalidating TLB (0x%8x)\n", (unsigned long)linAddr); 00297 invalidatePage( (unsigned long)linAddr ); 00298 00299 pages[(unsigned long)pageOffset] = 0; 00300 00301 return; 00302 } 00303 00304 /* 00305 void *memoryAllocatePages(long numPages) { 00306 void *linStartAddr; 00307 long physStartAddr; 00308 long i, pointer; 00309 00310 // check if there's enough pages available 00311 if(appStack.pointer < numPages) { 00312 consoleOut("Not enough physical memory!\n"); 00313 return 0; 00314 } else { 00315 // remove these pages from the stack right away, so any other 00316 // attempt to call this function won't allocate the same 00317 // range of memory... or should I loop through this one at a time... (yes) 00318 pointer = appStack.pointer; 00319 appStack.pointer -= numPages; 00320 } 00321 00322 // keep track of the starting page in physical and linear address space 00323 physStartAddr = appStack.base[pointer--]; 00324 consoleOut("First page, physical: 0x%8x ", physStartAddr); 00325 00326 // find a chunk in linear address space that'll hold the newly allocated 00327 // memory 00328 linStartAddr = (void *)memoryFindLinearChunk(numPages); 00329 if(linStartAddr = 0x0) { 00330 consoleOut("Not enough linear memory!\n"); 00331 return 0; 00332 } 00333 //linStartAddr = (void *)pagerMapPage(physStartAddr); 00334 consoleOut("linear 0x%8x\n", linStartAddr); 00335 00336 // map the rest of the pages into the apps linear address space 00337 for(i = 1; i < numPages; i++) { 00338 consoleOut("Allocating page #%d\n", i); 00339 pagerMapPage(appStack.base[pointer--]); 00340 } 00341 00342 consoleOut("Marking pages as used\n"); 00343 // now mark these pages into the "used" stack 00344 if(numPages > 1) { 00345 consoleOut(" more than one\n"); 00346 *(long *)(appStack.used.base - (appStack.used.pointer++)) = numPages; 00347 *(long *)(appStack.used.base - (appStack.used.pointer++)) = physStartAddr & 00348 (MULTI_PAGE_ENTRY); 00349 } 00350 else { 00351 consoleOut(" just one\n"); 00352 *(long *)(appStack.used.base - (appStack.used.pointer++)) = physStartAddr; 00353 } 00354 consoleOut("returning\n"); 00355 return linStartAddr; 00356 } 00357 */ 00358 00359 /* 00360 void memoryFreePages(void *addr) { 00361 long pageNum = 0; 00362 long numPages; 00363 long pointer; 00364 long *pages; 00365 long physAddr; 00366 pageStack *pStack; 00367 00368 // Because the page directory is mapped into itself, as the last 00369 // page table, all physical pages in the linear address space are 00370 // included in the top 4MB... 00371 // 00372 pages = (long *)0xffc00000; 00373 00374 // addr will be in linear address space... convert it to physical 00375 while(pages[pageNum] != (long)addr) pageNum++; 00376 physAddr = pages[pageNum]; 00377 00378 if((long)physAddr < TOP_OF_DMA_STACK) pStack = &dmaStack; 00379 else pStack = &appStack; 00380 00381 // now search for this physical address in the used page stack 00382 pointer = pStack->used.pointer; 00383 while( (pStack->used.base[pointer] & 0xfffff000) != physAddr) { 00384 if(pStack->used.base[pointer] & MULTI_PAGE_ENTRY) pointer -= 2; 00385 else pointer -= 1; 00386 } 00387 00388 // how many pages are associated with this deallocation? 00389 if(pStack->used.base[pointer] & MULTI_PAGE_ENTRY) 00390 numPages = pStack->used.base[pointer-1]; 00391 else numPages = 1; 00392 00393 // remove pages from the used stack... 00394 // copy top of stack overtop of, soon-to-be, deallocated page 00395 pStack->used.base[pointer] = pStack->used.base[pStack->used.pointer]; 00396 pStack->used.pointer--; 00397 00398 // if this is a multi-page deallocation, copy over the page count too 00399 if(numPages > 1) { 00400 pStack->used.base[pointer-1] = pStack->used.base[pStack->used.pointer]; 00401 pStack->used.pointer--; 00402 } 00403 00404 // and, finally, remove these pages from the tasks paging directory 00405 while(numPages--) 00406 pages[pageNum++] = 0; 00407 } 00408 */ 00409 00410 /* Maps physAddr into the linear address space where-ever there 00411 * is an available page (should be in pager.c!?) 00412 */ 00413 void *memoryMapPage(void *physAddr) { 00414 long *pages = (long *)0xffc00000; 00415 long pageNum = ALLOC_OFFSET; 00416 long pTableNum; 00417 00418 consoleOut("memoryMapPage: 0x%8x\n", physAddr); 00419 pTableNum = pageNum / 1024; 00420 pageNum &= 1023; 00421 while(1) { 00422 // if the current page table isn't allocated, create it 00423 if(!pagerTableExists(pTableNum)) 00424 pagerCreateTable(pTableNum); 00425 00426 // if this page is empty, use it, and return the 00427 // linear address 00428 // NOTE: pages[pageNum] not correct... pageNum = 0...1023 00429 if(pages[(pTableNum*1024)+pageNum] == 0) { 00430 pages[(pTableNum*1024)+pageNum] = (long) physAddr | PAGE_PRESENT 00431 | PAGE_READ_WRITE 00432 | PAGE_P3_ACCESS; 00433 return (void *)((pTableNum*1024)*4096)+(pageNum * 4096); 00434 } 00435 00436 // incrememnt to the next page 00437 pageNum++; 00438 00439 // if we've gone through 1024 pages, go to the next table 00440 if(pageNum == 1024) { 00441 pTableNum++; 00442 pageNum = 0; 00443 } 00444 00445 // out of memory! 00446 if(pTableNum == 1024) return 0; 00447 } 00448 } 00449 00450 /* 00451 void *memoryFindLinearChunk(long size) { 00452 void *linearAddr; 00453 long *pages = 0xffc00000; 00454 //long *pgDir = 0xfffff000; 00455 long pTableNum; 00456 long pageNum; 00457 // start allocating pages at the 2GB mark (linear) 00458 long startAtPage = ALLOC_OFFSET;//524288; 00459 long remainingPages = size; 00460 00461 pageNum = startAtPage; 00462 remainingPages = size; 00463 pTableNum = pageNum / 1024; 00464 while(remainingPages) { 00465 if(!pagerTableExists(pTableNum)) 00466 pagerCreateTable(pTableNum); 00467 00468 // is the next page empty? 00469 if(pages[pageNum] == 0) { 00470 remainingPages--; 00471 } else { 00472 remainingPages = size; 00473 } 00474 00475 // incrememnt to the next page 00476 pageNum++; 00477 00478 // if we've gone through 1024 pages, go to the next table 00479 if(pageNum == 1024) { 00480 pTableNum++; 00481 pageNum = 0; 00482 } 00483 } 00484 00485 return linearAddr; 00486 } 00487 */ 00488 00489 /* Allocate only the kernel data, code and bss sections. The rZero 00490 * modules will be allocated via separate calls to the memory manager, 00491 * so that they may be removed from memory independantly from the 00492 * kernel (such as the splashScreen, which serves no purpose in memory 00493 * past boot up). 00494 */ 00495 /* 00496 void memoryAllocateKernel(void) { 00497 // allocate the kernel itself 00498 memoryAllocateUsedPages(kernelStart, kernelEnd - kernelStart); 00499 // allocate the pmode idt + gdt, and real mode idt 00500 memoryAllocateUsedPages(0, 2); 00501 // allocate the kernel + vm86 page directory + table 00502 memoryAllocateUsedPages(0x90000/PAGE_SIZE, 4); 00503 } 00504 */ 00505 00506 /* This function is used the allocate pages that have been used by the 00507 * kernel, prior to memory management being introduced 00508 */ 00509 /* 00510 void memoryAllocateUsedPages(long startPage, long totalPages) { 00511 pageStack *ps; 00512 long i; 00513 long startAddr = startPage * PAGE_SIZE; 00514 00515 // allocate from the correct stack (problems arise 00516 // if the allocation spans tables, but it never should 00517 // at this point... the kernel should pretty much 00518 // always reside withen the dmaStack anyway) 00519 if(startPage < dmaStack.total) pageStack = &dmaStack; 00520 else pageStack = &appStack; 00521 00522 // find the right page 00523 for(i = 0; i < ps->pointer; i++) 00524 if(startAddr == ps->base[i]) { 00525 // remove these pages from the stack, copying the 00526 // other address upwards 00527 memcpy(&ps->base[i], &ps->base[i] + (totalPages*4), 00528 &ps->base[pointer] - (startPage + totalPages); 00529 } 00530 } 00531 */ 00532 00533 void memoryStackSpecs(pageStack *pStack) { 00534 consoleOut(" Total: %d\n" 00535 " pointer: %d\n" 00536 " base: 0x%8x\n" 00537 " Used:\n" 00538 " pointer: %d\n" 00539 " base: 0x%8x\n", 00540 pStack->total, 00541 pStack->avail.pointer, 00542 pStack->avail.base, 00543 pStack->used.pointer, 00544 pStack->used.base 00545 ); 00546 }