GDT, page frame allocator
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
#include "Panic.hpp"
|
||||
|
||||
void Panic(const char *meditationString, System::Registers registers) {
|
||||
kerr << "=========== Kernel panic ===========" << Kt::newline;
|
||||
kerr << meditationString << Kt::newline;
|
||||
|
||||
|
||||
while (true) {
|
||||
#if defined (__x86_64__)
|
||||
asm ("cli");
|
||||
asm ("hlt");
|
||||
#elif defined (__aarch64__) || defined (__riscv)
|
||||
asm ("wfi");
|
||||
#elif defined (__loongarch64)
|
||||
asm ("idle 0");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
#include <System/Registers.hpp>
|
||||
#include <KernelTerminal/terminal.hpp>
|
||||
|
||||
void Panic(const char *meditationString, System::Registers registers);
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* UEFI.hpp
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace Efi {
|
||||
typedef void* EFI_HANDLE;
|
||||
|
||||
struct TableHeader {
|
||||
std::uint64_t Signature;
|
||||
std::uint32_t Revision;
|
||||
std::uint32_t HeaderSize;
|
||||
std::uint32_t CRC32;
|
||||
std::uint32_t Reserved;
|
||||
}__attribute__((packed));
|
||||
|
||||
struct SystemTable {
|
||||
TableHeader Header;
|
||||
void* FirmwareVendor; // Pointer to a CHAR16 string of the fw vendor name string
|
||||
std::uint32_t FirmwareRevision;
|
||||
|
||||
EFI_HANDLE ConsoleInHandle;
|
||||
void* ConIn;
|
||||
EFI_HANDLE ConsoleOutHandle;
|
||||
void* ConOut;
|
||||
|
||||
EFI_HANDLE StandardErrorHandle;
|
||||
void* StdErr;
|
||||
|
||||
// Jackpot
|
||||
void *RuntimeServices;
|
||||
|
||||
void *BootServices;
|
||||
|
||||
std::uint64_t NumberOfTableEntries;
|
||||
|
||||
void *ConfigurationTable;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
;
|
||||
; gdt.asm
|
||||
; Copyright (c) 2025 Daniel Hammer
|
||||
;
|
||||
|
||||
[bits 64]
|
||||
section .text ; Text/code section
|
||||
|
||||
global ReloadSegments
|
||||
global LoadGDT
|
||||
|
||||
LoadGDT:
|
||||
lgdt [rdi] ; Run LGDT on the contents of 1st C parameter
|
||||
ret
|
||||
|
||||
ReloadSegments:
|
||||
push 0x08 ; CS descriptor
|
||||
lea rax, [rel .reload_CS]
|
||||
push rax
|
||||
retfq
|
||||
.reload_CS:
|
||||
mov ax, 0x10 ; DS descriptor
|
||||
|
||||
; ds, es, fs, gs, ss are segment registers on x86_64
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
mov ss, ax
|
||||
|
||||
ret
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* gdt.hpp
|
||||
*/
|
||||
|
||||
#include "GDT.hpp"
|
||||
#include "../KernelTerminal/terminal.hpp"
|
||||
|
||||
// Limine loads a GDT of course, (CS = 0x28) but we will need to make a TSS someday... therefore we load our own now
|
||||
|
||||
namespace Hal {
|
||||
using namespace Kt;
|
||||
|
||||
GDTPointer gdtPointer{};
|
||||
BasicGDT kernelGDT{};
|
||||
|
||||
void PrepareGDT() {
|
||||
kout << "[Hal] GDT at " << base::hex << (uint64_t)&kernelGDT << "\n";
|
||||
kernelGDT = {
|
||||
// Code segment offset 0x08
|
||||
// Data segment offset 0x10
|
||||
|
||||
// Not sure if having LimitLow set to 0xFFFF for the Null segment is kosher
|
||||
{0xFFFF, 0, 0, 0x00, 0x00, 0},
|
||||
|
||||
// Kernel code/data
|
||||
{0xFFFF, 0, 0, 0x9A, 0xA0, 0},
|
||||
{0xFFFF, 0, 0, 0x92, 0xA0, 0},
|
||||
|
||||
// User code/data
|
||||
{0xFFFF, 0, 0, 0x9A, 0xA0, 0},
|
||||
{0xFFFF, 0, 0, 0x92, 0xA0, 0},
|
||||
|
||||
// One day this will point to our actual TSS
|
||||
{
|
||||
// Limit = sizeof(TSS) - 1
|
||||
0,
|
||||
|
||||
// Base = &TSS
|
||||
0,
|
||||
0,
|
||||
|
||||
// Access byte = 0xFA
|
||||
0xFA,
|
||||
|
||||
// Granularity = 0x00
|
||||
0x00,
|
||||
|
||||
0x0
|
||||
}
|
||||
};
|
||||
|
||||
gdtPointer = GDTPointer{
|
||||
.Size = sizeof(kernelGDT) - 1,
|
||||
.GDTAddress = (uint64_t)&kernelGDT
|
||||
};
|
||||
}
|
||||
|
||||
// Helpers implemented in gdt.asm
|
||||
extern "C" void LoadGDT(GDTPointer *gdtPointer);
|
||||
extern "C" void ReloadSegments();
|
||||
|
||||
void BridgeLoadGDT() {
|
||||
// Puts the GDT pointer structure into the GDTR
|
||||
kout << "[Hal] Setting GDTR" << Kt::newline;
|
||||
LoadGDT(&gdtPointer);
|
||||
|
||||
kout << "[Hal] Reloading segments" << Kt::newline;
|
||||
ReloadSegments();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* gdt.hpp
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
using namespace std;
|
||||
|
||||
// __attribute__((packed)) is the GCC extensions way of telling the compiler to ensure that it doesn't mess with these structures or add packing bytes
|
||||
// for optimization because that would easily result in a triple fault.
|
||||
|
||||
namespace Hal {
|
||||
class GDTEntry {
|
||||
public:
|
||||
// Base/Limit are obsolete in Long mode because segmentation is no longer used
|
||||
// Sadly we must still build and load the GDT during kernel init
|
||||
|
||||
uint16_t LimitLow;
|
||||
uint16_t BaseLow;
|
||||
uint8_t BaseMiddle;
|
||||
|
||||
// Determine which processor rings this segment can be used in
|
||||
uint8_t AccessByte;
|
||||
// Lower 4 bits are the higher 4 bits of limit
|
||||
uint8_t GranularityByte;
|
||||
|
||||
uint8_t BaseHigh;
|
||||
|
||||
// 16 + 16 + 8 + 8 = 48 bits
|
||||
}__attribute__((packed));
|
||||
|
||||
struct BasicGDT {
|
||||
// Conventionally the first entry of the GDT has all values zeroed out.
|
||||
GDTEntry Null;
|
||||
|
||||
// Kernel code segment descriptor
|
||||
GDTEntry KernelCode;
|
||||
|
||||
// Kernel data segment descriptor
|
||||
GDTEntry KernelData;
|
||||
|
||||
// UM code segment descriptor
|
||||
GDTEntry UserCode;
|
||||
|
||||
// UM data segment descriptor
|
||||
GDTEntry UserData;
|
||||
|
||||
// Task State Segment
|
||||
GDTEntry TSS;
|
||||
}__attribute__((packed));
|
||||
|
||||
// Simple structure that tells the CPU the size of the GDT, and it's address
|
||||
struct GDTPointer {
|
||||
uint16_t Size;
|
||||
uint64_t GDTAddress;
|
||||
}__attribute__((packed));
|
||||
|
||||
void BridgeLoadGDT();
|
||||
void PrepareGDT();
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "terminal.hpp"
|
||||
#include "../Libraries/flanterm/backends/fb.h"
|
||||
#include "../Libraries/flanterm/flanterm.h"
|
||||
|
||||
#include "../Libraries/string.hpp"
|
||||
|
||||
namespace Kt {
|
||||
flanterm_context *ctx;
|
||||
|
||||
void Initialize(std::uint32_t *framebuffer, std::size_t width, std::size_t height, std::size_t pitch,
|
||||
std::uint8_t red_mask_size, std::uint8_t red_mask_shift,
|
||||
std::uint8_t green_mask_size, std::uint8_t green_mask_shift,
|
||||
std::uint8_t blue_mask_size, std::uint8_t blue_mask_shift
|
||||
)
|
||||
{
|
||||
ctx = flanterm_fb_init(
|
||||
NULL,
|
||||
NULL,
|
||||
framebuffer,
|
||||
width, height, pitch,
|
||||
red_mask_size, red_mask_shift,
|
||||
green_mask_size, green_mask_shift,
|
||||
blue_mask_size, blue_mask_shift,
|
||||
NULL,
|
||||
NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL, NULL,
|
||||
NULL, 0, 0, 1,
|
||||
0, 0,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
void Putchar(char c) {
|
||||
flanterm_write(ctx, &c, 1);
|
||||
}
|
||||
|
||||
void Print(const char *text) {
|
||||
flanterm_write(ctx, text, Lib::strlen(text));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,104 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
#include <Libraries/string.hpp>
|
||||
|
||||
namespace Kt
|
||||
{
|
||||
constexpr char newline = '\n';
|
||||
|
||||
namespace screen
|
||||
{
|
||||
constexpr const char *clear = "\033[2J";
|
||||
constexpr const char *cursor_reset = "\033[H";
|
||||
};
|
||||
|
||||
void Initialize(std::uint32_t *framebuffer, std::size_t width, std::size_t height, std::size_t pitch,
|
||||
std::uint8_t red_mask_size, std::uint8_t red_mask_shift,
|
||||
std::uint8_t green_mask_size, std::uint8_t green_mask_shift,
|
||||
std::uint8_t blue_mask_size, std::uint8_t blue_mask_shift);
|
||||
void Putchar(char c);
|
||||
void Print(const char *text);
|
||||
|
||||
enum base
|
||||
{
|
||||
oct = 8,
|
||||
dec = 10,
|
||||
hex = 16
|
||||
};
|
||||
|
||||
inline base base_custom(int custom)
|
||||
{
|
||||
return (base)custom;
|
||||
}
|
||||
|
||||
class KernelOutStream
|
||||
{
|
||||
public:
|
||||
base streamBaseType = base::dec;
|
||||
|
||||
// C++ streaming operator like cout
|
||||
friend KernelOutStream &operator<<(KernelOutStream &t, const char *string)
|
||||
{
|
||||
Print(string);
|
||||
return t;
|
||||
}
|
||||
|
||||
// C++ streaming operator like cout
|
||||
friend KernelOutStream &operator<<(KernelOutStream &t, const char chr)
|
||||
{
|
||||
Putchar(chr);
|
||||
return t;
|
||||
}
|
||||
|
||||
friend KernelOutStream &operator<<(KernelOutStream &t, int number)
|
||||
{
|
||||
Print(Lib::int2basestr(number, t.streamBaseType));
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
friend KernelOutStream &operator<<(KernelOutStream &t, std::uint32_t number)
|
||||
{
|
||||
Print(Lib::uint2basestr(number, t.streamBaseType));
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
friend KernelOutStream &operator<<(KernelOutStream &t, std::uint64_t number)
|
||||
{
|
||||
Print(Lib::u64_2_basestr(number, t.streamBaseType));
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
friend KernelOutStream &operator<<(KernelOutStream &t, base newBase)
|
||||
{
|
||||
t.streamBaseType = newBase;
|
||||
|
||||
return t;
|
||||
}
|
||||
};
|
||||
|
||||
// This will be on the kernel entry point's stack. Which is totally fine since we don't ever exit from that function
|
||||
};
|
||||
|
||||
extern Kt::KernelOutStream kout;
|
||||
|
||||
namespace Kt
|
||||
{
|
||||
class KernelErrorStream
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
friend KernelErrorStream &operator<<(KernelErrorStream &t, T value)
|
||||
{
|
||||
kout << "\e[0;31m" << value << "\e[0m";
|
||||
|
||||
return t;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
extern Kt::KernelErrorStream kerr;
|
||||
Submodule
+1
Submodule kernel/src/Libraries/flanterm added at 201100c968
@@ -0,0 +1,81 @@
|
||||
#include "string.hpp"
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Lib
|
||||
{
|
||||
char output[1024];
|
||||
|
||||
// TODO make this buffer safe
|
||||
char *int2basestr(int num, size_t radix)
|
||||
{
|
||||
char * str = (char *)&output;
|
||||
|
||||
if (!num) {
|
||||
return "0";
|
||||
}
|
||||
|
||||
char base[] = "0123456789ABCDEFGH"; // IJKLMN ....
|
||||
if (radix < 2 || radix > (sizeof(base) - 1))
|
||||
{ // radix supported?
|
||||
str[0] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
int si = 0;
|
||||
if (num < 0)
|
||||
{
|
||||
str[si++] = '-';
|
||||
num = -num;
|
||||
}
|
||||
char tmp[256];
|
||||
int rdi = -1;
|
||||
while (num)
|
||||
{
|
||||
tmp[++rdi] = base[num % radix];
|
||||
num /= radix;
|
||||
}
|
||||
while (rdi >= 0)
|
||||
str[si++] = tmp[rdi--];
|
||||
str[si] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
char *u64_2_basestr(uint64_t num, size_t radix)
|
||||
{
|
||||
char * str = (char *)&output;
|
||||
|
||||
if (!num) {
|
||||
return "0";
|
||||
}
|
||||
|
||||
char base[] = "0123456789ABCDEFGH"; // IJKLMN ....
|
||||
|
||||
if (radix < 2 || radix > (sizeof(base) - 1))
|
||||
{ // radix supported?
|
||||
str[0] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
int si = 0;
|
||||
|
||||
char tmp[256];
|
||||
int rdi = -1;
|
||||
while (num)
|
||||
{
|
||||
tmp[++rdi] = base[num % radix];
|
||||
num /= radix;
|
||||
}
|
||||
while (rdi >= 0)
|
||||
str[si++] = tmp[rdi--];
|
||||
str[si] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
char *uint2basestr(uint32_t num, size_t radix)
|
||||
{
|
||||
return u64_2_basestr((uint64_t)num, radix);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
namespace Lib {
|
||||
inline int strlen(const char *string) {
|
||||
int c = 0;
|
||||
|
||||
while (*string != '\0') {
|
||||
string++;
|
||||
c++;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
char *int2basestr(int num, size_t radix);
|
||||
char *u64_2_basestr(uint64_t num, size_t radix);
|
||||
char *uint2basestr(uint32_t num, size_t radix);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
namespace Memory {
|
||||
extern std::uint64_t HHDMBase;
|
||||
|
||||
inline uint64_t HHDM(uint64_t address) {
|
||||
return HHDMBase + address;
|
||||
}
|
||||
|
||||
inline uint64_t HHDM(void* address) {
|
||||
return HHDMBase + (uint64_t)address;
|
||||
}
|
||||
|
||||
inline uint64_t SubHHDM(uint64_t address) {
|
||||
return address - HHDMBase;
|
||||
}
|
||||
|
||||
inline uint64_t SubHHDM(void* address) {
|
||||
return SubHHDM((uint64_t)address);
|
||||
}
|
||||
|
||||
inline uint64_t IsHDDMVirtAddr(uint64_t address) {
|
||||
if (address > HHDMBase) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "Memmap.hpp"
|
||||
#include <KernelTerminal/terminal.hpp>
|
||||
#include <Common/Panic.hpp>
|
||||
|
||||
#include "PageAllocator.hpp"
|
||||
|
||||
using namespace Kt;
|
||||
|
||||
namespace Memory {
|
||||
LargestSection Scan(limine_memmap_response* mmap) {
|
||||
LargestSection currentLargestSection{};
|
||||
|
||||
for (size_t i = 0; i < mmap->entry_count; i++) {
|
||||
auto entry = mmap->entries[i];
|
||||
|
||||
if (entry->base == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry->type == LIMINE_MEMMAP_USABLE) {
|
||||
kout << "[Mem] Found conventional memory section (size = " << base::dec << entry->length << " bytes, address = 0x" << base::hex << (uint64_t)entry->base << ")" << newline;
|
||||
|
||||
if (entry->length > currentLargestSection.size) {
|
||||
currentLargestSection = {
|
||||
.address = (uint64_t)entry->base,
|
||||
.size = entry->length
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[unlikely]] if (currentLargestSection.address == 0) {
|
||||
Panic("Couldn't find a usable memory section.", System::Registers{});
|
||||
}
|
||||
|
||||
return currentLargestSection;
|
||||
|
||||
// PageAllocator pa(currentLargestSection);
|
||||
|
||||
// uint64_t alloc1 = (uint64_t)pa.Allocate();
|
||||
// uint64_t alloc2 = (uint64_t)pa.Allocate();
|
||||
// pa.Free((void *)alloc2);
|
||||
// uint64_t alloc3 = (uint64_t)pa.Allocate();
|
||||
|
||||
// kout << "0x" << base::hex << alloc1 << "\n";
|
||||
// kout << "0x" << base::hex << alloc2 << "\n";
|
||||
// kout << "0x" << base::hex << alloc3 << "\n";
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include <limine.h>
|
||||
#include <cstddef>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Memory {
|
||||
// Shared
|
||||
struct LargestSection {
|
||||
uint64_t address;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
LargestSection Scan(limine_memmap_response* mmap);
|
||||
};
|
||||
@@ -0,0 +1,131 @@
|
||||
#include "PageAllocator.hpp"
|
||||
#include "Memmap.hpp"
|
||||
#include "HHDM.hpp"
|
||||
|
||||
#include <KernelTerminal/terminal.hpp>
|
||||
|
||||
namespace Memory
|
||||
{
|
||||
#if defined(__x86_64__) || defined(__aarch64__)
|
||||
#define PAGE_SIZE 4096
|
||||
#else
|
||||
#error Sorry, Unimplemented!
|
||||
#endif
|
||||
|
||||
PageAllocator::PageAllocator(LargestSection section)
|
||||
{
|
||||
g_section = section;
|
||||
|
||||
size_t pages = g_section.size / PAGE_SIZE;
|
||||
Block *current_ref{&head};
|
||||
Block *start_block_ptr {(Block *)HHDM(section.address)};
|
||||
Block& start_block = *start_block_ptr;
|
||||
|
||||
start_block = Block {
|
||||
.size = section.size,
|
||||
.next = nullptr
|
||||
};
|
||||
|
||||
*start_block_ptr = start_block;
|
||||
|
||||
current_ref->next = start_block_ptr;
|
||||
|
||||
kout << "[Mem] PageAllocator created for memory section starting at 0x" << Kt::base::hex << section.address << Kt::newline;
|
||||
}
|
||||
|
||||
void* PageAllocator::RequestPage(bool phys) {
|
||||
bool found_block = false;
|
||||
|
||||
Block* current_block_ptr{head.next};
|
||||
Block* current_prev_ptr{(Block *)&head};
|
||||
|
||||
while (!found_block) {
|
||||
if (current_block_ptr == nullptr) {
|
||||
kerr << "A PageAllocator was forced to return a Null pointer. (1)" << Kt::newline;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (current_prev_ptr == nullptr) {
|
||||
kerr << "A PageAllocator was forced to return a Null pointer. (2)" << Kt::newline;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (current_block_ptr->size >= 0x1000) { // 0x1000 == 4096
|
||||
current_block_ptr->size -= 0x1000;
|
||||
|
||||
if (!current_block_ptr->size) {
|
||||
current_prev_ptr->next = current_block_ptr->next;
|
||||
return (void *)current_block_ptr;
|
||||
}
|
||||
|
||||
Block block_copy = *current_block_ptr;
|
||||
|
||||
current_prev_ptr->next = (Block *)(uint64_t)current_block_ptr + 0x1000;
|
||||
*current_prev_ptr->next = block_copy;
|
||||
|
||||
if (phys) {
|
||||
return (void *)SubHHDM(current_block_ptr);
|
||||
}
|
||||
|
||||
return (void *)current_block_ptr;
|
||||
} else {
|
||||
if (current_block_ptr->next != nullptr) {
|
||||
current_prev_ptr = current_block_ptr;
|
||||
current_block_ptr = current_block_ptr->next;
|
||||
} else {
|
||||
kerr << "A PageAllocator was forced to return a Null pointer. (3)" << Kt::newline;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (current_block_ptr != nullptr) {
|
||||
if (current_block_ptr->next == nullptr) {
|
||||
kerr << "A PageAllocator was forced to return a Null pointer. (4)" << Kt::newline;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
current_block_ptr = current_block_ptr->next;
|
||||
} else
|
||||
{
|
||||
kerr << "A PageAllocator was forced to return a Null pointer. (5)" << Kt::newline;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PageAllocator::Free(void *pagePtr) {
|
||||
if (!IsHDDMVirtAddr((uint64_t)pagePtr)) {
|
||||
pagePtr = (void *)HHDM(pagePtr);
|
||||
}
|
||||
|
||||
Block* fmrNext = head.next;
|
||||
|
||||
Block* blockPtr = (Block *)pagePtr;
|
||||
|
||||
head.next = blockPtr;
|
||||
head.next->size = 0x1000;
|
||||
|
||||
head.next->next = fmrNext;
|
||||
}
|
||||
|
||||
void PageAllocator::Stress() {
|
||||
// ~410 MiB
|
||||
for (size_t i = 0; i < 100000; i++) {
|
||||
kout << "[Mem] Stress allocation " << Kt::base::dec << i << ": " << "0x" << Kt::base::hex << (uint64_t)Memory::KernelPFA->RequestPage(false) << Kt::newline;
|
||||
}
|
||||
}
|
||||
|
||||
// Traverses the PageAllocator's linked list for debugging
|
||||
void PageAllocator::Walk() {
|
||||
Block* current = {head.next};
|
||||
size_t i{0};
|
||||
|
||||
while (current != nullptr) {
|
||||
kout << "Block " << Kt::base::dec << i << " {" << current->size << " bytes & address 0x" << Kt::base::hex << (uint64_t)current << "}" << Kt::newline;
|
||||
current = current->next;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "Memmap.hpp"
|
||||
|
||||
namespace Memory {
|
||||
class PageAllocator {
|
||||
LargestSection g_section;
|
||||
|
||||
struct Block {
|
||||
size_t size;
|
||||
Block* next;
|
||||
};
|
||||
|
||||
Block head;
|
||||
public:
|
||||
PageAllocator(LargestSection section);
|
||||
void* RequestPage(bool phys = false);
|
||||
void Free(void *pagePtr);
|
||||
|
||||
void Stress();
|
||||
void Walk();
|
||||
};
|
||||
|
||||
extern PageAllocator* KernelPFA;
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#if defined (__x86_64__)
|
||||
namespace System {
|
||||
struct Registers {
|
||||
|
||||
};
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,249 @@
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <limine.h>
|
||||
|
||||
#include <Hal/GDT.hpp>
|
||||
#include <KernelTerminal/terminal.hpp>
|
||||
#include <Libraries/string.hpp>
|
||||
#include <Efi/UEFI.hpp>
|
||||
#include <Common/Panic.hpp>
|
||||
#include <Memory/Memmap.hpp>
|
||||
#include <Memory/PageAllocator.hpp>
|
||||
|
||||
using namespace Kt;
|
||||
|
||||
namespace Memory {
|
||||
PageAllocator* KernelPFA;
|
||||
uint64_t HHDMBase;
|
||||
};
|
||||
|
||||
KernelOutStream kout;
|
||||
KernelErrorStream kerr;
|
||||
|
||||
// Set the base revision to 3, this is recommended as this is the latest
|
||||
// base revision described by the Limine boot protocol specification.
|
||||
// See specification for further info.
|
||||
|
||||
namespace {
|
||||
|
||||
__attribute__((used, section(".limine_requests")))
|
||||
volatile LIMINE_BASE_REVISION(3);
|
||||
|
||||
}
|
||||
|
||||
// The Limine requests can be placed anywhere, but it is important that
|
||||
// the compiler does not optimise them away, so, usually, they should
|
||||
// be made volatile or equivalent, _and_ they should be accessed at least
|
||||
// once or marked as used with the "used" attribute as done here.
|
||||
|
||||
namespace {
|
||||
|
||||
__attribute__((used, section(".limine_requests")))
|
||||
volatile limine_framebuffer_request framebuffer_request = {
|
||||
.id = LIMINE_FRAMEBUFFER_REQUEST,
|
||||
.revision = 0,
|
||||
.response = nullptr
|
||||
};
|
||||
|
||||
__attribute__((used, section(".limine_requests")))
|
||||
volatile limine_efi_system_table_request system_table_request = {
|
||||
.id = LIMINE_EFI_SYSTEM_TABLE_REQUEST,
|
||||
.revision = 0,
|
||||
.response = nullptr
|
||||
};
|
||||
|
||||
__attribute__((used, section(".limine_requests")))
|
||||
volatile limine_hhdm_request hhdm_request = {
|
||||
.id = LIMINE_HHDM_REQUEST,
|
||||
.revision = 0,
|
||||
.response = nullptr
|
||||
};
|
||||
|
||||
__attribute__((used, section(".limine_requests")))
|
||||
volatile limine_memmap_request memmap_request = {
|
||||
.id = LIMINE_MEMMAP_REQUEST,
|
||||
.revision = 0,
|
||||
.response = nullptr
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// Finally, define the start and end markers for the Limine requests.
|
||||
// These can also be moved anywhere, to any .cpp file, as seen fit.
|
||||
|
||||
namespace {
|
||||
|
||||
__attribute__((used, section(".limine_requests_start")))
|
||||
volatile LIMINE_REQUESTS_START_MARKER;
|
||||
|
||||
__attribute__((used, section(".limine_requests_end")))
|
||||
volatile LIMINE_REQUESTS_END_MARKER;
|
||||
|
||||
}
|
||||
|
||||
// GCC and Clang reserve the right to generate calls to the following
|
||||
// 4 functions even if they are not directly called.
|
||||
// Implement them as the C specification mandates.
|
||||
// DO NOT remove or rename these functions, or stuff will eventually break!
|
||||
// They CAN be moved to a different .cpp file.
|
||||
|
||||
extern "C" {
|
||||
|
||||
void *memcpy(void *dest, const void *src, std::size_t n) {
|
||||
std::uint8_t *pdest = static_cast<std::uint8_t *>(dest);
|
||||
const std::uint8_t *psrc = static_cast<const std::uint8_t *>(src);
|
||||
|
||||
for (std::size_t i = 0; i < n; i++) {
|
||||
pdest[i] = psrc[i];
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
void *memset(void *s, int c, std::size_t n) {
|
||||
std::uint8_t *p = static_cast<std::uint8_t *>(s);
|
||||
|
||||
for (std::size_t i = 0; i < n; i++) {
|
||||
p[i] = static_cast<uint8_t>(c);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void *memmove(void *dest, const void *src, std::size_t n) {
|
||||
std::uint8_t *pdest = static_cast<std::uint8_t *>(dest);
|
||||
const std::uint8_t *psrc = static_cast<const std::uint8_t *>(src);
|
||||
|
||||
if (src > dest) {
|
||||
for (std::size_t i = 0; i < n; i++) {
|
||||
pdest[i] = psrc[i];
|
||||
}
|
||||
} else if (src < dest) {
|
||||
for (std::size_t i = n; i > 0; i--) {
|
||||
pdest[i-1] = psrc[i-1];
|
||||
}
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
int memcmp(const void *s1, const void *s2, std::size_t n) {
|
||||
const std::uint8_t *p1 = static_cast<const std::uint8_t *>(s1);
|
||||
const std::uint8_t *p2 = static_cast<const std::uint8_t *>(s2);
|
||||
|
||||
for (std::size_t i = 0; i < n; i++) {
|
||||
if (p1[i] != p2[i]) {
|
||||
return p1[i] < p2[i] ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Halt and catch fire function.
|
||||
namespace {
|
||||
|
||||
void hcf() {
|
||||
for (;;) {
|
||||
#if defined (__x86_64__)
|
||||
asm ("hlt");
|
||||
#elif defined (__aarch64__) || defined (__riscv)
|
||||
asm ("wfi");
|
||||
#elif defined (__loongarch64)
|
||||
asm ("idle 0");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// The following stubs are required by the Itanium C++ ABI (the one we use,
|
||||
// regardless of the "Itanium" nomenclature).
|
||||
// Like the memory functions above, these stubs can be moved to a different .cpp file,
|
||||
// but should not be removed, unless you know what you are doing.
|
||||
extern "C" {
|
||||
int __cxa_atexit(void (*)(void *), void *, void *) { return 0; }
|
||||
void __cxa_pure_virtual() { hcf(); }
|
||||
void *__dso_handle;
|
||||
}
|
||||
|
||||
// Extern declarations for global constructors array.
|
||||
extern void (*__init_array[])();
|
||||
extern void (*__init_array_end[])();
|
||||
|
||||
// The following will be our kernel's entry point.
|
||||
// If renaming kmain() to something else, make sure to change the
|
||||
// linker script accordingly.
|
||||
extern "C" void kmain() {
|
||||
// Ensure the bootloader actually understands our base revision (see spec).
|
||||
if (LIMINE_BASE_REVISION_SUPPORTED == false) {
|
||||
hcf();
|
||||
}
|
||||
|
||||
// Call global constructors.
|
||||
for (std::size_t i = 0; &__init_array[i] != __init_array_end; i++) {
|
||||
__init_array[i]();
|
||||
}
|
||||
|
||||
// Ensure we got a framebuffer.
|
||||
if (framebuffer_request.response == nullptr
|
||||
|| framebuffer_request.response->framebuffer_count < 1) {
|
||||
hcf();
|
||||
}
|
||||
|
||||
// Fetch the first framebuffer.
|
||||
limine_framebuffer *framebuffer{framebuffer_request.response->framebuffers[0]};
|
||||
|
||||
// Initialize the terminal
|
||||
Kt::Initialize((uint32_t*)framebuffer->address, framebuffer->width, framebuffer->height, framebuffer->pitch, framebuffer->red_mask_size, framebuffer->red_mask_shift,
|
||||
framebuffer->green_mask_size, framebuffer->green_mask_shift,
|
||||
framebuffer->blue_mask_size, framebuffer->blue_mask_shift);
|
||||
|
||||
#if defined (__x86_64__)
|
||||
Hal::PrepareGDT();
|
||||
Hal::BridgeLoadGDT();
|
||||
#endif
|
||||
|
||||
// RGB lines
|
||||
for (std::size_t i = 500; i < 800; i++) {
|
||||
volatile std::uint32_t *fb_ptr = static_cast<volatile std::uint32_t *>(framebuffer->address);
|
||||
fb_ptr[i * (framebuffer->pitch / 4) + (i - 5*5)] = 0xFF0000; // Red
|
||||
fb_ptr[i * (framebuffer->pitch / 4) + (i - 10*5)] = 0x00FF00; // Green
|
||||
fb_ptr[i * (framebuffer->pitch / 4) + (i - 15*5)] = 0x0000FF; // Blue
|
||||
|
||||
// fb_ptr[i * (framebuffer->pitch / 4) + (i - 20*5)] = 0xFF0000; // Red
|
||||
// fb_ptr[i * (framebuffer->pitch / 4) + (i - 25*5)] = 0x00FF00; // Green
|
||||
// fb_ptr[i * (framebuffer->pitch / 4) + (i - 30*5)] = 0x0000FF; // Blue
|
||||
|
||||
// fb_ptr[i * (framebuffer->pitch / 4) + (i - 35*5)] = 0xFF0000; // Red
|
||||
// fb_ptr[i * (framebuffer->pitch / 4) + (i - 40*5)] = 0x00FF00; // Green
|
||||
// fb_ptr[i * (framebuffer->pitch / 4) + (i - 45*5)] = 0x0000FF; // Blue
|
||||
}
|
||||
|
||||
uint64_t hhdm_offset = hhdm_request.response->offset;
|
||||
|
||||
kout << "[Mem] HHDM offset: 0x" << base::hex << hhdm_offset << newline;
|
||||
|
||||
Memory::HHDMBase = hhdm_offset;
|
||||
|
||||
if (!system_table_request. response || !system_table_request.response->address) {
|
||||
kerr << "[Efi] EFI System Table not supported" << newline;
|
||||
} else {
|
||||
kout << "[Efi] EFI system table at 0x" << base::hex << (uint64_t)system_table_request.response->address << newline;
|
||||
}
|
||||
|
||||
if (memmap_request.response != nullptr) {
|
||||
auto result = Memory::Scan(memmap_request.response);
|
||||
auto allocator = Memory::PageAllocator(result);
|
||||
|
||||
kout << "[Mem] Creating PageAllocator for system conventional memory" << newline;
|
||||
Memory::KernelPFA = &allocator;
|
||||
} else {
|
||||
Panic("Guru Meditation Error: System memory map missing!", System::Registers{});
|
||||
}
|
||||
|
||||
|
||||
hcf();
|
||||
}
|
||||
Reference in New Issue
Block a user