lindows/lcrash/mm/kmalloc.c
2024-04-22 07:58:33 -04:00

283 lines
8.0 KiB
C

#include <lcrash/mm/kmalloc.h>
#include <lcrash/mm/phys.h>
/**
* A block in the kernel heap. the performance is abysmal, this structure may be larger
* than a lot of kernel objects so we'll have about half the memory we could have ^-^
*/
struct KernelHeapBlock {
/// Next block
struct KernelHeapBlock* Next;
/// Previous block
struct KernelHeapBlock* Prev;
/// Next free block
struct KernelHeapBlock* NextFree;
/// Previous free block
struct KernelHeapBlock* PrevFree;
/// Block size
u64 Size;
/// Block is free
bool Free;
};
/**
* A structure that describes a specific kernel heap, yes, we can have more than one and,
* yes, most will do the same thing
*/
struct KernelHeapZone {
/// Next heap zone
struct KernelHeapZone* Next;
/// Previous heap zone
struct KernelHeapZone* Prev;
/// First heap frame
struct KernelHeapFrame* FirstFrame;
/// Last heap frame
struct KernelHeapFrame* LastFrame;
/// First block
struct KernelHeapBlock* First;
/// Last block
struct KernelHeapBlock* Last;
/// First free block
struct KernelHeapBlock* FirstFree;
/// Last free block
struct KernelHeapBlock* LastFree;
};
/**
* A block of physical memory owned by a specific heap, where is it stored exactly? In
* the heap of course!
*/
struct KernelHeapFrame {
/// The block
struct PmemAllocation Block;
/// The block's size
u64 Size;
/// Linked list jumpscare
struct KernelHeapFrame* NextFrame;
/// I wonder if this is a pointer that points to the previous frame in a linked
/// list of the structure which stores kernel heap frames
struct KernelHeapFrame* PrevFrame;
};
struct KernelHeapZone* KernelHeapFirst = 0;
struct KernelHeapZone* KernelHeapLast = 0;
/**
* \todo Maybe split the zone allocation code out of the thing
*/
int KernelHeapInitialize() {
// Allocate uh this amount of memory for the heap
struct PmemAllocation Block;
if (PmemAllocateBlock(0x100000, &Block)) return -1;
// Make our zone
struct KernelHeapZone* Zone = Block.Address;
Zone->Next = 0;
Zone->Prev = 0;
KernelHeapFirst = Zone;
KernelHeapLast = Zone;
// Make the frame block
struct KernelHeapBlock* FrameBlock = Block.Address + sizeof(struct KernelHeapZone);
FrameBlock->Prev = 0;
FrameBlock->PrevFree = 0;
FrameBlock->Size = sizeof(struct KernelHeapFrame);
FrameBlock->Free = false;
// Make the frame structure
struct KernelHeapFrame* Frame = Block.Address
+ sizeof(struct KernelHeapZone)
+ sizeof(struct KernelHeapBlock);
Frame->Block = Block;
Frame->Size = 0x100000;
Frame->NextFrame = 0;
Frame->PrevFrame = 0;
// Make the root block
struct KernelHeapBlock* RootBlock = (struct KernelHeapBlock*)((void*)Frame + sizeof(struct KernelHeapFrame));
RootBlock->Prev = FrameBlock;
FrameBlock->Next = RootBlock;
FrameBlock->NextFree = RootBlock;
RootBlock->Next = 0;
RootBlock->PrevFree = 0;
RootBlock->NextFree = 0;
RootBlock->Free = true;
RootBlock->Size = 0x100000
- sizeof(struct KernelHeapZone)
- sizeof(struct KernelHeapBlock)
- sizeof(struct KernelHeapFrame)
- sizeof(struct KernelHeapBlock);
// Fill out the rest of the zone
Zone->FirstFrame = Frame;
Zone->LastFrame = Frame;
Zone->FirstFree = RootBlock;
Zone->LastFree = RootBlock;
Zone->First = FrameBlock;
Zone->Last = RootBlock;
// Tada!
return 0;
}
/// Shamelessly copy pasted from function above
int KernelHeapGrow(struct KernelHeapZone* Zone, u32 Level, u32 Flags) {
// Allocate
struct PmemAllocation Block;
if (PmemAllocateBlock(1 << Level, &Block)) return -1;
// Make the frame block
struct KernelHeapBlock* FrameBlock = Block.Address;
FrameBlock->Prev = Zone->Last;
FrameBlock->PrevFree = Zone->LastFree;
FrameBlock->Size = sizeof(struct KernelHeapFrame);
FrameBlock->Free = false;
// Make the frame structure
struct KernelHeapFrame* Frame = Block.Address
+ sizeof(struct KernelHeapBlock);
Frame->Block = Block;
Frame->Size = 1 << Level;
Frame->NextFrame = 0;
Frame->PrevFrame = Zone->LastFrame;
// Make the root block
struct KernelHeapBlock* RootBlock = (struct KernelHeapBlock*)((void*)Frame + sizeof(struct KernelHeapFrame));
RootBlock->Prev = FrameBlock;
FrameBlock->Next = RootBlock;
FrameBlock->NextFree = RootBlock;
RootBlock->Next = 0;
RootBlock->PrevFree = Zone->LastFree;
RootBlock->NextFree = 0;
RootBlock->Free = true;
RootBlock->Size = (1 << Level)
- sizeof(struct KernelHeapBlock)
- sizeof(struct KernelHeapFrame)
- sizeof(struct KernelHeapBlock);
// Fill out the rest of the zone
Zone->LastFrame->NextFrame = Frame;
Zone->LastFrame = Frame;
Zone->Last->Next = FrameBlock;
for (struct KernelHeapBlock* Block = Zone->Last; Block != 0 && Block->NextFree == 0; Block = Block->Prev) Block->NextFree = RootBlock;
Zone->Last = RootBlock;
return 0;
}
void* KernelHeapAllocInternal(u32 Size, u32 Align, u32 Flags, u32 Iteration) {
u64 AlignMask = 0;
for (u32 i = 1; i < Align; i <<= 1) AlignMask |= i;
AlignMask = ~AlignMask;
// Find a free block
for (struct KernelHeapZone* Zone = KernelHeapFirst; Zone != 0; Zone = Zone->Next) {
for (struct KernelHeapBlock* Block = Zone->FirstFree; Block != 0; Block = Block->NextFree) {
if (Block->Size >= Size) {
u64 PaddingSize = 0;
if (Align > 1) {
// Align uses memory poorly
if (Block->Prev == 0) continue;
u64 BlockStart = (uptr)Block + sizeof(struct KernelHeapBlock);
u64 BlockEnd = BlockStart + Block->Size;
if (!(BlockStart < ((BlockStart + Align) & AlignMask) && BlockEnd > ((BlockStart + Align) & AlignMask))) continue;
PaddingSize = ((BlockStart + Align) & AlignMask) - BlockStart;
}
if (PaddingSize > 0) {
struct KernelHeapBlock* NewBlock = (struct KernelHeapBlock*)((void*)Block + PaddingSize);
NewBlock->Prev = Block->Prev;
NewBlock->Next = Block->Next;
NewBlock->PrevFree = Block->PrevFree;
NewBlock->NextFree = Block->NextFree;
NewBlock->Free = Block->Free;
NewBlock->Size = Block->Size - PaddingSize;
// This is incredibly slow
struct KernelHeapBlock* Next = Zone->First;
while (Next != 0) {
struct KernelHeapBlock* NextNext = Next->Next;
if (Next->Prev == Block) Next->Prev = NewBlock;
if (Next->Next == Block) Next->Next = NewBlock;
if (Next->PrevFree == Block) Next->PrevFree = NewBlock;
if (Next->NextFree == Block) Next->NextFree = NewBlock;
Next = NextNext;
}
// GOD.
if (NewBlock->Prev != 0) NewBlock->Prev->Size += (uptr)Block + PaddingSize;
Block = NewBlock;
}
s64 SplitSize = (s64)Block->Size - Size - (sizeof(struct KernelHeapBlock) * 2) - 8;
if (SplitSize > 0) {
struct KernelHeapBlock* NewBlock = (struct KernelHeapBlock*)((void*)Block + sizeof(struct KernelHeapBlock) + Size);
NewBlock->Prev = Block;
NewBlock->Next = 0;
NewBlock->PrevFree = Block->PrevFree;
NewBlock->NextFree = Block->NextFree;
NewBlock->Free = true;
NewBlock->Size = (u64)SplitSize;
// Set zone stuff
if (Block->NextFree == 0) Zone->LastFree = NewBlock;
if (Block->PrevFree == 0) Zone->FirstFree = NewBlock;
if (Block->Next == 0) Zone->Last = NewBlock;
// Set next free in other blocks
for (struct KernelHeapBlock* ModBlock = Block->Prev; ModBlock != 0 && (Block->NextFree != 0 || Block->NextFree != Block); ModBlock = ModBlock->Prev) {
ModBlock->NextFree = NewBlock;
}
// Set prev free in other blocks
for (struct KernelHeapBlock* ModBlock = Block->Next; ModBlock != 0 && (Block->PrevFree != 0 || Block->PrevFree != Block); ModBlock = ModBlock->Next) {
ModBlock->PrevFree = NewBlock;
}
// Block stuff
Block->Next = NewBlock;
}
Block->Free = 0;
Block->Size = Size;
return (void*)Block + sizeof(struct KernelHeapBlock);
}
}
}
if (Iteration < 3) {
KernelHeapGrow(KernelHeapLast, 20, 0);
return KernelHeapAllocInternal(Size, Align, Flags, Iteration + 1);
} else return 0;
}
void* KernelHeapAlloc(u32 Size, u32 Align, u32 Flags) {
return KernelHeapAllocInternal(Size, Align, Flags, 0);
}
void KernelHeapFree(void* Address) {
}