Reimplemented Xalloc from scratch

This commit is contained in:
Alex
2023-02-24 10:19:55 +02:00
parent 62e9d7b9c9
commit 4519204580
11 changed files with 760 additions and 501 deletions

View 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.
---

View 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);
}

View 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__

View 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;
}
}