<commit_before>/*
Lithium OS kernel initialisation

30/11/2013 - BMW:	Added more information printing as OS loads.
*/

#include <interrupt.h>
#include <print.h>
#include <timer.h>
#include <keyboard.h>
#include <pmmngr.h>
#include <vmmngr.h>
#include <stdinc.h>
#include <gdt.h>
#include <syscall.h>
#include <scheduler.h>
#include <kmalloc.h>

#define PAGEDIR_PHYSICAL_ADDRESS	0x00080000
#define PAGEDIR_VIRTUAL_ADDRESS		0xFFFFF000
#define VIDMEM_PHYSICAL_ADDRESS		0x000B8000
#define VIDMEM_VIRTUAL_ADDRESS		0xFFBFA000
#define MEMBITMAP_PHYSICAL_ADDRESS	0x00100000
#define MEMBITMAP_VIRTUAL_ADDRESS	0xFFBBA000
#define GDT_PHYSICAL_ADDRESS		0x00000000
#define GDT_VIRTUAL_ADDRESS			0xFFBB9000

struct memoryMapEntry
{
	uint32_t baseL; // Base address QWORD
	uint32_t baseH;
	uint32_t lengthL; // Length QWORD
	uint32_t lengthH;
	uint16_t type; // Region type
	uint16_t acpi; // Extended
	uint32_t padding; // Padding to make it 24 bytes
 
} __attribute__((__packed__));

void initialise_memory(void* ptrMemoryMap, uint32_t uiMemoryMapEntryCount);

void kmain(void *ptrMemoryMap, uint32_t memoryMapEntryCount)
{
	// Self-explanatory functions
	set_colour(0x0F);
	clear_screen();
	print_string("Lithium OS - Loading...\n");
	setup_interrupts();
	print_string("IDT installed\n");
	timer_install();
	keyboard_install();

	initialise_memory(ptrMemoryMap, memoryMapEntryCount);

	scheduler_setup_tss();

	// Install the system call interrupt handler
	irq_install_handler(2, call_handler);

	unsigned char TEST_BIN[] =
	{
		0x55, 0x89, 0xe5, 0x83, 0xe4, 0xf0, 0x83, 0xec, 0x10, 0xc7, 0x44, 0x24,
		0x04, 0xab, 0x00, 0x00, 0x00, 0xc7, 0x04, 0x24, 0x4f, 0x10, 0x00, 0x00,
		0xe8, 0x03, 0x00, 0x00, 0x00, 0xeb, 0xfe, 0x90, 0x55, 0x89, 0xe5, 0x53,
		0x83, 0xec, 0x14, 0x8b, 0x45, 0x0c, 0x88, 0x45, 0xe8, 0x8b, 0x45, 0x08,
		0x89, 0x45, 0xf8, 0x8a, 0x45, 0xe8, 0x88, 0x45, 0xf7, 0x8d, 0x45, 0xf7,
		0x89, 0x45, 0xf0, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x5d, 0xf0, 0xcd,
	 	0x22, 0x83, 0xc4, 0x14, 0x5b, 0x5d, 0xc3, 0x55, 0x73, 0x65, 0x72, 0x20,
		0x6d, 0x6f, 0x64, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d,
		0x21, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x01, 0x7a, 0x52, 0x00, 0x01, 0x7c, 0x08, 0x01, 0x1b, 0x0c, 0x04, 0x04,
		0x88, 0x01, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
		0x7c, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0e, 0x08,
		0x85, 0x02, 0x42, 0x0d, 0x05, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x01, 0x7a, 0x52, 0x00, 0x01, 0x7c, 0x08, 0x01,
		0x1b, 0x0c, 0x04, 0x04, 0x88, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
		0x1c, 0x00, 0x00, 0x00, 0x68, 0xff, 0xff, 0xff, 0x2f, 0x00, 0x00, 0x00,
		0x00, 0x41, 0x0e, 0x08, 0x85, 0x02, 0x42, 0x0d, 0x05, 0x44, 0x83, 0x03,
		0x66, 0xc3, 0x41, 0xc5, 0x0c, 0x04, 0x04, 0x00
	};

	unsigned char TEST2_BIN[] =
	{
		0x55, 0x89, 0xe5, 0x83, 0xe4, 0xf0, 0x83, 0xec, 0x10, 0xc7, 0x44, 0x24,
		0x04, 0x07, 0x00, 0x00, 0x00, 0xc7, 0x04, 0x24, 0x50, 0x10, 0x00, 0x00,
		0xe8, 0x03, 0x00, 0x00, 0x00, 0xeb, 0xfe, 0x90, 0x55, 0x89, 0xe5, 0x53,
		0x83, 0xec, 0x14, 0x8b, 0x45, 0x0c, 0x88, 0x45, 0xe8, 0x8b, 0x45, 0x08,
		0x89, 0x45, 0xf8, 0x8a, 0x45, 0xe8, 0x88, 0x45, 0xf7, 0x8d, 0x45, 0xf7,
		0x89, 0x45, 0xf0, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x5d, 0xf0, 0xcd,
		0x22, 0x83, 0xc4, 0x14, 0x5b, 0x5d, 0xc3, 0x00, 0x54, 0x68, 0x69, 0x73,
		0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x75, 0x73, 0x65, 0x72, 0x6d, 0x6f,
		0x64, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x2c, 0x20,
		0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x69,
		0x73, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20,
		0x61, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20, 0x63, 0x61, 0x6c,
		0x6c, 0x2e, 0x20, 0x3a, 0x44, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x01, 0x7a, 0x52, 0x00, 0x01, 0x7c, 0x08, 0x01,
		0x1b, 0x0c, 0x04, 0x04, 0x88, 0x01, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
		0x1c, 0x00, 0x00, 0x00, 0x48, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0x00,
		0x00, 0x41, 0x0e, 0x08, 0x85, 0x02, 0x42, 0x0d, 0x05, 0x00, 0x00, 0x00,
		0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7a, 0x52, 0x00,
		0x01, 0x7c, 0x08, 0x01, 0x1b, 0x0c, 0x04, 0x04, 0x88, 0x01, 0x00, 0x00,
		0x20, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff,
		0x2f, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0e, 0x08, 0x85, 0x02, 0x42, 0x0d,
		0x05, 0x44, 0x83, 0x03, 0x66, 0xc3, 0x41, 0xc5, 0x0c, 0x04, 0x04, 0x00
	};

	//uint32_t id = scheduler_add_process((void *)TEST_BIN, sizeof(TEST_BIN));

	//scheduler_add_thread(id, (void *)0x1000);

	uint32_t id = scheduler_add_process((void *)TEST2_BIN, sizeof(TEST2_BIN));

	//scheduler_add_thread(id, (void *)0x1000);

	// Interrupts are still disabled from the stage 2 bootloader
	enable_interrupts();

	while(1)
	{
		halt_cpu();
	}
}

void initialise_memory(void* ptrMemoryMap, uint32_t memoryMapEntryCount)
{
	uint32_t memSize = 0;

	struct memoryMapEntry* ptrMemoryMapEntry = (struct memoryMapEntry*)ptrMemoryMap;

	print_string("Parsing BIOS memory map... ");

	// Calculate the usable memory size from the BIOS memory map
	for(uint32_t i = 0; i < memoryMapEntryCount; i++)
	{
		// Any memory above 4GiB is being ignored since this is a 32-bit OS
		if(ptrMemoryMapEntry[i].baseH == 0)
			memSize += ptrMemoryMapEntry[i].lengthL;
	}

	print_string("done\n");
	
	// Initialise the physical memory manager
	pmmngr_init(memSize, (physical_addr)MEMBITMAP_PHYSICAL_ADDRESS);

	print_string("Physical memory manager initialised\n");

	// Process BIOS memory map
	for(uint32_t i = 0; i < memoryMapEntryCount; i++)
	{
		// Check if free for use or ACPI reclaimable
		if(ptrMemoryMapEntry[i].type == 1 || ptrMemoryMapEntry[i].type == 3)
		{
			// Initialise region - makes it free for use
			pmmngr_init_region(ptrMemoryMapEntry[i].baseL, ptrMemoryMapEntry[i].lengthL);
		}
	}

	// Calculate usable memory in KiB
	uint32_t usableMem = pmmngr_get_free_block_count() * PAGE_SIZE / 1024;
	char printBuf[12] = {0};
	itoa(usableMem, printBuf, 10);
	print_string("Initialised ");
	print_string(printBuf);
	print_string(" KiB of usable memory\n");

	// Calculate the location and size of the extended BIOS data area
	uint32_t ebdaBase = (uint32_t)(*(uint16_t*)0x040E) << 4;

	if(ebdaBase > 0xA0000)
		ebdaBase >>= 4;

	uint32_t ebdaLength = 0xA0000 - ebdaBase;
	
	// De-initialise regions that we have used
	pmmngr_deinit_region(0x00, 0x1000); // GDT
	pmmngr_deinit_region(0x7F000, 0x1000); // Kernel stack
	pmmngr_deinit_region(ebdaBase, ebdaLength); // EBDA
	pmmngr_deinit_region(0xA0000, 0x60000); // Video memory/ROM area
	pmmngr_deinit_region(MEMBITMAP_PHYSICAL_ADDRESS, 0x40000); // Physical memory manager bitmap
	pmmngr_deinit_region(PAGEDIR_PHYSICAL_ADDRESS, 0x4000); // Page directory and 3 page tables
	pmmngr_deinit_region(0x01000000, 0x64000); // The kernel (400KiB max)

	// Recursive paging - we can use physical address since first 4MiB is identity mapped
	pd_entry *pde =  vmmngr_pdirectory_lookup_entry((pdirectory *)PAGEDIR_PHYSICAL_ADDRESS, 0xFFFFFFFF);
	pd_entry_set_frame(pde, PAGEDIR_PHYSICAL_ADDRESS);
	pd_entry_add_attrib(pde, PDE_PRESENT);
	
	// Initialise virtual memory manager. If it fails print a message and halt system.
	if(!vmmngr_init((physical_addr)PAGEDIR_PHYSICAL_ADDRESS))
	{
		print_string("vmmngr_init() failed! System halted.");
		disable_interrupts();
		halt_cpu();
	}

	print_string("Virtual memory manager initialised\n");

	// Map video memory
	vmmngr_map_page(VIDMEM_PHYSICAL_ADDRESS, VIDMEM_VIRTUAL_ADDRESS);
	vmmngr_map_page(VIDMEM_PHYSICAL_ADDRESS + 4096, VIDMEM_VIRTUAL_ADDRESS + 4096);
	vmmngr_map_page(VIDMEM_PHYSICAL_ADDRESS + 8192, VIDMEM_VIRTUAL_ADDRESS + 8192);
	vmmngr_map_page(VIDMEM_PHYSICAL_ADDRESS + 12288, VIDMEM_VIRTUAL_ADDRESS + 12288);
	
	// Update the video memory pointer
	set_vid_mem((void *)VIDMEM_VIRTUAL_ADDRESS);
	
	// Map the memory management bitmap. The bitmap is 256KiB in size, so that's (256 / 4 = 64) pages.
	for(uint32_t i = 0; i < 64; i++)
	{
		vmmngr_map_page((physical_addr)(MEMBITMAP_PHYSICAL_ADDRESS + i * 4096),
			(virtual_addr)(MEMBITMAP_VIRTUAL_ADDRESS + i * 4096));
	}
	
	// Update the memory bitmap pointer
	pmmngr_set_bitmap_address((void *)MEMBITMAP_VIRTUAL_ADDRESS);
	
	// Map GDT
	vmmngr_map_page((physical_addr)GDT_PHYSICAL_ADDRESS, (virtual_addr)GDT_VIRTUAL_ADDRESS);

	// Set GDT address
	// TODO: set_gdt_address(GDT_VIRTUAL_ADDRESS);
	// ATM it is assuming the correct address
	
	// Setup GDT
	gdt_set_entry(0, 0x0000000000000000); // NULL descriptor.
	//								  limit		 base	 access  granularity
	gdt_set_entry(1, create_gdt_entry(0xFFFFF, 0x00000000, 0x9A, 0xC)); // Kernel code descriptor
	gdt_set_entry(2, create_gdt_entry(0xFFFFF, 0x00000000, 0x92, 0xC)); // Kernel data descriptor
	gdt_set_entry(3, create_gdt_entry(0xFFFFF, 0x00000000, 0xFA, 0xC)); // User code descriptor
	gdt_set_entry(4, create_gdt_entry(0xFFFFF, 0x00000000, 0xF2, 0xC)); // User data descriptor
	
	// Load GDT with 5 entries
	load_gdt(5);

	print_string("GDT updated\n");
	
	// Un-identity-map first 4MiB
	pde =  vmmngr_pdirectory_lookup_entry((pdirectory*)PAGEDIR_VIRTUAL_ADDRESS, 0x00000000);
	pd_entry_del_attrib(pde, PDE_PRESENT);

	// The first page table (used for first 4MiB) was at 0x81000, so we can free that memory now<commit_msg>Various minor changes<commit_after>/*
Lithium OS kernel initialisation

30/11/2013 - BMW:	Added more information printing as OS loads.
*/

#include <interrupt.h>
#include <print.h>
#include <timer.h>
#include <keyboard.h>
#include <pmmngr.h>
#include <vmmngr.h>
#include <stdinc.h>
#include <gdt.h>
#include <syscall.h>
#include <scheduler.h>
#include <kmalloc.h>

#define PAGEDIR_PHYSICAL_ADDRESS	0x00080000
#define PAGEDIR_VIRTUAL_ADDRESS		0xFFFFF000
#define VIDMEM_PHYSICAL_ADDRESS		0x000B8000
#define VIDMEM_VIRTUAL_ADDRESS		0xFFBFA000
#define MEMBITMAP_PHYSICAL_ADDRESS	0x00100000
#define MEMBITMAP_VIRTUAL_ADDRESS	0xFFBBA000
#define GDT_PHYSICAL_ADDRESS		0x00000000
#define GDT_VIRTUAL_ADDRESS			0xFFBB9000

struct memoryMapEntry
{
	uint32_t baseL; // Base address QWORD
	uint32_t baseH;
	uint32_t lengthL; // Length QWORD
	uint32_t lengthH;
	uint16_t type; // Region type
	uint16_t acpi; // Extended
	uint32_t padding; // Padding to make it 24 bytes
 
} __attribute__((__packed__));

void initialise_memory(void *ptrMemoryMap, uint32_t uiMemoryMapEntryCount);

void kmain(void *ptrMemoryMap, uint32_t memoryMapEntryCount)
{
	// Self-explanatory functions
	set_colour(0x0F);
	clear_screen();
	print_string("Lithium OS - Loading...\n");
	setup_interrupts();
	print_string("IDT installed\n");
	timer_install();
	keyboard_install();

	initialise_memory(ptrMemoryMap, memoryMapEntryCount);

	scheduler_setup_tss();

	// Install the system call interrupt handler
	irq_install_handler(2, call_handler);

	unsigned char TEST_BIN[] =
	{
		0x55, 0x89, 0xe5, 0x83, 0xe4, 0xf0, 0x83, 0xec, 0x10, 0xc7, 0x44, 0x24,
		0x04, 0xab, 0x00, 0x00, 0x00, 0xc7, 0x04, 0x24, 0x4f, 0x10, 0x00, 0x00,
		0xe8, 0x03, 0x00, 0x00, 0x00, 0xeb, 0xfe, 0x90, 0x55, 0x89, 0xe5, 0x53,
		0x83, 0xec, 0x14, 0x8b, 0x45, 0x0c, 0x88, 0x45, 0xe8, 0x8b, 0x45, 0x08,
		0x89, 0x45, 0xf8, 0x8a, 0x45, 0xe8, 0x88, 0x45, 0xf7, 0x8d, 0x45, 0xf7,
		0x89, 0x45, 0xf0, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x5d, 0xf0, 0xcd,
	 	0x22, 0x83, 0xc4, 0x14, 0x5b, 0x5d, 0xc3, 0x55, 0x73, 0x65, 0x72, 0x20,
		0x6d, 0x6f, 0x64, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d,
		0x21, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x01, 0x7a, 0x52, 0x00, 0x01, 0x7c, 0x08, 0x01, 0x1b, 0x0c, 0x04, 0x04,
		0x88, 0x01, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
		0x7c, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0e, 0x08,
		0x85, 0x02, 0x42, 0x0d, 0x05, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x01, 0x7a, 0x52, 0x00, 0x01, 0x7c, 0x08, 0x01,
		0x1b, 0x0c, 0x04, 0x04, 0x88, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
		0x1c, 0x00, 0x00, 0x00, 0x68, 0xff, 0xff, 0xff, 0x2f, 0x00, 0x00, 0x00,
		0x00, 0x41, 0x0e, 0x08, 0x85, 0x02, 0x42, 0x0d, 0x05, 0x44, 0x83, 0x03,
		0x66, 0xc3, 0x41, 0xc5, 0x0c, 0x04, 0x04, 0x00
	};

	unsigned char TEST2_BIN[] =
	{
		0x55, 0x89, 0xe5, 0x83, 0xe4, 0xf0, 0x83, 0xec, 0x10, 0xc7, 0x44, 0x24,
		0x04, 0x07, 0x00, 0x00, 0x00, 0xc7, 0x04, 0x24, 0x50, 0x10, 0x00, 0x00,
		0xe8, 0x03, 0x00, 0x00, 0x00, 0xeb, 0xfe, 0x90, 0x55, 0x89, 0xe5, 0x53,
		0x83, 0xec, 0x14, 0x8b, 0x45, 0x0c, 0x88, 0x45, 0xe8, 0x8b, 0x45, 0x08,
		0x89, 0x45, 0xf8, 0x8a, 0x45, 0xe8, 0x88, 0x45, 0xf7, 0x8d, 0x45, 0xf7,
		0x89, 0x45, 0xf0, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x5d, 0xf0, 0xcd,
		0x22, 0x83, 0xc4, 0x14, 0x5b, 0x5d, 0xc3, 0x00, 0x54, 0x68, 0x69, 0x73,
		0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x75, 0x73, 0x65, 0x72, 0x6d, 0x6f,
		0x64, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x2c, 0x20,
		0x70, 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x74, 0x68, 0x69,
		0x73, 0x20, 0x74, 0x65, 0x78, 0x74, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20,
		0x61, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20, 0x63, 0x61, 0x6c,
		0x6c, 0x2e, 0x20, 0x3a, 0x44, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x01, 0x7a, 0x52, 0x00, 0x01, 0x7c, 0x08, 0x01,
		0x1b, 0x0c, 0x04, 0x04, 0x88, 0x01, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
		0x1c, 0x00, 0x00, 0x00, 0x48, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0x00,
		0x00, 0x41, 0x0e, 0x08, 0x85, 0x02, 0x42, 0x0d, 0x05, 0x00, 0x00, 0x00,
		0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7a, 0x52, 0x00,
		0x01, 0x7c, 0x08, 0x01, 0x1b, 0x0c, 0x04, 0x04, 0x88, 0x01, 0x00, 0x00,
		0x20, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff,
		0x2f, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0e, 0x08, 0x85, 0x02, 0x42, 0x0d,
		0x05, 0x44, 0x83, 0x03, 0x66, 0xc3, 0x41, 0xc5, 0x0c, 0x04, 0x04, 0x00
	};

	//uint32_t id = scheduler_add_process((void *)TEST_BIN, sizeof(TEST_BIN));

	//scheduler_add_thread(id, (void *)0x1000);

	uint32_t id = scheduler_add_process((void *)TEST2_BIN, sizeof(TEST2_BIN));

	if(id == 0)
		print_string("Adding process failed!\n");
	else
	{
		char buf[12] = {0};
		itoa(id, buf, 10);

		print_string("Process with ID ");
		print_string(buf);
		print_string(" added\n");

		id = scheduler_add_thread(id, (void *)0x1000);

		itoa(id, buf, 10);

		print_string("Thread with ID ");
		print_string(buf);
		print_string(" added\n");
	}

	// Interrupts are still disabled from the stage 2 bootloader
	enable_interrupts();

	while(1)
	{
		halt_cpu();
	}
}

void initialise_memory(void *ptrMemoryMap, uint32_t memoryMapEntryCount)
{
	uint32_t memSize = 0;

	struct memoryMapEntry* ptrMemoryMapEntry = (struct memoryMapEntry *)ptrMemoryMap;

	print_string("Parsing BIOS memory map... ");

	// Calculate the usable memory size from the BIOS memory map
	for(uint32_t i = 0; i < memoryMapEntryCount; i++)
	{
		// Any memory above 4GiB is being ignored since this is a 32-bit OS
		if(ptrMemoryMapEntry[i].baseH == 0)
			memSize += ptrMemoryMapEntry[i].lengthL;
	}

	print_string("done\n");
	
	// Initialise the physical memory manager
	pmmngr_init(memSize, (physical_addr)MEMBITMAP_PHYSICAL_ADDRESS);

	print_string("Physical memory manager initialised\n");

	// Process BIOS memory map
	for(uint32_t i = 0; i < memoryMapEntryCount; i++)
	{
		// Check if free for use or ACPI reclaimable
		if(ptrMemoryMapEntry[i].type == 1 || ptrMemoryMapEntry[i].type == 3)
		{
			// Initialise region - makes it free for use
			pmmngr_init_region(ptrMemoryMapEntry[i].baseL, ptrMemoryMapEntry[i].lengthL);
		}
	}

	// Calculate usable memory in KiB
	uint32_t usableMem = pmmngr_get_free_block_count() * PAGE_SIZE / 1024;
	char printBuf[12] = {0};
	itoa(usableMem, printBuf, 10);
	print_string("Initialised ");
	print_string(printBuf);
	print_string(" KiB of usable memory\n");

	// Calculate the location and size of the extended BIOS data area
	uint32_t ebdaBase = (uint32_t)(*(uint16_t*)0x040E) << 4;

	if(ebdaBase > 0xA0000)
		ebdaBase >>= 4;

	uint32_t ebdaLength = 0xA0000 - ebdaBase;
	
	// De-initialise regions that we have used
	pmmngr_deinit_region(0x00, 0x1000); // GDT
	pmmngr_deinit_region(0x7F000, 0x1000); // Kernel stack
	pmmngr_deinit_region(ebdaBase, ebdaLength); // EBDA
	pmmngr_deinit_region(0xA0000, 0x60000); // Video memory/ROM area
	pmmngr_deinit_region(MEMBITMAP_PHYSICAL_ADDRESS, 0x40000); // Physical memory manager bitmap
	pmmngr_deinit_region(PAGEDIR_PHYSICAL_ADDRESS, 0x4000); // Page directory and 3 page tables
	pmmngr_deinit_region(0x01000000, 0x64000); // The kernel (400KiB max)

	// Recursive paging - we can use physical address since first 4MiB is identity mapped
	pd_entry *pde =  vmmngr_pdirectory_lookup_entry((pdirectory *)PAGEDIR_PHYSICAL_ADDRESS, 0xFFFFFFFF);
	pd_entry_set_frame(pde, PAGEDIR_PHYSICAL_ADDRESS);
	pd_entry_add_attrib(pde, PDE_PRESENT);
	
	// Initialise virtual memory manager. If it fails print a message and halt system.
	if(!vmmngr_init((physical_addr)PAGEDIR_PHYSICAL_ADDRESS))
	{
		print_string("vmmngr_init() failed! System halted.");
		disable_interrupts();
		halt_cpu();
	}

	print_string("Virtual memory manager initialised\n");

	// Map video memory
	vmmngr_map_page(VIDMEM_PHYSICAL_ADDRESS, VIDMEM_VIRTUAL_ADDRESS);
	vmmngr_map_page(VIDMEM_PHYSICAL_ADDRESS + 4096, VIDMEM_VIRTUAL_ADDRESS + 4096);
	vmmngr_map_page(VIDMEM_PHYSICAL_ADDRESS + 8192, VIDMEM_VIRTUAL_ADDRESS + 8192);
	vmmngr_map_page(VIDMEM_PHYSICAL_ADDRESS + 12288, VIDMEM_VIRTUAL_ADDRESS + 12288);
	
	// Update the video memory pointer
	set_vid_mem((void *)VIDMEM_VIRTUAL_ADDRESS);
	
	// Map the memory management bitmap. The bitmap is 256KiB in size, so that's (256 / 4 = 64) pages.
	for(uint32_t i = 0; i < 64; i++)
	{
		vmmngr_map_page((physical_addr)(MEMBITMAP_PHYSICAL_ADDRESS + i * 4096),
			(virtual_addr)(MEMBITMAP_VIRTUAL_ADDRESS + i * 4096));
	}
	
	// Update the memory bitmap pointer
	pmmngr_set_bitmap_address((void *)MEMBITMAP_VIRTUAL_ADDRESS);
	
	// Map GDT
	vmmngr_map_page((physical_addr)GDT_PHYSICAL_ADDRESS, (virtual_addr)GDT_VIRTUAL_ADDRESS);

	// Set GDT address
	// TODO: set_gdt_address(GDT_VIRTUAL_ADDRESS);
	// ATM it is assuming the correct address
	
	// Setup GDT
	gdt_set_entry(0, 0x0000000000000000); // NULL descriptor.
	//								  limit		 base	 access  granularity
	gdt_set_entry(1, create_gdt_entry(0xFFFFF, 0x00000000, 0x9A, 0xC)); // Kernel code descriptor
	gdt_set_entry(2, create_gdt_entry(0xFFFFF, 0x00000000, 0x92, 0xC)); // Kernel data descriptor
	gdt_set_entry(3, create_gdt_entry(0xFFFFF, 0x00000000, 0xFA, 0xC)); // User code descriptor
	gdt_set_entry(4, create_gdt_entry(0xFFFFF, 0x00000000, 0xF2, 0xC)); // User data descriptor
	
	// Load GDT with 5 entries
	load_gdt(5);

	print_string("GDT updated\n");
	
	// Un-identity-map first 4MiB
	pde =  vmmngr_pdirectory_lookup_entry((pdirectory *)PAGEDIR_VIRTUAL_ADDRESS, 0x00000000);
	pd_entry_del_attrib(pde, PDE_PRESENT);

	// The first page table (used for first 4MiB) was at 0x81000, so we can free that memory now
{'fraction_non_alphanumeric': 0.12289207419898819, 'fraction_numerical': 0.17221753794266442, 'mean_word_length': 4.893478260869565, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 16, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3354851', 'n_tokens_mistral': 11371, 'n_tokens_neox': 8953, 'n_words': 2055}