mirror of
https://github.com/Fennix-Project/Kernel.git
synced 2025-07-11 23:39:20 +00:00
Reimplemented Xalloc from scratch
This commit is contained in:
137
Core/Memory/HeapAllocators/Xalloc/README.md
Normal file
137
Core/Memory/HeapAllocators/Xalloc/README.md
Normal file
@ -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 <page size> /* <-- Replace with your page size */
|
||||
#define Xalloc_trace(m, ...) <trace function>
|
||||
#define Xalloc_warn(m, ...) <warning function>
|
||||
#define Xalloc_err(m, ...) <error function>
|
||||
#define Xalloc_def <define a lock> /* eg. std::mutex Xalloc_lock; */
|
||||
#define Xalloc_lock <lock function>
|
||||
#define Xalloc_unlock <unlock function>
|
||||
```
|
||||
|
||||
### 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.
|
||||
|
||||
<br><br>
|
||||
|
||||
```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.
|
||||
|
||||
<br><br>
|
||||
|
||||
```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.
|
||||
|
||||
<br><br>
|
||||
|
||||
```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.
|
||||
|
||||
---
|
23
Core/Memory/HeapAllocators/Xalloc/Wrapper.cpp
Normal file
23
Core/Memory/HeapAllocators/Xalloc/Wrapper.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
#include "Xalloc.hpp"
|
||||
|
||||
#include <memory.hpp>
|
||||
|
||||
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);
|
||||
}
|
101
Core/Memory/HeapAllocators/Xalloc/Xalloc.hpp
Normal file
101
Core/Memory/HeapAllocators/Xalloc/Xalloc.hpp
Normal file
@ -0,0 +1,101 @@
|
||||
#ifndef __FENNIX_KERNEL_Xalloc_H__
|
||||
#define __FENNIX_KERNEL_Xalloc_H__
|
||||
|
||||
#include <memory.hpp>
|
||||
#include <lock.hpp>
|
||||
#include <debug.h>
|
||||
|
||||
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__
|
263
Core/Memory/HeapAllocators/Xalloc/XallocV1.cpp
Normal file
263
Core/Memory/HeapAllocators/Xalloc/XallocV1.cpp
Normal file
@ -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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user