From 4519204580587a2651277e4a3769a1490bebe51a Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 24 Feb 2023 10:19:55 +0200 Subject: [PATCH] Reimplemented Xalloc from scratch --- Core/Memory/HeapAllocators/Xalloc.cpp | 285 ------------------ Core/Memory/HeapAllocators/Xalloc.hpp | 180 ----------- Core/Memory/HeapAllocators/Xalloc/README.md | 137 +++++++++ Core/Memory/HeapAllocators/Xalloc/Wrapper.cpp | 23 ++ Core/Memory/HeapAllocators/Xalloc/Xalloc.hpp | 101 +++++++ .../Memory/HeapAllocators/Xalloc/XallocV1.cpp | 263 ++++++++++++++++ Core/Memory/Memory.cpp | 39 +-- KThread.cpp | 7 +- Kernel.cpp | 1 + Tests/MemoryAllocation.cpp | 224 ++++++++++++++ Tests/t.h | 1 + 11 files changed, 760 insertions(+), 501 deletions(-) delete mode 100644 Core/Memory/HeapAllocators/Xalloc.cpp delete mode 100644 Core/Memory/HeapAllocators/Xalloc.hpp create mode 100644 Core/Memory/HeapAllocators/Xalloc/README.md create mode 100644 Core/Memory/HeapAllocators/Xalloc/Wrapper.cpp create mode 100644 Core/Memory/HeapAllocators/Xalloc/Xalloc.hpp create mode 100644 Core/Memory/HeapAllocators/Xalloc/XallocV1.cpp create mode 100644 Tests/MemoryAllocation.cpp diff --git a/Core/Memory/HeapAllocators/Xalloc.cpp b/Core/Memory/HeapAllocators/Xalloc.cpp deleted file mode 100644 index 29eb0a2..0000000 --- a/Core/Memory/HeapAllocators/Xalloc.cpp +++ /dev/null @@ -1,285 +0,0 @@ -#include "Xalloc.hpp" - -namespace Xalloc -{ - class XLockClass - { - struct SpinLockData - { - uintptr_t LockData = 0x0; - const char *CurrentHolder = "(null)"; - const char *AttemptingToGet = "(null)"; - size_t Count = 0; - }; - - void DeadLock(SpinLockData Lock) - { - Xalloc_warn("Potential deadlock in lock '%s' held by '%s'! %ld locks in queue.", Lock.AttemptingToGet, Lock.CurrentHolder, Lock.Count); - } - - private: - SpinLockData LockData; - bool IsLocked = false; - - public: - int Lock(const char *FunctionName) - { - LockData.AttemptingToGet = FunctionName; - Retry: - unsigned int i = 0; - while (__atomic_exchange_n(&IsLocked, true, __ATOMIC_ACQUIRE) && ++i < 0x10000000) - ; - if (i >= 0x10000000) - { - DeadLock(LockData); - goto Retry; - } - LockData.Count++; - LockData.CurrentHolder = FunctionName; - __sync_synchronize(); - return 0; - } - - int Unlock() - { - __sync_synchronize(); - __atomic_store_n(&IsLocked, false, __ATOMIC_RELEASE); - LockData.Count--; - IsLocked = false; - return 0; - } - }; - - class XSmartLock - { - private: - XLockClass *LockPointer = nullptr; - - public: - XSmartLock(XLockClass &Lock, const char *FunctionName) - { - this->LockPointer = &Lock; - this->LockPointer->Lock(FunctionName); - } - ~XSmartLock() { this->LockPointer->Unlock(); } - }; - - XLockClass XLock; - -#define XSL XSmartLock CONCAT(lock##_, __COUNTER__)(XLock, __FUNCTION__) - - class SmartSMAPClass - { - private: - AllocatorV1 *allocator = nullptr; - - public: - SmartSMAPClass(AllocatorV1 *allocator) - { - this->allocator = allocator; - this->allocator->Xstac(); - } - ~SmartSMAPClass() { this->allocator->Xclac(); } - }; -#define SmartSMAP SmartSMAPClass XALLOC_CONCAT(SmartSMAP##_, __COUNTER__)(this) - - AllocatorV1::AllocatorV1(void *Address, bool UserMode, bool SMAPEnabled) - { - SmartSMAP; - XSL; - void *Position = Address; - UserMapping = UserMode; - SMAPUsed = SMAPEnabled; - for (Xuint64_t i = 0; i < 0x20; i++) - { - void *Page = Xalloc_REQUEST_PAGE(); - if (UserMapping) - Xalloc_MAP_MEMORY(Position, Page, Xalloc_MAP_MEMORY_READ_WRITE | Xalloc_MAP_MEMORY_USER); - else - Xalloc_MAP_MEMORY(Position, Page, Xalloc_MAP_MEMORY_READ_WRITE); - Xalloc_trace("Preallocate Heap Memory (%#llx-%#llx [%#llx])...", Position, (Xuint64_t)Position + Xalloc_PAGE_SIZE, Page); - Position = (void *)((Xuint64_t)Position + Xalloc_PAGE_SIZE); - } - Xuint64_t HeapLength = 16 * Xalloc_PAGE_SIZE; - this->HeapStart = Address; - this->HeapEnd = (void *)((Xuint64_t)this->HeapStart + HeapLength); - HeapSegment *StartSegment = (HeapSegment *)Address; - StartSegment->Length = HeapLength - sizeof(HeapSegment); - StartSegment->Next = nullptr; - StartSegment->Last = nullptr; - StartSegment->IsFree = true; - this->LastSegment = StartSegment; - } - - AllocatorV1::~AllocatorV1() - { - SmartSMAP; - XSL; - Xalloc_trace("Destructor not implemented yet."); - } - - void AllocatorV1::ExpandHeap(Xuint64_t Length) - { - if (Length % Xalloc_PAGE_SIZE) - { - Length -= Length % Xalloc_PAGE_SIZE; - Length += Xalloc_PAGE_SIZE; - } - Xuint64_t PageCount = Length / Xalloc_PAGE_SIZE; - HeapSegment *NewSegment = (HeapSegment *)this->HeapEnd; - for (Xuint64_t i = 0; i < PageCount; i++) - { - void *Page = Xalloc_REQUEST_PAGE(); - if (UserMapping) - Xalloc_MAP_MEMORY(this->HeapEnd, Page, Xalloc_MAP_MEMORY_READ_WRITE | Xalloc_MAP_MEMORY_USER); - else - Xalloc_MAP_MEMORY(this->HeapEnd, Page, Xalloc_MAP_MEMORY_READ_WRITE); - // Xalloc_trace("Expanding Heap Memory (%#llx-%#llx [%#llx])...", this->HeapEnd, (Xuint64_t)this->HeapEnd + Xalloc_PAGE_SIZE, Page); - this->HeapEnd = (void *)((Xuint64_t)this->HeapEnd + Xalloc_PAGE_SIZE); - } - NewSegment->IsFree = true; - NewSegment->Last = this->LastSegment; - this->LastSegment->Next = NewSegment; - this->LastSegment = NewSegment; - NewSegment->Next = nullptr; - NewSegment->Length = Length - sizeof(HeapSegment); - NewSegment->CombineBackward(this->LastSegment); - } - - void *AllocatorV1::Malloc(Xuint64_t Size) - { - SmartSMAP; - XSL; - if (this->HeapStart == nullptr) - { - Xalloc_err("Memory allocation not initialized yet!"); - return 0; - } - - if (Size < 0x10) - { - // Xalloc_warn("Allocation size is too small, using 0x10 instead!"); - Size = 0x10; - } - - // #ifdef DEBUG - // if (Size < 1024) - // debug("Allocating %dB", Size); - // else if (TO_KB(Size) < 1024) - // debug("Allocating %dKB", TO_KB(Size)); - // else if (TO_MB(Size) < 1024) - // debug("Allocating %dMB", TO_MB(Size)); - // else if (TO_GB(Size) < 1024) - // debug("Allocating %dGB", TO_GB(Size)); - // #endif - - if (Size % 0x10 > 0) // it is not a multiple of 0x10 - { - Size -= (Size % 0x10); - Size += 0x10; - } - if (Size == 0) - { - return nullptr; - } - - HeapSegment *CurrentSegment = (HeapSegment *)this->HeapStart; - while (true) - { - if (CurrentSegment->IsFree) - { - if (CurrentSegment->Length > Size) - { - CurrentSegment->Split(Size, this->LastSegment); - CurrentSegment->IsFree = false; - return (void *)((Xuint64_t)CurrentSegment + sizeof(HeapSegment)); - } - if (CurrentSegment->Length == Size) - { - CurrentSegment->IsFree = false; - return (void *)((Xuint64_t)CurrentSegment + sizeof(HeapSegment)); - } - } - if (CurrentSegment->Next == nullptr) - break; - CurrentSegment = CurrentSegment->Next; - } - ExpandHeap(Size); - XLock.Unlock(); - return this->Malloc(Size); - } - - void AllocatorV1::Free(void *Address) - { - SmartSMAP; - XSL; - if (this->HeapStart == nullptr) - { - Xalloc_err("Memory allocation not initialized yet!"); - return; - } - HeapSegment *Segment = (HeapSegment *)Address - 1; - Segment->IsFree = true; - Segment->CombineForward(this->LastSegment); - Segment->CombineBackward(this->LastSegment); - } - - void *AllocatorV1::Calloc(Xuint64_t NumberOfBlocks, Xuint64_t Size) - { - SmartSMAP; - XSL; - if (this->HeapStart == nullptr) - { - Xalloc_err("Memory allocation not initialized yet!"); - return 0; - } - - if (Size < 0x10) - { - // Xalloc_warn("Allocation size is too small, using 0x10 instead!"); - Size = 0x10; - } - - XLock.Unlock(); - void *Block = this->Malloc(NumberOfBlocks * Size); - XLock.Lock(__FUNCTION__); - - if (Block) - Xmemset(Block, 0, NumberOfBlocks * Size); - return Block; - } - - void *AllocatorV1::Realloc(void *Address, Xuint64_t Size) - { - SmartSMAP; - XSL; - if (this->HeapStart == nullptr) - { - Xalloc_err("Memory allocation not initialized yet!"); - return 0; - } - if (!Address && Size == 0) - { - XLock.Unlock(); - this->Free(Address); - return nullptr; - } - else if (!Address) - { - XLock.Unlock(); - return this->Calloc(Size, sizeof(char)); - } - - if (Size < 0x10) - { - // Xalloc_warn("Allocation size is too small, using 0x10 instead!"); - Size = 0x10; - } - - XLock.Unlock(); - void *newAddress = this->Calloc(Size, sizeof(char)); - XLock.Lock(__FUNCTION__); - Xmemcpy(newAddress, Address, Size); - return newAddress; - } -} diff --git a/Core/Memory/HeapAllocators/Xalloc.hpp b/Core/Memory/HeapAllocators/Xalloc.hpp deleted file mode 100644 index fbc3a53..0000000 --- a/Core/Memory/HeapAllocators/Xalloc.hpp +++ /dev/null @@ -1,180 +0,0 @@ -#pragma once -#include -#include - -// Functions defines - -// Page allocation functions -#define Xalloc_REQUEST_PAGE() KernelAllocator.RequestPage() -#define Xalloc_REQUEST_PAGES(Pages) KernelAllocator.RequestPages(Pages) -#define Xalloc_FREE_PAGE(Address) KernelAllocator.FreePage(Address) -#define Xalloc_FREE_PAGES(Address, Pages) KernelAllocator.FreePages(Address, Pages) - -#define Xalloc_MAP_MEMORY(VirtualAddress, PhysicalAddress, Flags) Memory::Virtual(KernelPageTable).Map(VirtualAddress, PhysicalAddress, Flags) -#define Xalloc_UNMAP_MEMORY(VirtualAddress) Memory::Virtual(KernelPageTable).Unmap(VirtualAddress) -#define Xalloc_MAP_MEMORY_READ_WRITE Memory::PTFlag::RW -#define Xalloc_MAP_MEMORY_USER Memory::PTFlag::US - -#define Xalloc_PAGE_SIZE PAGE_SIZE - -#define Xalloc_trace(m, ...) trace(m, ##__VA_ARGS__) -#define Xalloc_warn(m, ...) warn(m, ##__VA_ARGS__) -#define Xalloc_err(m, ...) error(m, ##__VA_ARGS__) - -#define XALLOC_CONCAT(x, y) x##y - -typedef long unsigned Xuint64_t; - -namespace Xalloc -{ - class AllocatorV1 - { - private: - struct HeapSegment - { - Xuint64_t Length; - HeapSegment *Next; - HeapSegment *Last; - bool IsFree; - - HeapSegment *Split(Xuint64_t SplitLength, HeapSegment *LastSegment) - { - if (SplitLength < 0x10) - return nullptr; - int64_t SplitSegmentLength = Length - SplitLength - (sizeof(HeapSegment)); - if (SplitSegmentLength < 0x10) - return nullptr; - HeapSegment *NewSplitHdr = (HeapSegment *)((Xuint64_t)this + SplitLength + sizeof(HeapSegment)); - Next->Last = NewSplitHdr; - NewSplitHdr->Next = Next; - Next = NewSplitHdr; - NewSplitHdr->Last = this; - NewSplitHdr->Length = SplitSegmentLength; - NewSplitHdr->IsFree = IsFree; - Length = SplitLength; - if (LastSegment == this) - LastSegment = NewSplitHdr; - return NewSplitHdr; - } - - void CombineForward(HeapSegment *LastSegment) - { - if (Next == nullptr) - return; - if (Next->IsFree == false) - return; - if (Next == LastSegment) - LastSegment = this; - if (Next->Next != nullptr) - Next->Next->Last = this; - - Length = Length + Next->Length + sizeof(HeapSegment); - Next = Next->Next; - } - - void CombineBackward(HeapSegment *LastSegment) - { - if (Last != nullptr && Last->IsFree) - Last->CombineForward(LastSegment); - } - } __attribute__((aligned(16))); - - void *HeapStart = nullptr; - void *HeapEnd = nullptr; - HeapSegment *LastSegment = nullptr; - bool UserMapping = false; - bool SMAPUsed = false; - - void ExpandHeap(Xuint64_t Length); - - // TODO: Change memcpy with an optimized version - static inline void *Xmemcpy(void *__restrict__ Destination, const void *__restrict__ Source, Xuint64_t Length) - { - unsigned char *dst = (unsigned char *)Destination; - const unsigned char *src = (const unsigned char *)Source; - for (Xuint64_t i = 0; i < Length; i++) - dst[i] = src[i]; - return Destination; - } - - // TODO: Change memset with an optimized version - static inline void *Xmemset(void *__restrict__ Destination, int Data, Xuint64_t Length) - { - unsigned char *Buffer = (unsigned char *)Destination; - for (Xuint64_t i = 0; i < Length; i++) - Buffer[i] = (unsigned char)Data; - return Destination; - } - - public: - inline void Xstac() - { - if (this->SMAPUsed) - { -#if defined(__amd64__) || defined(__i386__) - asm volatile("stac" :: - : "cc"); -#endif - } - } - - inline void Xclac() - { - if (this->SMAPUsed) - { -#if defined(__amd64__) || defined(__i386__) - asm volatile("clac" :: - : "cc"); -#endif - } - } - - /** - * @brief Construct a new Allocator V1 object - * - * @param Address Virtual address to allocate. - * @param UserMode Map the new pages with USER flag? - * @param SMAPEnabled Does the kernel has Supervisor Mode Access Prevention enabled? - */ - AllocatorV1(void *Address, bool UserMode, bool SMAPEnabled); - - /** - * @brief Destroy the Allocator V 1 object - * - */ - ~AllocatorV1(); - - /** - * @brief Allocate a new memory block - * - * @param Size Size of the block to allocate. - * @return void* Pointer to the allocated block. - */ - void *Malloc(Xuint64_t Size); - - /** - * @brief Free a previously allocated block - * - * @param Address Address of the block to free. - */ - void Free(void *Address); - - /** - * @brief Allocate a new memory block - * - * @param NumberOfBlocks Number of blocks to allocate. - * @param Size Size of the block to allocate. - * @return void* Pointer to the allocated block. - */ - void *Calloc(Xuint64_t NumberOfBlocks, Xuint64_t Size); - - /** - * @brief Reallocate a previously allocated block - * - * @param Address Address of the block to reallocate. - * @param Size New size of the block. - * @return void* Pointer to the reallocated block. - */ - void *Realloc(void *Address, Xuint64_t Size); - }; -} diff --git a/Core/Memory/HeapAllocators/Xalloc/README.md b/Core/Memory/HeapAllocators/Xalloc/README.md new file mode 100644 index 0000000..4c54e9c --- /dev/null +++ b/Core/Memory/HeapAllocators/Xalloc/README.md @@ -0,0 +1,137 @@ +# Xalloc + +Xalloc is a custom memory allocator designed for hobby operating systems. It is written in C++ and provides a simple and efficient way to manage memory in your hobby OS. + +#### ❗ This project is still in development and is not ready for use in production environments. ❗ + +--- + +## Features + +- **Simple API** - Xalloc provides a simple API for allocating and freeing memory. It is designed to be easy to use and understand. + +- [ ] todo complete this + +--- + +## Getting Started + +### Implementing missing functions + +You will need to implement the following functions in your OS: + +##### Wrapper.cpp +```cpp +extern "C" void *Xalloc_REQUEST_PAGES(Xsize_t Pages) +{ + // ... +} + +extern "C" void Xalloc_FREE_PAGES(void *Address, Xsize_t Pages) +{ + // ... +} + +extern "C" void Xalloc_MAP_MEMORY(void *VirtualAddress, void *PhysicalAddress, Xsize_t Flags) +{ + // ... +} + +extern "C" void Xalloc_UNMAP_MEMORY(void *VirtualAddress) +{ + // ... +} +``` + +##### Xalloc.hpp +```cpp +#define Xalloc_PAGE_SIZE /* <-- Replace with your page size */ +#define Xalloc_trace(m, ...) +#define Xalloc_warn(m, ...) +#define Xalloc_err(m, ...) +#define Xalloc_def /* eg. std::mutex Xalloc_lock; */ +#define Xalloc_lock +#define Xalloc_unlock +``` + +### Typical usage + +```cpp +#include "Xalloc.hpp" + +Xalloc::V1 *XallocV1Allocator = nullptr; + +int main() +{ + /* Virtual Base User SMAP */ + XallocV1Allocator = new Xalloc::V1((void *)0xFFFFA00000000000, false, false); + + void *p = XallocV1Allocator->malloc(1234); + /* ... */ + XallocV1Allocator->free(p); + delete XallocV1Allocator; + return 0; +} +``` + +or + +```cpp +#include "Xalloc.hpp" + +int main() +{ + /* Virtual Base User SMAP */ + Xalloc::V1 XallocV1Allocator((void *)0xFFFFA00000000000, false, false); + + void *p = XallocV1Allocator.malloc(1234); + /* ... */ + XallocV1Allocator.free(p); + return 0; +} +``` + +--- + +## API + +### Xalloc::V1 + +```cpp +void *malloc(Xsize_t Size); +``` +Allocates a block of memory of size `Size` bytes. +If `Size` is 0, then `nullptr` is returned. +- `Size` - The size of the block to allocate in bytes. + +

+ +```cpp +void free(void *Address); +``` +Frees the memory block pointed to by `Address`. +If `Address` is `nullptr`, then no operation is performed. +- `Address` - The address of the memory block to free. + +

+ +```cpp +void *calloc(Xsize_t NumberOfBlocks, Xsize_t Size); +``` +Allocates a block of memory for an array of `NumberOfBlocks` elements, each of them `Size` bytes long. +If `NumberOfBlocks` or `Size` is 0, then `nullptr` is returned. +- `NumberOfBlocks` - The number of elements to allocate. +- `Size` - The size of each element in bytes. + +

+ +```cpp +void *realloc(void *Address, Xsize_t Size); +``` +Changes the size of the memory block pointed to by `Address` to `Size` bytes. +If `Address` is `nullptr`, then the call is equivalent to `malloc(Size)`. +If `Size` is equal to zero, and `Address` is not `nullptr`, then the call is equivalent to `free(Address)`. +- `Address` - The address of the memory block to resize. +- `Size` - The new size of the memory block in bytes. + +--- diff --git a/Core/Memory/HeapAllocators/Xalloc/Wrapper.cpp b/Core/Memory/HeapAllocators/Xalloc/Wrapper.cpp new file mode 100644 index 0000000..a9321b1 --- /dev/null +++ b/Core/Memory/HeapAllocators/Xalloc/Wrapper.cpp @@ -0,0 +1,23 @@ +#include "Xalloc.hpp" + +#include + +extern "C" void *Xalloc_REQUEST_PAGES(Xsize_t Pages) +{ + return KernelAllocator.RequestPages(Pages); +} + +extern "C" void Xalloc_FREE_PAGES(void *Address, Xsize_t Pages) +{ + KernelAllocator.FreePages(Address, Pages); +} + +extern "C" void Xalloc_MAP_MEMORY(void *VirtualAddress, void *PhysicalAddress, Xsize_t Flags) +{ + Memory::Virtual(KernelPageTable).Map(VirtualAddress, PhysicalAddress, Flags); +} + +extern "C" void Xalloc_UNMAP_MEMORY(void *VirtualAddress) +{ + Memory::Virtual(KernelPageTable).Unmap(VirtualAddress); +} diff --git a/Core/Memory/HeapAllocators/Xalloc/Xalloc.hpp b/Core/Memory/HeapAllocators/Xalloc/Xalloc.hpp new file mode 100644 index 0000000..9a484f8 --- /dev/null +++ b/Core/Memory/HeapAllocators/Xalloc/Xalloc.hpp @@ -0,0 +1,101 @@ +#ifndef __FENNIX_KERNEL_Xalloc_H__ +#define __FENNIX_KERNEL_Xalloc_H__ + +#include +#include +#include + +typedef long unsigned Xuint64_t; +typedef long unsigned Xsize_t; + +#define Xalloc_PAGE_SIZE PAGE_SIZE +#define Xalloc_trace(m, ...) trace(m, ##__VA_ARGS__) +#define Xalloc_warn(m, ...) warn(m, ##__VA_ARGS__) +#define Xalloc_err(m, ...) error(m, ##__VA_ARGS__) +#define Xalloc_def NewLock(XallocLock) +#define Xalloc_lock XallocLock.Lock(__FUNCTION__) +#define Xalloc_unlock XallocLock.Unlock() + +namespace Xalloc +{ + class V1 + { + private: + void *BaseVirtualAddress = nullptr; + void *FirstBlock = nullptr; + void *LastBlock = nullptr; + + bool UserMapping = false; + bool SMAPUsed = false; + + public: + /** @brief Execute "stac" instruction if the kernel has SMAP enabled */ + void Xstac(); + + /** @brief Execute "clac" instruction if the kernel has SMAP enabled */ + void Xclac(); + + /** + * @brief Arrange the blocks to optimize the memory usage + * The allocator is not arranged by default + * to avoid performance issues. + * This function will defragment the memory + * and free the unused blocks. + * + * You should call this function when the + * kernel is idle or when is not using + * the allocator. + */ + void Arrange(); + + /** + * @brief Allocate a new memory block + * + * @param Size Size of the block to allocate. + * @return void* Pointer to the allocated block. + */ + void *malloc(Xsize_t Size); + + /** + * @brief Free a previously allocated block + * + * @param Address Address of the block to free. + */ + void free(void *Address); + + /** + * @brief Allocate a new memory block + * + * @param NumberOfBlocks Number of blocks to allocate. + * @param Size Size of the block to allocate. + * @return void* Pointer to the allocated block. + */ + void *calloc(Xsize_t NumberOfBlocks, Xsize_t Size); + + /** + * @brief Reallocate a previously allocated block + * + * @param Address Address of the block to reallocate. + * @param Size New size of the block. + * @return void* Pointer to the reallocated block. + */ + void *realloc(void *Address, Xsize_t Size); + + /** + * @brief Construct a new Allocator object + * + * @param BaseVirtualAddress Virtual address to map the pages. + * @param UserMode Map the new pages with USER flag? + * @param SMAPEnabled Does the kernel has Supervisor Mode Access Prevention enabled? + */ + V1(void *BaseVirtualAddress, bool UserMode, bool SMAPEnabled); + + /** + * @brief Destroy the Allocator object + * + */ + ~V1(); + }; +} + +#endif // !__FENNIX_KERNEL_Xalloc_H__ diff --git a/Core/Memory/HeapAllocators/Xalloc/XallocV1.cpp b/Core/Memory/HeapAllocators/Xalloc/XallocV1.cpp new file mode 100644 index 0000000..dd97eff --- /dev/null +++ b/Core/Memory/HeapAllocators/Xalloc/XallocV1.cpp @@ -0,0 +1,263 @@ +#include "Xalloc.hpp" + +Xalloc_def; + +#define XALLOC_CONCAT(x, y) x##y +#define XStoP(x) (x / Xalloc_PAGE_SIZE + 1) +#define XPtoS(x) (x * Xalloc_PAGE_SIZE) +#define Xalloc_BlockChecksum 0xA110C + +extern "C" void *Xalloc_REQUEST_PAGES(Xsize_t Pages); +extern "C" void Xalloc_FREE_PAGES(void *Address, Xsize_t Pages); +extern "C" void Xalloc_MAP_MEMORY(void *VirtualAddress, void *PhysicalAddress, Xsize_t Flags); +extern "C" void Xalloc_UNMAP_MEMORY(void *VirtualAddress); + +// TODO: Change memcpy with an optimized version +void *Xmemcpy(void *__restrict__ Destination, const void *__restrict__ Source, Xuint64_t Length) +{ + unsigned char *dst = (unsigned char *)Destination; + const unsigned char *src = (const unsigned char *)Source; + for (Xuint64_t i = 0; i < Length; i++) + dst[i] = src[i]; + return Destination; +} + +// TODO: Change memset with an optimized version +void *Xmemset(void *__restrict__ Destination, int Data, Xuint64_t Length) +{ + unsigned char *Buffer = (unsigned char *)Destination; + for (Xuint64_t i = 0; i < Length; i++) + Buffer[i] = (unsigned char)Data; + return Destination; +} + +namespace Xalloc +{ + class Block + { + public: + void *Address = nullptr; + + int Checksum = Xalloc_BlockChecksum; + Xsize_t Size = 0; + Block *Next = nullptr; + Block *Last = nullptr; + bool IsFree = true; + + bool Check() + { + if (this->Checksum != Xalloc_BlockChecksum) + return false; + return true; + } + + Block(Xsize_t Size) + { + this->Size = Size; + this->Address = Xalloc_REQUEST_PAGES(XStoP(Size)); + } + + ~Block() + { + Xalloc_FREE_PAGES(this->Address, XStoP(this->Size)); + } + + /** + * @brief Overload new operator to allocate memory from the heap + * @param Size Unused + * @return void* Pointer to the allocated memory + */ + void *operator new(Xsize_t Size) + { + void *ptr = Xalloc_REQUEST_PAGES(XStoP(sizeof(Block))); + return ptr; + } + + /** + * @brief Overload delete operator to free memory from the heap + * @param Address Pointer to the memory to free + */ + void operator delete(void *Address) + { + Xalloc_FREE_PAGES(Address, XStoP(sizeof(Block))); + } + } __attribute__((packed, aligned((16)))); + + class SmartSMAPClass + { + private: + V1 *allocator = nullptr; + + public: + SmartSMAPClass(V1 *allocator) + { + this->allocator = allocator; + this->allocator->Xstac(); + } + ~SmartSMAPClass() { this->allocator->Xclac(); } + }; +#define SmartSMAP SmartSMAPClass XALLOC_CONCAT(SmartSMAP##_, __COUNTER__)(this) + + void V1::Xstac() + { + if (this->SMAPUsed) + { +#if defined(__amd64__) || defined(__i386__) + asm volatile("stac" :: + : "cc"); +#endif + } + } + + void V1::Xclac() + { + if (this->SMAPUsed) + { +#if defined(__amd64__) || defined(__i386__) + asm volatile("clac" :: + : "cc"); +#endif + } + } + + void V1::Arrange() + { + Xalloc_err("Arrange() is not implemented yet!"); + } + + void *V1::malloc(Xsize_t Size) + { + if (Size == 0) + { + Xalloc_warn("Attempted to allocate 0 bytes!"); + return nullptr; + } + + SmartSMAP; + Xalloc_lock; + + if (this->FirstBlock == nullptr) + { + this->FirstBlock = new Block(Size); + ((Block *)this->FirstBlock)->IsFree = false; + ((Block *)this->FirstBlock)->Checksum = Xalloc_BlockChecksum; + Xmemset(((Block *)this->FirstBlock)->Address, 0, Size); + Xalloc_unlock; + return ((Block *)this->FirstBlock)->Address; + } + + Block *CurrentBlock = ((Block *)this->FirstBlock); + while (CurrentBlock != nullptr) + { + if (CurrentBlock->IsFree && CurrentBlock->Size >= Size) + { + CurrentBlock->IsFree = false; + CurrentBlock->Checksum = Xalloc_BlockChecksum; + Xmemset(CurrentBlock->Address, 0, Size); + Xalloc_unlock; + return CurrentBlock->Address; + } + CurrentBlock = CurrentBlock->Next; + } + + CurrentBlock = ((Block *)this->FirstBlock); + while (CurrentBlock->Next != nullptr) + CurrentBlock = CurrentBlock->Next; + + CurrentBlock->Next = new Block(Size); + ((Block *)CurrentBlock->Next)->Last = CurrentBlock; + ((Block *)CurrentBlock->Next)->IsFree = false; + ((Block *)CurrentBlock->Next)->Checksum = Xalloc_BlockChecksum; + Xmemset(((Block *)CurrentBlock->Next)->Address, 0, Size); + Xalloc_unlock; + return ((Block *)CurrentBlock->Next)->Address; + } + + void V1::free(void *Address) + { + if (Address == nullptr) + { + Xalloc_warn("Attempted to free a null pointer!"); + return; + } + + SmartSMAP; + Xalloc_lock; + + Block *CurrentBlock = ((Block *)this->FirstBlock); + while (CurrentBlock != nullptr) + { + if (CurrentBlock->Address == Address) + { + if (CurrentBlock->IsFree) + { + Xalloc_warn("Attempted to free an already freed pointer!"); + Xalloc_unlock; + return; + } + + CurrentBlock->IsFree = true; + CurrentBlock->Checksum = Xalloc_BlockChecksum; + Xalloc_unlock; + return; + } + CurrentBlock = CurrentBlock->Next; + } + + Xalloc_err("Invalid address."); + Xalloc_unlock; + } + + void *V1::calloc(Xsize_t NumberOfBlocks, Xsize_t Size) + { + if (NumberOfBlocks == 0 || Size == 0) + { + Xalloc_warn("The %s%s%s is 0!", + NumberOfBlocks == 0 ? "NumberOfBlocks" : "", + NumberOfBlocks == 0 && Size == 0 ? " and " : "", + Size == 0 ? "Size" : ""); + return nullptr; + } + + return this->malloc(NumberOfBlocks * Size); + } + + void *V1::realloc(void *Address, Xsize_t Size) + { + if (Address == nullptr) + return this->malloc(Size); + + if (Size == 0) + { + this->free(Address); + return nullptr; + } + + // SmartSMAP; + // Xalloc_lock; + // ... + // Xalloc_unlock; + + // TODO: Implement realloc + this->free(Address); + return this->malloc(Size); + } + + V1::V1(void *BaseVirtualAddress, bool UserMode, bool SMAPEnabled) + { + SmartSMAP; + Xalloc_lock; + this->SMAPUsed = SMAPEnabled; + this->UserMapping = UserMode; + this->BaseVirtualAddress = BaseVirtualAddress; + Xalloc_unlock; + } + + V1::~V1() + { + SmartSMAP; + Xalloc_lock; + Xalloc_trace("Destructor not implemented yet."); + Xalloc_unlock; + } +} diff --git a/Core/Memory/Memory.cpp b/Core/Memory/Memory.cpp index 4f77a3e..e0c3081 100644 --- a/Core/Memory/Memory.cpp +++ b/Core/Memory/Memory.cpp @@ -4,7 +4,7 @@ #include #include -#include "HeapAllocators/Xalloc.hpp" +#include "HeapAllocators/Xalloc/Xalloc.hpp" #include "../Library/liballoc_1_1.h" #include "../../kernel.h" @@ -32,7 +32,7 @@ PageTable4 *UserspaceKernelOnlyPageTable = nullptr; void *KPT = nullptr; static MemoryAllocatorType AllocatorType = MemoryAllocatorType::None; -Xalloc::AllocatorV1 *XallocV1Allocator = nullptr; +Xalloc::V1 *XallocV1Allocator = nullptr; #ifdef DEBUG __no_instrument_function void tracepagetable(PageTable4 *pt) @@ -241,7 +241,7 @@ __no_instrument_function void InitializeMemoryManagement(BootInfo *Info) debug("Page table updated."); if (strstr(Info->Kernel.CommandLine, "xallocv1")) { - XallocV1Allocator = new Xalloc::AllocatorV1((void *)KERNEL_HEAP_BASE, false, false); + XallocV1Allocator = new Xalloc::V1((void *)KERNEL_HEAP_BASE, false, false); AllocatorType = MemoryAllocatorType::XallocV1; trace("XallocV1 Allocator initialized (%p)", XallocV1Allocator); } @@ -262,11 +262,7 @@ void *HeapMalloc(size_t Size) case unlikely(MemoryAllocatorType::Pages): return KernelAllocator.RequestPages(TO_PAGES(Size)); case MemoryAllocatorType::XallocV1: - { - void *ret = XallocV1Allocator->Malloc(Size); - memset(ret, 0, Size); - return ret; - } + return XallocV1Allocator->malloc(Size); case MemoryAllocatorType::liballoc11: { void *ret = PREFIX(malloc)(Size); @@ -289,11 +285,7 @@ void *HeapCalloc(size_t n, size_t Size) case unlikely(MemoryAllocatorType::Pages): return KernelAllocator.RequestPages(TO_PAGES(n * Size)); case MemoryAllocatorType::XallocV1: - { - void *ret = XallocV1Allocator->Calloc(n, Size); - memset(ret, 0, n * Size); - return ret; - } + return XallocV1Allocator->calloc(n, Size); case MemoryAllocatorType::liballoc11: { void *ret = PREFIX(calloc)(n, Size); @@ -311,22 +303,12 @@ void *HeapRealloc(void *Address, size_t Size) SmartLockClass lock___COUNTER__(AllocatorLock, (KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown")); #endif memdbg("realloc(%#lx, %d)->[%s]", Address, Size, KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown"); - if (unlikely(!Address)) - { - error("Attempt to realloc a null pointer"); - return nullptr; - } - switch (AllocatorType) { case unlikely(MemoryAllocatorType::Pages): return KernelAllocator.RequestPages(TO_PAGES(Size)); // WARNING: Potential memory leak case MemoryAllocatorType::XallocV1: - { - void *ret = XallocV1Allocator->Realloc(Address, Size); - memset(ret, 0, Size); - return ret; - } + return XallocV1Allocator->realloc(Address, Size); case MemoryAllocatorType::liballoc11: { void *ret = PREFIX(realloc)(Address, Size); @@ -344,20 +326,13 @@ void HeapFree(void *Address) SmartLockClass lock___COUNTER__(AllocatorLock, (KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown")); #endif memdbg("free(%#lx)->[%s]", Address, KernelSymbolTable ? KernelSymbolTable->GetSymbolFromAddress((uintptr_t)__builtin_return_address(0)) : "Unknown"); - if (unlikely(!Address)) - { - warn("Attempt to free a null pointer"); - return; - } - switch (AllocatorType) { case unlikely(MemoryAllocatorType::Pages): KernelAllocator.FreePage(Address); // WARNING: Potential memory leak break; case MemoryAllocatorType::XallocV1: - if (XallocV1Allocator) - XallocV1Allocator->Free(Address); + XallocV1Allocator->free(Address); break; case MemoryAllocatorType::liballoc11: PREFIX(free) diff --git a/KThread.cpp b/KThread.cpp index 535ee6c..4ccf5bd 100644 --- a/KThread.cpp +++ b/KThread.cpp @@ -32,6 +32,7 @@ void TreeFS(Node *node, int Depth) { printf("%*c %s\eFFFFFF\n", Depth, ' ', Chld->Name); Display->SetBuffer(0); + TaskManager->Sleep(100); TreeFS(Chld, Depth + 1); } } @@ -137,6 +138,8 @@ void KernelMainThread() /* TODO: This should not be enabled because it may cause a deadlock. Not sure where or how. */ // Tasking::PCB *tskMgr = TaskManager->CreateProcess(TaskManager->GetCurrentProcess(), "Debug Task Manager", Tasking::TaskTrustLevel::Kernel); // TaskManager->CreateThread(tskMgr, (Tasking::IP)TaskMgr)->SetPriority(Tasking::High); + + TreeFS(vfs->GetRootNode(), 0); #endif KPrint("Kernel Compiled at: %s %s with C++ Standard: %d", __DATE__, __TIME__, CPP_LANGUAGE_STANDARD); @@ -163,10 +166,6 @@ void KernelMainThread() KPrint("Starting Network Interface Manager..."); NIManager->StartService(); -#ifdef DEBUG - TreeFS(vfs->GetRootNode(), 0); -#endif - Time::Clock tm = Time::ReadClock(); printf("\eCCCCCC[\e00AEFF%02d:%02d:%02d\eCCCCCC] ", tm.Hour, tm.Minute, tm.Second); const char *USpace_msg = "Setting up userspace"; diff --git a/Kernel.cpp b/Kernel.cpp index d121cca..f3afe24 100644 --- a/Kernel.cpp +++ b/Kernel.cpp @@ -340,6 +340,7 @@ EXTERNC __no_stack_protector __no_instrument_function void Entry(BootInfo *Info) #ifdef DEBUG // Running tests TestString(); + TestMemoryAllocation(); #endif EnableProfiler = true; diff --git a/Tests/MemoryAllocation.cpp b/Tests/MemoryAllocation.cpp new file mode 100644 index 0000000..5e04487 --- /dev/null +++ b/Tests/MemoryAllocation.cpp @@ -0,0 +1,224 @@ +#ifdef DEBUG + +#include +#include +#include + +/* Originally from: https://github.com/EnderIce2/FennixProject/blob/main/kernel/test.cpp */ + +#define MEMTEST_ITERATIONS 1024 + +class test_mem_new_delete +{ +public: + test_mem_new_delete(); + ~test_mem_new_delete(); +}; + +test_mem_new_delete::test_mem_new_delete() +{ + for (char i = 0; i < 2; i++) + ; +} + +test_mem_new_delete::~test_mem_new_delete() +{ + for (char i = 0; i < 2; i++) + ; +} + +void TestMemoryAllocation() +{ + void *tmpAlloc1 = kmalloc(176); + void *tmpAlloc2 = kmalloc(511); + void *tmpAlloc3 = kmalloc(1027); + void *tmpAlloc4 = kmalloc(1569); + for (int repeat = 0; repeat < 4; repeat++) + { + debug("---------------[TEST %d]---------------\n", repeat); + + debug("Single Page Request Test"); + { + uint64_t prq1 = (uint64_t)KernelAllocator.RequestPage(); + KernelAllocator.FreePage((void *)prq1); + + for (uint64_t i = 0; i < MEMTEST_ITERATIONS; i++) + KernelAllocator.FreePage(KernelAllocator.RequestPage()); + + uint64_t prq2 = (uint64_t)KernelAllocator.RequestPage(); + KernelAllocator.FreePage((void *)prq2); + + debug(" Result:\t\t1-[%#lx]; 2-[%#lx]", (void *)prq1, (void *)prq2); + assert(prq1 == prq2); + } + + debug("Multiple Page Request Test"); + { + uint64_t prq1 = (uint64_t)KernelAllocator.RequestPages(10); + KernelAllocator.FreePages((void *)prq1, 10); + + for (uint64_t i = 0; i < MEMTEST_ITERATIONS; i++) + KernelAllocator.FreePages(KernelAllocator.RequestPages(20), 20); + + uint64_t prq2 = (uint64_t)KernelAllocator.RequestPages(10); + KernelAllocator.FreePages((void *)prq2, 10); + + debug(" Result:\t\t1-[%#lx]; 2-[%#lx]", (void *)prq1, (void *)prq2); + assert(prq1 == prq2); + } + + debug("Multiple Fixed Malloc Test"); + { + uint64_t prq1 = (uint64_t)kmalloc(0x1000); + kfree((void *)prq1); + + for (uint64_t i = 0; i < MEMTEST_ITERATIONS; i++) + kfree(kmalloc(0x10000)); + + uint64_t prq2 = (uint64_t)kmalloc(0x1000); + kfree((void *)prq2); + + debug(" Result:\t\t1-[%#lx]; 2-[%#lx]", (void *)prq1, (void *)prq2); + assert(prq1 == prq2); + } + + debug("Multiple Dynamic Malloc Test"); + { + uint64_t prq1 = (uint64_t)kmalloc(0x1000); + kfree((void *)prq1); + + for (uint64_t i = 0; i < MEMTEST_ITERATIONS; i++) + kfree(kmalloc(i)); + + uint64_t prq2 = (uint64_t)kmalloc(0x1000); + kfree((void *)prq2); + + debug(" Result:\t1-[%#lx]; 2-[%#lx]", (void *)prq1, (void *)prq2); + assert(prq1 == prq2); + } + + debug("New/Delete Test"); + { + uint64_t prq1 = (uint64_t)kmalloc(0x1000); + kfree((void *)prq1); + + for (uint64_t i = 0; i < MEMTEST_ITERATIONS; i++) + { + test_mem_new_delete *t = new test_mem_new_delete(); + delete t; + } + + uint64_t prq2 = (uint64_t)kmalloc(0x1000); + kfree((void *)prq2); + + debug(" Result: \t1-[%#lx]; 2-[%#lx]", (void *)prq1, (void *)prq2); + assert(prq1 == prq2); + } + + debug("New/Delete Fixed Array Test"); + { + uint64_t prq1 = (uint64_t)kmalloc(0x1000); + kfree((void *)prq1); + + for (uint64_t i = 0; i < MEMTEST_ITERATIONS; i++) + { + char *t = new char[128]; + delete[] t; + } + + uint64_t prq2 = (uint64_t)kmalloc(0x1000); + kfree((void *)prq2); + + debug(" Result: \t1-[%#lx]; 2-[%#lx]", (void *)prq1, (void *)prq2); + assert(prq1 == prq2); + } + + debug("New/Delete Dynamic Array Test"); + { + uint64_t prq1 = (uint64_t)kmalloc(0x1000); + kfree((void *)prq1); + + for (uint64_t i = 0; i < MEMTEST_ITERATIONS; i++) + { + if (i == 0) + continue; + char *t = new char[i]; + delete[] t; + } + + uint64_t prq2 = (uint64_t)kmalloc(0x1000); + kfree((void *)prq2); + + debug(" Result:\t1-[%#lx]; 2-[%#lx]\n", (void *)prq1, (void *)prq2); + assert(prq1 == prq2); + } + + debug("calloc Test"); + { + uint64_t prq1 = (uint64_t)kmalloc(0x1000); + kfree((void *)prq1); + + for (uint64_t i = 0; i < MEMTEST_ITERATIONS; i++) + { + char *t = (char *)kcalloc(128, 1); + kfree(t); + } + + uint64_t prq2 = (uint64_t)kmalloc(0x1000); + kfree((void *)prq2); + + debug(" Result:\t1-[%#lx]; 2-[%#lx]", (void *)prq1, (void *)prq2); + assert(prq1 == prq2); + } + + debug("realloc Test"); + { + uint64_t prq1 = (uint64_t)kmalloc(0x1000); + kfree((void *)prq1); + + for (uint64_t i = 0; i < MEMTEST_ITERATIONS; i++) + { + char *t = (char *)kmalloc(128); + t = (char *)krealloc(t, 256); + kfree(t); + } + + uint64_t prq2 = (uint64_t)kmalloc(0x1000); + kfree((void *)prq2); + + debug(" Result:\t1-[%#lx]; 2-[%#lx]", (void *)prq1, (void *)prq2); + assert(prq1 == prq2); + } + } + + kfree(tmpAlloc1); + kfree(tmpAlloc2); + kfree(tmpAlloc3); + kfree(tmpAlloc4); + + debug("Memory Stress Test"); + for (size_t i = 0; i < 0x1000; i++) + kfree(kmalloc(i)); + + debug("Invalid Usage Test"); + kfree(tmpAlloc1); + kfree(tmpAlloc2); + kfree(tmpAlloc3); + kfree(tmpAlloc4); + + void *InvMlc = kmalloc(0); + assert(InvMlc == nullptr); + krealloc(InvMlc, 0); + assert(InvMlc == nullptr); + kcalloc(0, 0); + assert(InvMlc == nullptr); + kcalloc(1, 0); + assert(InvMlc == nullptr); + kcalloc(0, 1); + assert(InvMlc == nullptr); + kfree(InvMlc); + + debug("Memory Test Complete\n"); +} + +#endif // DEBUG diff --git a/Tests/t.h b/Tests/t.h index 96670db..4118f03 100644 --- a/Tests/t.h +++ b/Tests/t.h @@ -5,6 +5,7 @@ #include void TestString(); +void TestMemoryAllocation(); #endif // DEBUG #endif // !__FENNIX_KERNEL_non_constructor_tests_H__