diff --git a/include/pmm.h b/include/pmm.h new file mode 100644 index 0000000..87f037f --- /dev/null +++ b/include/pmm.h @@ -0,0 +1,18 @@ +#ifndef FROSTBYTE_PMM_H +#define FROSTBYTE_PMM_H + +#include "types.h" + +#define frameSize 4096 + +void pmmInit(uint64 multibootInfo, uint64 protectedEnd); +uint64 pmmAlloc(void); +void pmmFree(uint64 frame); +void pmmReserve(uint64 start, uint64 size); +uint64 pmmReservedEnd(void); +uint64 pmmTotalFrames(void); +uint64 pmmUsedFrames(void); +uint64 pmmFreeFrames(void); +uint64 pmmTotalBytes(void); + +#endif diff --git a/src/pmm.c b/src/pmm.c new file mode 100644 index 0000000..0a670b5 --- /dev/null +++ b/src/pmm.c @@ -0,0 +1,165 @@ +#include "pmm.h" +#include "multiboot2.h" +#include "string.h" + +extern uint8 kernelPhysEnd[]; + +static uint8* bitmap; +static uint64 frameCount; +static uint64 usedFrames; +static uint64 totalMemoryBytes; +static uint64 reservedEnd; +static uint64 allocHint; + +static void markUsed(uint64 frame) +{ + if (frame >= frameCount) + return; + uint64 index = frame / 8; + uint8 bit = (uint8)(1 << (frame % 8)); + if (!(bitmap[index] & bit)) { + bitmap[index] |= bit; + usedFrames++; + } +} + +static void markFree(uint64 frame) +{ + if (frame >= frameCount) + return; + uint64 index = frame / 8; + uint8 bit = (uint8)(1 << (frame % 8)); + if (bitmap[index] & bit) { + bitmap[index] &= (uint8)~bit; + usedFrames--; + } +} + +static bool isUsed(uint64 frame) +{ + return (bitmap[frame / 8] & (1 << (frame % 8))) != 0; +} + +static uint64 alignUp(uint64 value, uint64 alignment) +{ + return (value + alignment - 1) & ~(alignment - 1); +} + +void pmmReserve(uint64 start, uint64 size) +{ + uint64 first = start / frameSize; + uint64 last = (start + size + frameSize - 1) / frameSize; + for (uint64 frame = first; frame < last; frame++) + markUsed(frame); +} + +uint64 pmmReservedEnd(void) +{ + return reservedEnd; +} + +void pmmInit(uint64 multibootInfo, uint64 protectedEnd) +{ + uint32 totalSize = *(uint32*)multibootInfo; + uint64 tagAddr = multibootInfo + 8; + uint64 endAddr = multibootInfo + totalSize; + + MultibootTagMmap* mmap = nullptr; + uint64 highestAddr = 0; + + while (tagAddr < endAddr) { + MultibootTag* tag = (MultibootTag*)tagAddr; + if (tag->type == multibootTagTypeEnd) + break; + if (tag->type == multibootTagTypeMmap) + mmap = (MultibootTagMmap*)tag; + tagAddr = alignUp(tagAddr + tag->size, 8); + } + + if (mmap) { + uint64 entryAddr = (uint64)mmap->entries; + uint64 mmapEnd = (uint64)mmap + mmap->size; + while (entryAddr < mmapEnd) { + MultibootMmapEntry* entry = (MultibootMmapEntry*)entryAddr; + if (entry->type == multibootMemoryAvailable) { + uint64 regionEnd = entry->baseAddr + entry->length; + totalMemoryBytes += entry->length; + if (regionEnd > highestAddr) + highestAddr = regionEnd; + } + entryAddr += mmap->entrySize; + } + } + + if (highestAddr == 0) + highestAddr = 128 * 1024 * 1024; + + frameCount = highestAddr / frameSize; + + uint64 bitmapAnchor = (uint64)kernelPhysEnd; + if (protectedEnd > bitmapAnchor) + bitmapAnchor = protectedEnd; + bitmap = (uint8*)alignUp(bitmapAnchor, frameSize); + uint64 bitmapBytes = (frameCount + 7) / 8; + memset(bitmap, 0xFF, bitmapBytes); + usedFrames = frameCount; + + if (mmap) { + uint64 entryAddr = (uint64)mmap->entries; + uint64 mmapEnd = (uint64)mmap + mmap->size; + while (entryAddr < mmapEnd) { + MultibootMmapEntry* entry = (MultibootMmapEntry*)entryAddr; + if (entry->type == multibootMemoryAvailable) { + uint64 first = alignUp(entry->baseAddr, frameSize) / frameSize; + uint64 last = (entry->baseAddr + entry->length) / frameSize; + for (uint64 frame = first; frame < last; frame++) + markFree(frame); + } + entryAddr += mmap->entrySize; + } + } + + reservedEnd = alignUp((uint64)bitmap + bitmapBytes, frameSize); + pmmReserve(0, reservedEnd); + allocHint = reservedEnd / frameSize; +} + +uint64 pmmAlloc(void) +{ + for (uint64 offset = 0; offset < frameCount; offset++) { + uint64 frame = allocHint + offset; + if (frame >= frameCount) + frame -= frameCount; + if (!isUsed(frame)) { + markUsed(frame); + allocHint = frame + 1; + return frame * frameSize; + } + } + return 0; +} + +void pmmFree(uint64 frame) +{ + markFree(frame / frameSize); +} + +uint64 pmmTotalFrames(void) +{ + return frameCount; +} + +uint64 pmmUsedFrames(void) +{ + return usedFrames; +} + +uint64 pmmFreeFrames(void) +{ + return frameCount - usedFrames; +} + +uint64 pmmTotalBytes(void) +{ + return totalMemoryBytes; +}