/** * This is extremely bad, a large amount of this code is dangerous and error prone, and if something goes wrong, it has no way to indicate that and roll back the error. * This needs to be fixed */ #include #include #include #include #include #include #include #include #define BUDDY_BLK_(_ORDER, _ADDR) ((_ADDR) >> (_ORDER)) /** * Root table for the memory allocation data, once we know where we want it, it won't need extra memory space. */ struct BuddyTable { /// Minimum size of a block log 2 u32 StartWidth; /// Maximum size of a block log 2 u32 EndWidth; /// Order tables struct BuddyOrder* Order[64]; }; /** * The order of a buddy block */ struct BuddyOrder { /// Number of blocks u64 Count; /// Length of Free u64 Length; /// Free blocks u8 Free[]; }; /** * Pointer to the buddy root table */ struct BuddyTable* PmemBuddyTable = 0; /** * Merge a block * * \todo Optimize */ void PmemBuddyBlkMerge(u32 MergeTo, u32 Ordr, u64 Address) { // Nothing to merge if (Ordr <= PmemBuddyTable->StartWidth) return; // Select blocks struct BuddyOrder* Order = PmemBuddyTable->Order[Ordr - 1]; u64 LBlock = BUDDY_BLK_(Ordr - 1, Address & ~(1ULL << (Ordr - 1))); u64 RBlock = BUDDY_BLK_(Ordr - 1, Address | (1ULL << (Ordr - 1))); // Mark as used Order->Free[LBlock >> 3] &= ~(1ULL << (LBlock & 7)); Order->Free[RBlock >> 3] &= ~(1ULL << (RBlock & 7)); // Merge lower blocks PmemBuddyBlkMerge(MergeTo, Ordr - 1, Address & ~(1ULL << (Ordr - 1))); PmemBuddyBlkMerge(MergeTo, Ordr - 1, Address | (1ULL << (Ordr - 1))); } /** * Split a block */ void PmemBuddyBlkSplit(u32 Ordr, u64 Address) { struct BuddyOrder* UOrder = PmemBuddyTable->Order[Ordr]; struct BuddyOrder* LOrder = PmemBuddyTable->Order[Ordr - 1]; u64 Block = BUDDY_BLK_(Ordr, Address); u64 LBlock = BUDDY_BLK_(Ordr - 1, Address & ~(1ULL << (Ordr - 1))); u64 RBlock = BUDDY_BLK_(Ordr - 1, Address | (1ULL << (Ordr - 1))); // Split UOrder->Free[Block >> 3] &= ~(1ULL << (Block & 7)); LOrder->Free[LBlock >> 3] |= 1ULL << (LBlock & 7); LOrder->Free[RBlock >> 3] |= 1ULL << (RBlock & 7); } /** * Mark a block as allocated * * \todo Optimize */ void PmemBuddyBlkMarkAlloc(u32 Ordr, u64 Address) { struct BuddyOrder* Order = PmemBuddyTable->Order[Ordr]; u64 Block = BUDDY_BLK_(Ordr, Address); // Mark in use Order->Free[Block >> 3] &= ~(1ULL << (Block & 7)); // Merge lower orders PmemBuddyBlkMerge(Ordr, Ordr, Address); } /** * Mark a block as free * * \todo Optimize */ void PmemBuddyBlkMarkFree(u32 Ordr, u64 Address) { struct BuddyOrder* Order = PmemBuddyTable->Order[Ordr]; u64 Block = BUDDY_BLK_(Ordr, Address); // Mark free Order->Free[Block >> 3] |= 1ULL << (Block & 7); // Merge PmemBuddyBlkMerge(Ordr, Ordr, Address); } /** * Recursively split blocks until one is free * * \todo Optimize */ void PmemBuddyBlkSplitUp(u32 Ordr, u64 Address) { struct BuddyOrder* Order = PmemBuddyTable->Order[Ordr]; u64 Block = BUDDY_BLK_(Ordr, Address); // Check if this block is split if (!(Order->Free[Block >> 3] & (1ULL << (Block & 7)))) { if (Ordr < PmemBuddyTable->EndWidth) { PmemBuddyBlkSplitUp(Ordr + 1, Address & (((u64)-1) << Ordr)); } } // Hopefully we are now PmemBuddyBlkSplit(Ordr, Address); } /** * Allocates a block and splits parent blocks if necessary * * \todo Optimize */ void PmemBuddyBlkAlloc(u32 Ordr, u64 Address) { //struct BuddyOrder* Order = PmemBuddyTable->Order[Ordr]; //u64 Block = BUDDY_BLK_(Ordr, Address); // Split the block above us if (Ordr < PmemBuddyTable->EndWidth) { PmemBuddyBlkSplitUp(Ordr + 1, Address & (((u64)-1) << Ordr)); } PmemBuddyBlkMarkAlloc(Ordr, Address); } /** * Mark a range as allocated * * \todo Optimize */ void PmemBuddyMarkAllocRange(void* From, void* To) { u64 OwningOrder = 0; for (OwningOrder = PmemBuddyTable->StartWidth; OwningOrder < PmemBuddyTable->EndWidth; OwningOrder++) { if ((1ULL << OwningOrder) >= (uptr)(To - From)) break; } // Select a "pivot" block to merge relative to void* CentralBlockStartAddress = (void*)((uptr)From & (-1ULL << OwningOrder)); void* CentralBlockMidAddress = (void*)((uptr)From | (1ULL << (OwningOrder - 1))); void* CentralBlockEndAddress = (void*)((uptr)CentralBlockStartAddress + (1ULL << OwningOrder)) - 1; void* BigAreaBlockEndAddress = (void*)((uptr)To & (-1ULL << OwningOrder)); // It's merging time if (From <= CentralBlockStartAddress && To >= CentralBlockEndAddress) { PmemBuddyBlkAlloc(OwningOrder, (u64)CentralBlockStartAddress); // The rest of the for (void* Block = CentralBlockEndAddress + 1; Block < BigAreaBlockEndAddress; Block += 1ULL << OwningOrder) { PmemBuddyBlkAlloc(OwningOrder, (u64)Block); } // Other blocks we need to split if (BigAreaBlockEndAddress > CentralBlockEndAddress && To > BigAreaBlockEndAddress) { PmemBuddyMarkAllocRange(BigAreaBlockEndAddress, To); } } else { // Looks like a split if (From < CentralBlockMidAddress && To >= CentralBlockMidAddress) { PmemBuddyMarkAllocRange(From, CentralBlockMidAddress - 1); PmemBuddyMarkAllocRange(CentralBlockMidAddress, To); } else if (From >= CentralBlockMidAddress) { PmemBuddyMarkAllocRange(From, CentralBlockEndAddress); PmemBuddyMarkAllocRange(CentralBlockEndAddress + 1, To); } } } int PmemInitialize() { // Allocate a sizable chunk of stack memory for scanning, we lack // a heap so we need to be creative with how we store this. void* BeginAddresses[0x80]; void* EndAddresses[0x80]; u32 NextAddress = 0; /* // Find our memory address if (EfiPresent()) { // Scan the EFI memory map for usable memory struct EfiMemoryDescription *Entry; int Index; EFI_MEMORY_MAP_FOREACH_(Index, Entry) { if (Entry->Type == EFI_MEMORY_TYPE_CONVENTIONAL_MEMORY) { BeginAddresses[NextAddress] = Entry->PhysicalStart; EndAddresses[NextAddress] = Entry->NumberOfPages * 0x1000 + Entry->PhysicalStart; NextAddress += 1; } } } else while (true) {} // die */ // Scan BIOS e820 table provided by inferior for (int i = 0; i < BootBootParams->e820_entries; i++) { if (BootBootParams->e820_table[i].type == 1) { BeginAddresses[NextAddress] = (void*)BootBootParams->e820_table[i].base; EndAddresses[NextAddress] = (void*)BootBootParams->e820_table[i].base + BootBootParams->e820_table[i].length; NextAddress += 1; } } // Mark kernel memory as reserved const c8* CoreHeaderParam; if ((CoreHeaderParam = CmdlineGet("elfcorehdr"))) { /// TODO: Should this be its own function? if (CoreHeaderParam[0] == '0' && CoreHeaderParam[1] == 'x') { uptr CoreHeaderAddr = 0; for (int i = 2; CoreHeaderParam[i] != 0; i++) { c8 c = CoreHeaderParam[i]; CoreHeaderAddr *= 16; if (c >= '0' && c <= '9') { CoreHeaderAddr += c - '0'; } else if (c >= 'a' && c <= 'f') { CoreHeaderAddr += c - 'a'; } else if (c >= 'A' && c <= 'F') { CoreHeaderAddr += c - 'A'; } else break; } // TODO: 32-bit machines? struct Elf64_Ehdr* CoreHeader = (struct Elf64_Ehdr*)CoreHeaderAddr; // We don't check for the ELF signature or version, // if the kernel doesn't give us anything valid, // we're fucked anyways if (CoreHeader->e_type != ET_CORE) goto sort; // Scan core dump for kernel memory void* CoreSegmentLocation = (void*)CoreHeader + CoreHeader->e_phoff; for (int i = 0; i < CoreHeader->e_phnum; i++) { struct Elf64_Phdr* Segment = (struct Elf64_Phdr*)CoreSegmentLocation; // Only take LOAD areas if (Segment->p_type == PT_LOAD) { void* BeginAddr = (void*)Segment->p_paddr; void* EndAddr = (void*)Segment->p_paddr + Segment->p_memsz; long EndSplitScan = NextAddress; for (int j = 0; j < EndSplitScan; j++) { if (BeginAddr > BeginAddresses[j] && BeginAddr < EndAddresses[j]) { // Begins in the middle of a block if (EndAddr < EndAddresses[j]) { // Create new address block BeginAddresses[NextAddress] = EndAddr; EndAddresses[NextAddress] = EndAddresses[j]; // Decrease size of old address block EndAddresses[j] = BeginAddr - 1; // Grow NextAddress += 1; } else { // Shrink block EndAddresses[j] = BeginAddr - 1; } } else if (BeginAddr == BeginAddresses[j]) { // Begins at the start of a block if (EndAddr < EndAddresses[j]) { // Shrink EndAddresses[j] = BeginAddr - 1; } else goto delblk; } else if (BeginAddr < BeginAddresses[j] && EndAddr >= EndAddresses[j]) { delblk: for (int k = 0; k < NextAddress - 1; k++) { BeginAddresses[k] = BeginAddresses[k + 1]; EndAddresses[k] = EndAddresses[k + 1]; } NextAddress -= 1; EndSplitScan -= 1; } } } CoreSegmentLocation += CoreHeader->e_phentsize; } } } // TODO: Please replace this. // Sort address ranges with lazily tossed together slow piece of crap sort: while (true) { bool KeepGoing = false; for (int i = 0; i < NextAddress - 1; i++) { if (BeginAddresses[i] > BeginAddresses[i + 1]) { void* Temp = BeginAddresses[i]; BeginAddresses[i] = BeginAddresses[i + 1]; BeginAddresses[i + 1] = Temp; Temp = EndAddresses[i]; EndAddresses[i] = EndAddresses[i + 1]; EndAddresses[i + 1] = Temp; KeepGoing = true; } } if (!KeepGoing) break; }; // Set up our physical memory table u64 MemorySize = (u64)EndAddresses[NextAddress - 1]; u32 BuddyStartWidth = 12; u32 BuddyEndWidth = 20; u64 BuddyRange = MemorySize + MemorySize % (1ULL << BuddyEndWidth); u32 BuddyTableSize = 0; // Calculate table size BuddyTableSize += sizeof(struct BuddyTable); BuddyTableSize += BuddyTableSize % 8; for (u32 Width = BuddyStartWidth; Width <= BuddyEndWidth; Width++) { u64 BlockSize = 1ULL << Width; u64 BlockCount = BuddyRange / BlockSize; BlockCount += BlockCount % 8; BlockCount /= 8; BlockCount += BlockCount % 8; BlockCount += sizeof(struct BuddyOrder); BlockCount += BlockCount % 8; BuddyTableSize += BlockCount; } // Find a safe area for the allocation tables for (int i = 0; i < NextAddress; i++) { if (EndAddresses[i] - BeginAddresses[i] >= BuddyTableSize) { PmemBuddyTable = (struct BuddyTable*)BeginAddresses[i]; break; } } // Uh oh. TODO: We should probably panic here when we figure out how to do that. if (PmemBuddyTable == 0) while (true) {} // Fill out the table PmemBuddyTable->StartWidth = BuddyStartWidth; PmemBuddyTable->EndWidth = BuddyEndWidth; for (int i = 0; i < 64; i++) PmemBuddyTable->Order[i] = 0; // Build our order tables void* NextOrderLocation = (void*)PmemBuddyTable + sizeof(struct BuddyTable); NextOrderLocation += (uptr)NextOrderLocation % 8; for (int i = BuddyStartWidth; i <= BuddyEndWidth; i++) { struct BuddyOrder* Order = (struct BuddyOrder*)NextOrderLocation; // I sure hope this gets optimized into a rep Order->Count = BuddyRange / (1ULL << i); Order->Length = Order->Count / 8; Order->Length = Order->Length ? Order->Length : 1; if (i == BuddyEndWidth) { for (int j = 0; j < Order->Length; j++) Order->Free[j] = 255; } else { for (int j = 0; j < Order->Length; j++) Order->Free[j] = 0; } // Save it PmemBuddyTable->Order[i] = Order; // Calculate next order address NextOrderLocation += sizeof(struct BuddyOrder); NextOrderLocation += Order->Length; NextOrderLocation += (uptr)NextOrderLocation % 8; } // Mark anything we can't touch as used for (int i = 0; i < NextAddress; i++) { if (i == 0) { PmemBuddyMarkAllocRange(0, BeginAddresses[i] - 1); } if (i + 1 == NextAddress) { PmemBuddyMarkAllocRange(EndAddresses[i], (void*)((1ULL << BuddyEndWidth) * PmemBuddyTable->Order[BuddyEndWidth]->Count) - 1); } else { PmemBuddyMarkAllocRange(EndAddresses[i], BeginAddresses[i + 1] - 1); } } return 0; } /** * Find a free block of the specified order * * \return Returns 0 on success */ int PmemBuddyFindBlock(u32 Ordr, u64* Result) { struct BuddyOrder* Order = PmemBuddyTable->Order[Ordr]; // Find our block for (uptr i = 0; i < Order->Length; i++) { if (Order->Free[i]) { for (int sub = 0; sub < 8; sub++) { if (Order->Free[i] & (1 << sub)) { // We got it *Result = (i << 3) | sub; return 0; } } } } *Result = 0; return -1; } [[gnu::nonnull(2)]] int PmemAllocateBlock(uptr RequiredSize, struct PmemAllocation* Allocated) { // Don't function if the PMM subsystem isn't ready if (PmemBuddyTable == 0) return -1; // We can't make massive allocations if (RequiredSize > (1 << PmemBuddyTable->EndWidth)) return -1; // Calculate how big we need our stuff to be u32 Order = PmemBuddyTable->StartWidth; for (; RequiredSize >= (1 << Order) && Order < PmemBuddyTable->EndWidth; Order++); // Find a block u64 Block = 0; if (PmemBuddyFindBlock(Order, &Block)) { // We need to split a block to get ours bool Found = false; u32 OrderAttempt = Order + 1; // Find (a) block(s) to split u64 ToSplit = 0; for (; OrderAttempt <= PmemBuddyTable->EndWidth; OrderAttempt++) { if (!PmemBuddyFindBlock(OrderAttempt, &ToSplit)) { Found = true; break; } } // Out of memory if (!Found) return -1; // Split for (; OrderAttempt > Order; OrderAttempt--) { PmemBuddyBlkSplit(OrderAttempt, ToSplit << OrderAttempt); ToSplit <<= 1; } // Here's our block Block = ToSplit; } // TODO: This isn't optimal, space is wasted, fix this as soon as possible PmemBuddyBlkMarkAlloc(Order, Block << Order); // Return our crap Allocated->Address = (void*)(Block << Order); Allocated->AllocationOrder = Order; Allocated->AllocationBlock = Block; return 0; } [[gnu::nonnull(1)]] int PmemFreeBlock(struct PmemAllocation* Block) { // Don't function if the PMM subsystem isn't ready if (PmemBuddyTable == 0) return -1; // Deallocate the block PmemBuddyBlkMarkFree(Block->AllocationOrder, Block->AllocationBlock << Block->AllocationOrder); return 0; }