//----------------------------------------------------------------------------// // OS on Kaleid // // // // Desc: Page allocator related functions // // // // // // Copyright © 2018-2021 The OS/K Team // // // // This file is part of OS/K. // // // // OS/K is free software: you can redistribute it and/or modify // // it under the terms of the GNU General Public License as published by // // the Free Software Foundation, either version 3 of the License, or // // any later version. // // // // OS/K is distributed in the hope that it will be useful, // // but WITHOUT ANY WARRANTY//without even the implied warranty of // // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // // GNU General Public License for more details. // // // // You should have received a copy of the GNU General Public License // // along with OS/K. If not, see . // //----------------------------------------------------------------------------// #include #include #include #include #include #include #include #include #include //--------- enum { Whatever = 1UL << 52, Whatever2 = 1UL << 62 }; static AllocatedPage_t busyPagesList = { (void*)0, (void*)0, 0, (AllocatedPage_t*)0 }; extern MemoryMap_t memoryMap; extern ulong MmPhysLastKernAddress; static ulong NSuccessfulAlloc = 0; static ulong NSuccessfulFree = 0; //--------- // Returns 0 if page is not busy, its last address if yes static ulong isPageBusy(void *phyPageAddr) { AllocatedPage_t *busyPage = &busyPagesList; ulong isBusy = 0; // In case of NVS, ACPI or BADRAM zone, considered busy if (MmIsBusyZone(phyPageAddr)) { return 1; } // Search in the busylist if the phy addr is here while(busyPage->next) { busyPage = busyPage->next; if (phyPageAddr >= busyPage->phyAddressBegin && phyPageAddr <= busyPage->phyAddressEnd ) { isBusy = (ulong)busyPage->phyAddressEnd; break; } } return isBusy; } static void printBusyPages(void) { AllocatedPage_t *busyPage = &busyPagesList; if (!busyPage->next) { KernLog("No busy page\n"); } else { while(busyPage->next) { busyPage = busyPage->next; KernLog("Busy page at %p-%p\n", busyPage->phyAddressBegin, busyPage->phyAddressEnd); } } } ulong MmGetBusyPageSize(void) { ulong c = 0; AllocatedPage_t *busyPage = &busyPagesList; if (!busyPage->next) { return 0; } else { while(busyPage->next) { busyPage = busyPage->next; c += 4096 + (busyPage->phyAddressEnd - busyPage->phyAddressBegin); } } return c; } ulong MmGetTotalPageSize(void) { // Maximum PHYSICAL address in memory ulong phRamSize = memoryMap.freeRamSize; return ((phRamSize & 0xFFFFFFFFFF000) - ((MmPhysLastKernAddress + KPAGESIZE) & 0xFFFFFFFFFF000)); } static void addPageToBusyList(void *phyPageAddrBegin, void *phyPageAddrEnd, ulong id) { AllocatedPage_t *busyPage = &busyPagesList; AllocatedPage_t *prevBusyPage = NULL; while(busyPage->next) { prevBusyPage = busyPage; busyPage = busyPage->next; if (busyPage->phyAddressBegin > phyPageAddrEnd) { busyPage = prevBusyPage; break; } } AllocatedPage_t *newBusyPage = (AllocatedPage_t*)malloc(sizeof(AllocatedPage_t)); newBusyPage->phyAddressBegin = phyPageAddrBegin; newBusyPage->phyAddressEnd = phyPageAddrEnd; newBusyPage->id = id; newBusyPage->next = busyPage->next; busyPage->next = newBusyPage; } static void removePageFromBusyList(void *phyPageAddrBegin) { AllocatedPage_t *busyPage = &busyPagesList; AllocatedPage_t *prevBusyPage = NULL; while(busyPage->next) { prevBusyPage = busyPage; busyPage = busyPage->next; if (phyPageAddrBegin == busyPage->phyAddressBegin) { prevBusyPage->next = busyPage->next; free(busyPage); break; } } } // // Returns an id to identify a page frame allocated (kernel) // ulong MmAllocPageFrame(size_t size, bool contiguous) { assert(size); static ulong id = 0; ulong pageNumber = (((ulong)size) / KPAGESIZE) + 1; ulong curNumber = 0; void *futureBegin = 0; void *futureEnd = 0; ulong busyLastAddr = 0; // Incrementing id id++; // Maximum PHYSICAL address in memory ulong phRamSize = memoryMap.freeRamSize + memoryMap.nonfreeRamSize; DebugLog("Allocating %d pages (%u o)...\n", pageNumber, size); // Through the pages in physical memory... for (void *curPage = (void*)((MmPhysLastKernAddress + KPAGESIZE) & 0xFFFFFFFFFF000); curPage < (void*)(phRamSize & 0xFFFFFFFFFF000); curPage += KPAGESIZE) { busyLastAddr = isPageBusy(curPage); if (!busyLastAddr) { if (!futureBegin) { futureBegin = curPage; //DebugLog("Select begin : %p\n", curPage); } futureEnd = curPage; //DebugLog("Select page : %p\n", curPage); if (++curNumber >= pageNumber) { break; } } else { if (!contiguous) { if (futureBegin && futureEnd) addPageToBusyList(futureBegin, futureEnd, id); futureBegin = 0; } else { curNumber = 0; futureBegin = 0; } } } if (futureBegin && futureEnd) addPageToBusyList(futureBegin, futureEnd, id); if (curNumber != pageNumber) { KeStartPanic("MmAllocPageFrame: Out of memory"); return 0; } NSuccessfulAlloc++; DebugLog("Allocate page frame id %d, size %d MB, contiguousness %d\n", id, size / MB, contiguous ); return id; } // // Frees a page frame by its id // void MmFreePageFrame(ulong id) { assert(id); AllocatedPage_t *busyPage = &busyPagesList; bool success = false; while(busyPage->next) { busyPage = busyPage->next; if (id == busyPage->id) { removePageFromBusyList(busyPage->phyAddressBegin); success = true; } } if (success) { NSuccessfulFree++; DebugLog("Free page frame id %d\n", id ); } } // // Maps an allocated page frame to the given address // error_t MmMapPageFrame(ulong id, void *virtAddr, ulong flags) { assert(id); AllocatedPage_t *busyPage = &busyPagesList; void *virtAddrBegin = virtAddr; while(busyPage->next) { busyPage = busyPage->next; if (id == busyPage->id) { for (void *addr = busyPage->phyAddressBegin; addr <= busyPage->phyAddressEnd; addr += KPAGESIZE) { if (MmTransPhyToVirtAddr(addr)) { return EADDRINUSE; } //DebugLog("Map %p at %p\n", addr, virtAddr); MmMapPage((void*)((ulong)virtAddr), (void*)addr, flags); virtAddr += KPAGESIZE; } } } DebugLog("Page frame id %d mapped from %p to %p\n", id, virtAddrBegin, virtAddr); return EOK; } error_t MmUnmapPageFrame(ulong id) { assert(id); AllocatedPage_t *busyPage = &busyPagesList; void *actualPhys = 0; while(busyPage->next) { busyPage = busyPage->next; if (id == busyPage->id) { for (void *addr = busyPage->phyAddressBegin; addr <= busyPage->phyAddressEnd; addr += KPAGESIZE) { actualPhys = MmTransPhyToVirtAddr(addr); //DebugLog("Map %p at %p\n", addr, virtAddr); if (actualPhys && id == busyPage->id) { MmUnmapPage(MmTransPhyToVirtAddr(addr)); } } } } DebugLog("Page frame id %d unmapped\n", id); return EOK; }