Compare commits

...

5 Commits

Author SHA1 Message Date
e9c79f1d6a
Adjust bounds for page table filling. Some maths was wrong 2020-12-02 02:37:17 +00:00
69eb6e10c8
Separate out ELF header parsing to another file 2020-12-02 02:37:17 +00:00
Curle
13139779f2 Fix formatting in templates 2020-11-29 21:24:00 +00:00
Curle
2ae1c75019 Update issue templates 2020-11-29 21:22:39 +00:00
Curle
f9a6171f0a
Create LICENSE 2020-11-29 21:12:49 +00:00
12 changed files with 879 additions and 69 deletions

18
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,18 @@
---
name: Bug report
about: Tell us about something gone wrong
title: ''
labels: bug
assignees: TheCurle
---
**What went wrong?**
We need to know what the actual problem to be solved is - obviously!
**How can we recreate the issue?**
Having solid steps to reproduce the problem helps a ton!
Please put it in terms of "Step 1: Boot, Step 2: Press this combination of keys: `alt + F20 + J`..."
**What do you think went wrong?**
If you have any vague suggestions or have investigated the issue and know roughly what's wrong, feel free to put it here. Otherwise, don't worry.

View File

@ -0,0 +1,16 @@
---
name: Feature request
about: Got a cool idea for new functionality?
title: ''
labels: enhancement
assignees: TheCurle
---
**Describe the feature**
What's the actual feature? How would it be activated? What would it allow you to do?
What's the benefit, and why is it worth the work required?
**Alternatives?**
If the feature cannot be added for whatever reason, is there a reasonable compromise you have in mind?
Who knows, this alternative may be introduced alongside the main feature.

View File

@ -24,6 +24,7 @@ SET(src_files
${CMAKE_SOURCE_DIR}/chroma/system/memory/abstract_allocator.c ${CMAKE_SOURCE_DIR}/chroma/system/memory/abstract_allocator.c
${CMAKE_SOURCE_DIR}/chroma/system/memory/physmem.c ${CMAKE_SOURCE_DIR}/chroma/system/memory/physmem.c
${CMAKE_SOURCE_DIR}/chroma/system/drivers/keyboard.c ${CMAKE_SOURCE_DIR}/chroma/system/drivers/keyboard.c
${CMAKE_SOURCE_DIR}/chroma/system/drivers/elf.c
) )
SET(lib_files SET(lib_files

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Curle
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -11,6 +11,30 @@
extern "C" { extern "C" {
#endif #endif
/* ELF headers hate me.
* Let's do this the hard way. */
#define ELF_IDENT_MAGIC_OFF 0x0
#define ELF_IDENT_CLASS_OFF 0x4
#define ELF_IDENT_DATA_OFF 0x5
#define ELF_IDENT_VERSION_OFF 0x6
#define ELF_IDENT_OSABI_OFF 0x7
#define ELF_IDENT_ABI_VERSION_OFF 0x8
#define ELF_IDENT_PAD_OFF 0x9
#define ELFTYPE_OFF 0x10
#define ELFMACHINE_OFF 0x12
#define ELFVERSION_OFF 0x14
#define ELFENTRY_OFF 0x18
#define ELFPHOFF_OFF 0x20
#define ELFSHOFF_OFF 0x28
#define ELFFLAGS_OFF 0x30
#define ELFEHSIZE_OFF 0x34
#define ELFPHENTSIZE_OFF 0x36
#define ELFPHNUM_OFF 0x38
#define ELFSHENTSIZE_OFF 0x3A
#define ELFSHNUM_OFF 0x3C
#define ELFSHSTRNDX_OFF 0x40
#define BOOT_MAGIC "BOOT" #define BOOT_MAGIC "BOOT"
/* minimum protocol level: /* minimum protocol level:
@ -108,30 +132,6 @@ typedef struct {
} __attribute__((packed)) bootinfo; } __attribute__((packed)) bootinfo;
typedef struct {
uint32_t Magic;
uint8_t Class;
uint8_t Endianness;
uint8_t Version;
uint8_t ABI;
uint8_t ABIVersion;
uint8_t Unused[7];
uint16_t Type;
uint16_t TargetArchitecture;
uint32_t ELFVersion;
size_t EntryPoint;
size_t ProgramHeadersTable;
size_t SectionHeadersTable;
uint32_t Flags;
uint16_t ELFHeaderSize;
uint16_t ProgramHeadersEntrySize;
uint16_t ProgramHeaderEntries;
uint16_t SectionHeadersEntrySize;
uint16_t SectionHeaderEntries;
uint16_t SectionHeaderNameEntry;
uint8_t End;
} __attribute__((packed)) ELF64Header_t;
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -69,6 +69,8 @@ void InitPrint();
void SetupInitialGDT(); void SetupInitialGDT();
void SetupIDT(); void SetupIDT();
int ParseKernelHeader(size_t InitrdPtr);
int Main(); int Main();
void Exit(); void Exit();

View File

@ -8,8 +8,6 @@
*** Chroma *** *** Chroma ***
***********************/ ***********************/
size_t KernelLocation;
/************************************************ /************************************************
* C O N S T A N T S A N D M A C R O S * C O N S T A N T S A N D M A C R O S
@ -30,6 +28,11 @@ size_t KernelLocation;
#define REINTERPRET_CAST(target, intermediate, value) ((target*)((intermediate*)value)) #define REINTERPRET_CAST(target, intermediate, value) ((target*)((intermediate*)value))
#define FIXENDIAN64(x) __builtin_bswap64(x)
#define FIXENDIAN32(x) __builtin_bswap32(x)
#define FIXENDIAN16(x) __builtin_bswap16(x)
#define CONCAT(x, y) x ## y #define CONCAT(x, y) x ## y
#define CONCAT2(x, y) CONCAT(x, y) #define CONCAT2(x, y) CONCAT(x, y)
#define ASSERT(exp, error) \ #define ASSERT(exp, error) \
@ -54,7 +57,8 @@ size_t KernelLocation;
#define ERR_RESERVED 0x8 #define ERR_RESERVED 0x8
#define ERR_INST 0x10 #define ERR_INST 0x10
#define ELF64MAGIC 0x7F454c46 #define ELF64MAGIC 0x7F454c46
#define ELF64MAGICBE 0x464c457F
/* /*
@ -106,7 +110,7 @@ size_t KernelLocation;
#define MMIO_REGION 0xFFFFFFFFF8000000ull // Cannot move! #define MMIO_REGION 0xFFFFFFFFF8000000ull // Cannot move!
#define FB_REGION 0xFFFFFFFFFC000000ull // Cannot move! #define FB_REGION 0xFFFFFFFFFC000000ull // Cannot move!
#define FB_PHYSICAL 0x00000000E0000000ull // Physical location of the Framebuffer #define FB_PHYSICAL 0x00000000FD000000ull // Physical location of the Framebuffer
#define KERNEL_REGION 0xFFFFFFFFFFE00000ull // -2MiB, from bootloader #define KERNEL_REGION 0xFFFFFFFFFFE00000ull // -2MiB, from bootloader
#define USER_REGION 0x00007FFFFFFFFFFFull // Not needed yet, but we're higher half so we might as well be thorough #define USER_REGION 0x00007FFFFFFFFFFFull // Not needed yet, but we're higher half so we might as well be thorough

View File

@ -20,48 +20,13 @@ address_space_t KernelAddressSpace;
int Main(void) { int Main(void) {
KernelAddressSpace = (address_space_t) {0}; KernelAddressSpace = (address_space_t) {0};
KernelLocation = 0x112600;
SerialPrintf("\r\n[ boot] Booting Chroma..\r\n"); SerialPrintf("\r\n[ boot] Booting Chroma..\r\n");
SerialPrintf("[ boot] Kernel loaded at 0x%p, ends at 0x%p, is %d bytes long.\r\n", KernelAddr, KernelEnd, KernelEnd - KernelAddr); SerialPrintf("[ boot] Kernel loaded at 0x%p, ends at 0x%p, is %d bytes long.\r\n", KernelAddr, KernelEnd, KernelEnd - KernelAddr);
SerialPrintf("[ boot] Initrd is physically at 0x%p, and is %d bytes long.\r\n", bootldr.initrd_ptr, bootldr.initrd_size); SerialPrintf("[ boot] Initrd is physically at 0x%p, and is %d bytes long.\r\n", bootldr.initrd_ptr, bootldr.initrd_size);
SerialPrintf("[ boot] Initrd's header is 0x%p\r\n", FIXENDIAN32(*((volatile uint32_t*)(bootldr.initrd_ptr))));
SerialPrintf("[ boot] Searching for kernel... Constants start at 0x%p\r\n", &_kernel_text_start); ParseKernelHeader(bootldr.initrd_ptr);
// We stop at the constants in the kernel, otherwise we'll read the constant ELF64MAGIC which is stored inside the kernel...
size_t headerLoc = 0;
for(size_t i = KernelAddr; i < KernelEnd; i++) {
if(i < (size_t) (&_kernel_text_start) - KernelAddr) {
if(*((volatile uint32_t*)(i)) == ELF64MAGIC) {
SerialPrintf("[ boot] Matched kernel header at 0x%p.\r\n", i);
}
}
}
int flag = 0;
if(headerLoc) {
ELF64Header_t* PotentialKernelHeader = (ELF64Header_t*) &headerLoc;
SerialPrintf(
"[ boot] Considering ELF with:\r\n\tBitness %d\r\n\tEntry point 0x%p\r\n\tFile type %s : %d\r\n\tArchitecture %s : %d\r\n",
PotentialKernelHeader->Class == 2 ? 64 : 32, PotentialKernelHeader->EntryPoint, PotentialKernelHeader->Type == 0x02 ? "EXECUTABLE" : "OTHER", PotentialKernelHeader->Type, PotentialKernelHeader->TargetArchitecture == 0x3E ? "AMD64" : "OTHER", PotentialKernelHeader->TargetArchitecture);
if(PotentialKernelHeader->EntryPoint == KernelAddr) {
SerialPrintf("[ boot] Header at 0x%p matches kernel header.\r\n", headerLoc);
flag = 1;
}
if(!flag) {
for(size_t i = 0; i < 8; i++) {
SerialPrintf("[ boot] Header dump part %d: 0x%x\r\n", i, *((volatile uint32_t*)(headerLoc + i)));
}
}
}
if(!flag) {
SerialPrintf("[ boot] Unable to find kernel in memory. Fatal error.\r\n");
//for(;;) {}
}
SerialPrintf("[ boot] The bootloader has put the paging tables at 0x%p.\r\n", ReadControlRegister(3)); SerialPrintf("[ boot] The bootloader has put the paging tables at 0x%p.\r\n", ReadControlRegister(3));

View File

@ -0,0 +1,91 @@
#include <kernel/chroma.h>
/************************
*** Team Kitty, 2020 ***
*** Chroma ***
***********************/
/*
* This file provides utility functions for parsing ELF headers.
* This exists so that the kernel can find itself for remapping,
* but I may end up using ELF as the kernel's executable format.
* Writing an ELF loader is on the to-do list, after all.
! This needs to be cleaned up.
! This creates a mess of numbers on the print
! This is hacky and hardcoded as heck and needs to be fixed
*/
extern size_t KernelLocation;
int ParseKernelHeader(size_t InitrdPtr) {
int flag = 0;
SerialPrintf("[ boot] Searching for kernel... Constants start at 0x%p / 0x%p\r\n", ((size_t) (&_kernel_text_start) - KernelAddr) + InitrdPtr, (size_t) (&_kernel_text_start));
// We stop at the constants in the kernel, otherwise we'll read the constant ELF64MAGIC which is stored inside the kernel...
size_t headerLoc = 0;
for(size_t i = InitrdPtr; i < ((size_t) (&_kernel_text_start) - KernelAddr) + InitrdPtr; i++) {
if(*((volatile uint32_t*)(i)) == ELF64MAGIC) {
SerialPrintf("[ boot] Matched kernel header at 0x%p.\r\n", i);
headerLoc = i;
}
if(FIXENDIAN32(*((volatile uint32_t*)(i))) == ELF64MAGIC) {
SerialPrintf("[ boot] Matched little-endian kernel header at 0x%p.\r\n", i);
headerLoc = i;
}
}
if(headerLoc) {
/* For whatever reason, reading a size_t here fails. The max that seems to work is uint16_t, so we read in the
* 64 bit address by constructing it from 4 individual reads.
* Note that these 4 reads are little endian, so we need to flip them around individually
*/
uint16_t EntryPoint0 = FIXENDIAN16(*((volatile uint16_t*)(headerLoc + ELFENTRY_OFF)));
uint16_t EntryPoint1 = FIXENDIAN16(*((volatile uint16_t*)(headerLoc + ELFENTRY_OFF + 2)));
uint16_t EntryPoint2 = FIXENDIAN16(*((volatile uint16_t*)(headerLoc + ELFENTRY_OFF + 4)));
uint16_t EntryPoint3 = FIXENDIAN16(*((volatile uint16_t*)(headerLoc + ELFENTRY_OFF + 6)));
size_t EntryPoint = ((size_t) EntryPoint0 << 48) | ((size_t) EntryPoint1 << 32) | ((size_t) EntryPoint2 << 16) | ((size_t) EntryPoint3);
/* At this point, EntryPoint is a little-endian 64 bit integer. That means we have to fix its endianness in order to read it */
SerialPrintf("[ boot] Fixing entry point from 0x%p to 0x%p\r\n", EntryPoint, FIXENDIAN64(EntryPoint));
EntryPoint = FIXENDIAN64(EntryPoint);
/* Now we can continue as normal */
uint8_t HeaderClass = *((volatile uint8_t*)(headerLoc + ELF_IDENT_CLASS_OFF));
uint16_t ExecutableType = (uint16_t) *((volatile uint8_t*)(headerLoc + ELFTYPE_OFF));
uint16_t MachineType = (uint16_t) *((volatile uint8_t*)(headerLoc + ELFMACHINE_OFF));
SerialPrintf(
"[ boot] ELF header at 0x%p.\r\n\tConsidering ELF with:\r\n\tBitness %d: %d\r\n\tEntry point 0x%p\r\n\tFile type %s : 0x%x\r\n\tArchitecture %s : 0x%x\r\n",
headerLoc,
HeaderClass == 2 ? 64 : 32,
HeaderClass,
EntryPoint,
ExecutableType == FIXENDIAN16(0x0200) ? "EXECUTABLE" : "OTHER",
FIXENDIAN16(ExecutableType),
MachineType == FIXENDIAN16(0x3E00) ? "AMD64" : "OTHER",
FIXENDIAN16(MachineType));
if(EntryPoint == (size_t) (&_kernel_text_start)) {
SerialPrintf("[ boot] Header at 0x%p matches kernel header.\r\n", headerLoc);
flag = 1;
// At this point, we've found the right ELF64 executable!
// Great, now we can map it into the proper place
KernelLocation = headerLoc;
}
if(!flag) {
for(char i = 0; i < 64; i++) {
SerialPrintf("[ boot] Header dump part %x: 0x%x\r\n", i, *((volatile uint8_t*)(headerLoc + i)));
}
}
}
return flag;
}

View File

@ -52,6 +52,8 @@
extern size_t _kernel_rodata_start; extern size_t _kernel_rodata_start;
extern size_t _kernel_data_start; extern size_t _kernel_data_start;
size_t KernelLocation;
//__attribute__((aligned(4096))) static size_t Pagetable[512] = {0}; //__attribute__((aligned(4096))) static size_t Pagetable[512] = {0};
#define LAST_ENTRY 0xFF8 #define LAST_ENTRY 0xFF8
@ -151,8 +153,9 @@ void InitPaging() {
//TODO: Disallow execution of rodata and data, and bootldr/environment //TODO: Disallow execution of rodata and data, and bootldr/environment
for(void* Address = CAST(void*, KERNEL_REGION); for(void* Address = CAST(void*, KERNEL_REGION);
Address < CAST(void*, KERNEL_REGION + (KernelEnd - KernelAddr)); // Lower half of Kernel Address < CAST(void*, KERNEL_REGION + (KernelEnd - KernelAddr));
Address = CAST(void*, CAST(char*, Address) + PAGE_SIZE)) { Address = CAST(void*, CAST(char*, Address) + PAGE_SIZE)) {
SerialPrintf("[ mem] Mapping 0x%p to 0x%p, relative to kernel at 0x%p\r\n", (CAST(size_t, Address) - KERNEL_REGION) + KernelLocation, Address, (CAST(size_t, Address) - KERNEL_REGION));
MapVirtualMemory(&KernelAddressSpace, Address, (CAST(size_t, Address) - KERNEL_REGION) + KernelLocation, MAP_EXEC); MapVirtualMemory(&KernelAddressSpace, Address, (CAST(size_t, Address) - KERNEL_REGION) + KernelLocation, MAP_EXEC);
} }
@ -161,9 +164,9 @@ void InitPaging() {
Address = CAST(void*, CAST(char*, Address) + PAGE_SIZE)) { Address = CAST(void*, CAST(char*, Address) + PAGE_SIZE)) {
MapVirtualMemory(&KernelAddressSpace, Address, (CAST(size_t, Address) - KERNEL_REGION) + KERNEL_PHYSICAL_2, MAP_EXEC); MapVirtualMemory(&KernelAddressSpace, Address, (CAST(size_t, Address) - KERNEL_REGION) + KERNEL_PHYSICAL_2, MAP_EXEC);
}*/ }*/
SerialPrintf("[ mem] Framebuffer at 0x%p, is 0x%p long. Mapping to 0x%p.\r\n", bootldr.fb_ptr, bootldr.fb_size, FB_REGION);
for(void* Address = CAST(void*, FB_REGION); for(void* Address = CAST(void*, FB_REGION);
Address < CAST(void*, 0x200000); // TODO: Turn this into a calculation with bootldr.fb_size Address < CAST(void*, bootldr.fb_size + FB_REGION);
Address = CAST(void*, CAST(char*, Address) + PAGE_SIZE)) { Address = CAST(void*, CAST(char*, Address) + PAGE_SIZE)) {
MapVirtualMemory(&KernelAddressSpace, Address, (CAST(size_t, Address) - FB_REGION) + FB_PHYSICAL, MAP_WRITE); MapVirtualMemory(&KernelAddressSpace, Address, (CAST(size_t, Address) - FB_REGION) + FB_PHYSICAL, MAP_WRITE);
} }

View File

@ -0,0 +1,310 @@
void InitPagingT() {
size_t* PML4 = (size_t*) 0xFFA000; // Layer 4
size_t* PDPE_RAM = (size_t*) 0xFFE000; // Layer 3, contains map for the first 4GB of RAM
size_t* PDE_RAM = (size_t*) 0xFFF000;
size_t* PDPE_KERNEL = (size_t*) 0xFFB000; // Layer 3, contains map for the Kernel and everything it needs to run.
size_t* PDE_KERNEL_FB = (size_t*) 0xFFC000; // Layer 2, contains map for the linear framebuffer.
size_t* PT_KERNEL = (size_t*) 0xFFD000; // Layer 1, the page table for the kernel itself.
size_t fb_ptr = (size_t) &fb;
SET_ADDRESS(PML4, PDPE_RAM); // 3rd Layer entry for RAM
SET_ADDRESS(PML4 + LAST_ENTRY, PDPE_KERNEL); // 3rd Layer entry for Kernel
SET_ADDRESS(PDPE_KERNEL + LAST_ENTRY, PDE_KERNEL_FB); // 2nd Layer entry for the framebuffer
// Set the 480th entry (PDE_KERNEL_FB + (480 * 8))
// To the framebuffer + flags
SET_ADDRESS(PDE_KERNEL_FB + 3840, USERWRITEABLE_FLAGS(fb_ptr));
// In 4 byte increments, we're gonna map 3840 (the framebuffer)
// Up to (4096 - 8) in the PDE_KERNEL_FB with 2MB paging.
size_t MappingIterations = 1;
for(size_t i = 3844; i < 4088; i += 4) {
SET_ADDRESS(PDE_KERNEL_FB + i, USERWRITEABLE_FLAGS(fb_ptr) + (MappingIterations * (2 * MiB)));
MappingIterations++;
}
// Now we map the last entry of PDE_KERNEL_FB to our Page Table
SET_ADDRESS(PDE_KERNEL_FB + LAST_ENTRY, PT_KERNEL);
// Mapping the kernel into the page tables....
SET_ADDRESS(PT_KERNEL, 0xFF8001); // bootldr, bootinfo
SET_ADDRESS(PT_KERNEL + 8, 0xFF9001); // environment
// Map the kernel itself
SET_ADDRESS(PT_KERNEL + 16, KernelAddr + 1);
// Iterate through the pages, identity mapping each one
MappingIterations = 1;
size_t MappingOffset = 0x14;
for(size_t i = 0; i < ((KernelEnd - KernelAddr) >> 12); i++) {
// Page Table + (0x10 increasing by 0x04 each time) = x * 4KiB
SET_ADDRESS(PT_KERNEL + MappingOffset, (MappingIterations * (4 * KiB)));
MappingOffset += 4;
MappingIterations++;
}
// Now we need to map the core stacks. Top-down, from 0xDFF8
// There's always at least one core, so we do that one fixed.
// TODO: Account for 0-core CPUs
SET_ADDRESS(PT_KERNEL + LAST_ENTRY, 0xF14003);
MappingIterations = 1;
// For every core:
for(size_t i = 0; i < (bootldr.numcores + 3U) >> 2; i++) {
// PT_KERNEL[512 - (iterations + 1)] = 0x14003 + (iterations * page-width)
SET_ADDRESS(PT_KERNEL + LAST_ENTRY - (MappingIterations * 8), 0xF14003 + (4096 * MappingIterations));
MappingIterations++;
}
SET_ADDRESS(PDPE_RAM, PDE_RAM + PAGE_PRESENT + PAGE_RW);
SET_ADDRESS(PDPE_RAM + 8, 0xF10000 + PAGE_PRESENT + PAGE_RW);
SET_ADDRESS(PDPE_RAM + 16, 0xF11000 + PAGE_PRESENT + PAGE_RW);
SET_ADDRESS(PDPE_RAM + 24, 0xF12000 + PAGE_PRESENT + PAGE_RW);
// Identity map 4GB of ram
// Each page table can only hold 512 entries, but we
// just set up 4 of them - overflowing PDE_RAM (0xF000)
// will take us into 0x10000, into 0x11000, into 0x120000.
for(size_t i = 0; i < 512 * 4/*GB*/; i++) {
// add PDE_RAM, 4
// mov eax, 0x83
// add eax, 2*1024*1024
SET_ADDRESS(PDE_RAM + (i * 4), USERWRITEABLE_FLAGS(i * (2 * MiB)));
}
// Map first 2MB of memory
SET_ADDRESS(PDE_RAM, 0xF13000 + PAGE_PRESENT + PAGE_RW);
for(size_t i = 0; i < 512; i++) {
SET_ADDRESS(0xF13000 + i * 4, i * (4 * KiB) + PAGE_PRESENT + PAGE_RW);
}
// 0xA000 should now contain our memory map.
}
void TraversePageTables() {
}
void InitPagingOldImpl() {
// Disable paging so that we can work with the pagetable
//size_t registerTemp = ReadControlRegister(0);
//UNSET_PGBIT(registerTemp);
//WriteControlRegister(0, registerTemp);
// Clear space for our pagetable
size_t PagetableDest = 0x1000;
memset((char*)PagetableDest, 0, 4096);
// Start setting pagetable indexes
*((size_t*)PagetableDest) = 0x2003; // PDP at 0x2000, present & r/w
*((size_t*)PagetableDest + 0x1000) = 0x3003; // PDT at 0x3000, present & r/w
*((size_t*)PagetableDest + 0x2000) = 0x4003; // PT at 0x4000, present & r/w
size_t value = 0x3;
size_t offset = 8;
for(size_t i = 0; i < 512; i++) { // 512 iterations (entries into the page table)
*((size_t*) PagetableDest + offset) = value; // We're setting 512 bytes with x003
// (identity mapping the first 4 megabytes of memory)
// (mapping the page table to itself)
value += 4096; // Point to start of next page
offset += 8; // + 8 bytes (next entry in list)
}
// Enable PAE paging
size_t reg = ReadControlRegister(4);
SET_PAEBIT(reg);
WriteControlRegister(4, reg);
WriteControlRegister(3, PagetableDest);
}
/* size_t registerTemp = ReadControlRegister(4);
if(registerTemp & (1 << 7)) {
TOGGLE_PGEBIT(registerTemp);
WriteControlRegister(4, registerTemp);
}
if(registerTemp & (1 << 7))
WriteControlRegister(4, registerTemp ^ (1 << 7));
size_t CPUIDReturn;
asm volatile("cpuid" : "=d" (CPUIDReturn) : "a" (0x80000001) : "%rbx", "%rcx");
if(CPUIDReturn & (1 << 26)) {
SerialPrintf("System supports 1GB pages.\r\n");
if(registerTemp & (1 << 12)) {
SerialPrintf("PML5 paging available - using that instead.\r\n");
if(MemorySize > (1ULL << 57))
SerialPrintf("System has over 128Petabytes of RAM. Please consider upgrading the OS on your supercomputer.\r\n");
size_t MaxPML5 = 1;
size_t MaxPML4 = 1;
size_t MaxPDP = 512;
size_t LastPML4Entry = 512;
size_t LastPDPEntry = 512;
size_t MemorySearchDepth = MemorySize;
while(MemorySearchDepth > (256ULL << 30)) {
MaxPML5++;
MemorySearchDepth -= (256ULL << 30);
}
if(MaxPML5 > 512)
MaxPML5 = 512;
if(MemorySearchDepth) {
LastPDPEntry = ( (MemorySearchDepth + ((1 << 30) - 1)) & (~0ULL << 30)) >> 30;
if(MaxPML5 > 512)
MaxPML5 = 512;
}
size_t PML4Size = PAGETABLE_SIZE * MaxPML5;
size_t PDPSize = PML4Size * MaxPML4;
size_t PML4Base = AllocatePagetable(PML4Size + PDPSize);
size_t PDPBase = PML4Base + PML4Size;
for(size_t PML5Entry = 0; PML5Entry < MaxPML5; PML5Entry++) {
Pagetable[PML5Entry] = PML4Base + (PML5Entry << 12);
if(PML5Entry == (MaxPML5 - 1))
MaxPML4 = LastPML4Entry;
for(size_t PML4Entry = 0; PML4Entry < MaxPML4; PML4Entry++) {
((size_t*) Pagetable[PML5Entry])[PML4Entry] = PDPBase + (((PML5Entry << 9) + PML5Entry) << 12);
if( (PML5Entry == (MaxPML5 - 1)) && (PML4Entry == (MaxPML4 -1)) )
MaxPDP = LastPDPEntry;
for(size_t PDPEntry = 0; PDPEntry < MaxPDP; PDPEntry++) {
((size_t* ) ((size_t* ) Pagetable[PML5Entry])[PML4Entry])[PDPEntry] = ( ((PML5Entry << 18) + (PML4Entry << 9) + PDPEntry) << 30) | (0x83);
}
((size_t* ) Pagetable[PML5Entry])[PML4Entry] |= 0x3;
}
Pagetable[PML5Entry] |= 0x3;
}
} else {
SerialPrintf("PML4 available - using that instead.\r\n");
size_t MemorySearchDepth = MemorySize;
if(MemorySearchDepth > (1ULL << 48))
SerialPrintf("RAM limited to 256TB.\r\n");
size_t MaxPML4 = 1;
size_t MaxPDP = 512;
size_t LastPDPEntry = 512;
while(MemorySearchDepth > (512ULL << 30)) {
MaxPML4++;
MemorySearchDepth -= (512ULL << 30);
}
if(MaxPML4 > 512)
MaxPML4 = 512;
if(MemorySearchDepth) {
LastPDPEntry = ( (MemorySearchDepth + ((1 << 30) - 1)) & (~0ULL << 30)) >> 30;
if(LastPDPEntry > 512)
LastPDPEntry = 512;
}
size_t PDPSize = PAGETABLE_SIZE * MaxPML4;
size_t PDPBase = AllocatePagetable(PDPSize);
for(size_t PML4Entry = 0; PML4Entry < MaxPML4; PML4Entry++) {
Pagetable[PML4Entry] = PDPBase + (PML4Entry << 12);
if(PML4Entry == (MaxPML4 - 1)) {
MaxPDP = LastPDPEntry;
}
for(size_t PDPEntry = 0; PDPEntry < MaxPDP; PDPEntry++) {
((size_t* ) Pagetable[PML4Entry])[PDPEntry] = (((PML4Entry << 9) + PDPEntry) << 30) | 0x83;
}
Pagetable[PML4Entry] |= 0x3;
}
}
} else {
SerialPrintf("System does not support 1GB pages - using 2MiB paging instead.\r\n");
size_t MemorySearchDepth = MemorySize;
if(MemorySearchDepth > (1ULL << 48)) {
SerialPrintf("Usable RAM is limited to 256TB, and the page table alone will use 1GB of space in memory.\r\n");
}
size_t MaxPML4 = 1, MaxPDP = 512, MaxPD = 512, LastPDPEntry = 1;
while(MemorySearchDepth > (512ULL << 30)) {
MaxPML4++;
MemorySearchDepth -= (512ULL << 30);
}
if(MaxPML4 > 512)
MaxPML4 = 512;
if(MemorySearchDepth) {
LastPDPEntry = ((MemorySearchDepth + ((1 << 30) - 1)) & (~0ULL << 30)) >> 30;
if(LastPDPEntry > 512)
LastPDPEntry = 512;
}
size_t PDPSize = PAGETABLE_SIZE * MaxPML4;
size_t PDSize = PDPSize * MaxPDP;
size_t PDPBase = AllocatePagetable(PDPSize + PDSize);
size_t PDBase = PDPBase + PDSize;
for(size_t PML4Entry = 0; PML4Entry < MaxPML4; PML4Entry++) {
Pagetable[PML4Entry] = PDBase + (PML4Entry << 12);
if(PML4Entry == (MaxPML4 - 1)) {
MaxPDP = LastPDPEntry;
}
for(size_t PDPEntry = 0; PDPEntry < MaxPDP; PDPEntry++) {
( (size_t* ) Pagetable[PML4Entry])[PDPEntry] = PDBase + (((PML4Entry << 9) + PDPEntry) << 12);
for(size_t PDEntry = 0; PDEntry < MaxPD; PDEntry++) {
( (size_t* ) ((size_t*) Pagetable[PML4Entry])[PDPEntry])[PDEntry] = (( (PML4Entry << 18) + (PDPEntry << 9) + PDPEntry) << 21) | 0x83;
}
( (size_t* ) Pagetable[PML4Entry])[PDPEntry] |= 0x3;
}
Pagetable[PML4Entry] |= 0x3;
}
}
WriteControlRegister(3, Pagetable);
registerTemp = ReadControlRegister(4);
if(!(registerTemp & (1 << 7))) {
TOGGLE_PGEBIT(registerTemp);
WriteControlRegister(4, registerTemp);
}*/

View File

@ -0,0 +1,379 @@
#include <kernel/chroma.h>
#include <lainlib/lainlib.h>
/************************
*** Team Kitty, 2020 ***
*** Chroma ***
***********************/
/****************************************
* W O R K I N P R O G R E S S *
****************************************
*
* This file contains functions for virtual memory management.
*
* Virtual Memory Management is still a work in progress.
* The functions here are hold-offs from old versions of the software implemented here, as well as from the EFI version of Chroma, called Sync.
*
* There, these functions worked, but here, under BIOS, it's a lot more difficult.
* It will take some time to get these functions working.
*
* The general plan, being that the BOOTBOOT loader has given us static addresses for all of our doodads,
* is to keep the core kernel where it is (FFFFFFFFFFE00000) and load in modules and libraries around it.
*
* We start in the higher half, so we'll dedicate the lower half (7FFFFFFFFFFF and below) to userspace.
*
* That means we have about 3 terabytes of RAM for the kernel.
* This will be identity mapped, always.
*
* Handily, since most modern processors ignore the highest 2 bytes of a virtual address, and the kernel
* is mapped to 0x80000000000 and above, we can use the nomenclature:
* * 0x00007FFFFFFFFFFF and below is user space.
* * 0xFFFF800000000000 and above is kernel space.
* The processor will ignore the first 4 chars, and this provides a great deal of readability for the
* future of the kernel.
*
* We'll have a kernel heap mapped into this kernel space, as well as a kernel stack (for task switching and error tracing).
* These will be 1GB each.
* We may have to increase this in the future, once Helix is fully integrated.
* Helix will take a lot of memory, as it is a fully featured 3D engine. We may have to implement things like
* texture streaming and mipmapping. Minimising RAM usage is NOT a priority for me, but it would be nice
* to have a minimum requirement above 32GB.
*
* // TODO: Expand Kernel Heap
*
*
* //TODO: there are lots of calls to AllocateFrame here, those need to be separated out into AllocateZeroFrame if necessary.
*
*
*/
extern size_t _kernel_text_start;
extern size_t _kernel_rodata_start;
extern size_t _kernel_data_start;
//__attribute__((aligned(4096))) static size_t Pagetable[512] = {0};
#define LAST_ENTRY 0xFF8
#define SET_ADDRESS(a,b) ((*(size_t*) (a)) = (size_t) b)
/*
* It turns out it's useful to have macros for the standard
* data size units.
*
* Who would've thoguht?
*/
#define KiB 1 * 1024
#define MiB 1 * 1024 * KiB
#define PAGE_PRESENT 1
#define PAGE_RW 2
#define PAGE_USER 4
#define PAGE_GLOBAL 8
#define USERWRITEABLE_FLAGS(a) ((a & 0xFFFFFF00) + 0x83)
// The AbstractAllocator control struct
static allocator_t Allocator = NULL;
// The AbstractAllocator Ticketlock.
static ticketlock_t AllocatorLock = {0};
// Entries to help allocate the Kernel Stack
static list_entry_t StackFreeList;
static ticketlock_t StackLock = {0};
static void* StackPointer = (void*) KERNEL_STACK_REGION;
// A temporary itoa function for better debugging..
const char* IntToAscii(int In) {
char* OutputBuffer = " ";
size_t Temp, i = 0, j = 0;
do {
Temp = In % 10;
OutputBuffer[i++] = (Temp < 10) ? (Temp + '0') : (Temp + 'a' - 10);
} while (In /= 10);
OutputBuffer[i--] = 0;
for(j = 0; j < i; j++, i--) {
Temp = OutputBuffer[j];
OutputBuffer[j] = OutputBuffer[i];
OutputBuffer[i] = Temp;
}
return OutputBuffer;
}
void InitPaging() {
StackFreeList = (list_entry_t) { &StackFreeList, &StackFreeList };
size_t Size = AlignUpwards(AllocatorSize(), PAGE_SIZE);
Allocator = PhysAllocateZeroMem(Size);
Allocator = CreateAllocatorWithPool(Allocator, Size);
SerialPrintf("[ Mem] Everything preallocated for paging.\n");
KernelAddressSpace = (address_space_t) {
.Lock = {0},
.PML4 = PhysAllocateZeroMem(PAGE_SIZE)
};
size_t* Pagetable = KernelAddressSpace.PML4;
//SerialPrintf("[ Mem] About to identity map the higher half.\n");
// Identity map the higher half
for(int i = 256; i < 512; i++) {
Pagetable[i] = (size_t)PhysAllocateZeroMem(PAGE_SIZE);
Pagetable[i] = (size_t)(((char*)Pagetable[i]) - DIRECT_REGION);
Pagetable[i] |= (PAGE_PRESENT | PAGE_RW);
//SerialPrintf("%d", i - 256);
}
SerialPrintf("[ Mem] Identity mapping higher half complete.\n");
MMapEnt* TopEntry = (MMapEnt*)(((&bootldr) + bootldr.size) - sizeof(MMapEnt));
size_t LargestAddress = TopEntry->ptr + TopEntry->size;
SerialPrintf("[ Mem] About to map lower memory into the Direct Region.\n");
for(size_t Address = 0; Address < AlignUpwards(LargestAddress, PAGE_SIZE); Address += PAGE_SIZE) {
MapVirtualMemory(&KernelAddressSpace, (size_t*)(((char*)Address) + DIRECT_REGION), Address, MAP_WRITE);
}
SerialPrintf("[ Mem] Lower half mapping complete.\n");
SerialPrintf("[ Mem] Mapping kernel into new memory map.\r\n");
//TODO: Disallow execution of rodata and data, and bootldr/environment
for(void* Address = CAST(void*, KERNEL_REGION);
Address < CAST(void*, KERNEL_REGION + 0x2000); // Lower half of Kernel
Address = CAST(void*, CAST(char*, Address) + PAGE_SIZE)) {
MapVirtualMemory(&KernelAddressSpace, Address, (CAST(size_t, Address) - KERNEL_REGION) + KERNEL_PHYSICAL, MAP_EXEC);
}
for(void* Address = CAST(void*, KERNEL_REGION + 0x2000);
Address < CAST(void*, KERNEL_REGION + 0x12000); // Higher half of kernel
Address = CAST(void*, CAST(char*, Address) + PAGE_SIZE)) {
MapVirtualMemory(&KernelAddressSpace, Address, (CAST(size_t, Address) - KERNEL_REGION) + KERNEL_PHYSICAL_2, MAP_EXEC);
}
for(void* Address = CAST(void*, FB_REGION);
Address < CAST(void*, 0x200000); // TODO: Turn this into a calculation with bootldr.fb_size
Address = CAST(void*, CAST(char*, Address) + PAGE_SIZE)) {
MapVirtualMemory(&KernelAddressSpace, Address, (CAST(size_t, Address) - FB_REGION) + FB_PHYSICAL, MAP_WRITE);
}
SerialPrintf("[ Mem] Kernel mapped into pagetables. New PML4 at 0x%p\r\n", KernelAddressSpace.PML4);
//ASSERT(Allocator != NULL);
}
static size_t GetCachingAttribute(pagecache_t Cache) {
switch (Cache) {
case CACHE_WRITE_BACK: return 0;
case CACHE_WRITE_THROUGH: return 1 << 2;
case CACHE_NONE: return 1 << 3;
case CACHE_WRITE_COMBINING: return 1 << 6;
}
return 1 << 3;
}
static bool ExpandAllocator(size_t NewSize) {
size_t AllocSize = AlignUpwards(AllocatorPoolOverhead() + sizeof(size_t) * 5 + NewSize, PAGE_SIZE);
void* Pool = PhysAllocateMem(AllocSize);
return AddPoolToAllocator(Allocator, Pool, AllocSize) != NULL;
}
static void GetPageFromTables(address_space_t* AddressSpace, size_t VirtualAddress, size_t** Page) {
//ASSERT(Page != NULL);
//ASSERT(AddressSpace != NULL);
size_t* Pagetable = AddressSpace->PML4;
for(int Level = 4; Level > 1; Level--) {
size_t* Entry = &Pagetable[(VirtualAddress >> (12u + 9u * (Level - 1))) & 0x1FFU];
ASSERT(*Entry & PAGE_PRESENT, "Page not present during retrieval");
Pagetable = (size_t*)((char*)(*Entry & 0x7ffffffffffff000ull) + DIRECT_REGION);
}
ASSERT(Pagetable[(VirtualAddress >> 12U) & 0x1FFU] & PAGE_PRESENT, "PDPE not present during retrieval");
*Page = &Pagetable[(VirtualAddress >> 12U) & 0x1FFU];
}
void SetAddressSpace(address_space_t* AddressSpace) {
//ASSERT(AddressSpace != NULL);
if((size_t)((char*)ReadControlRegister(3) + DIRECT_REGION) != (size_t) &AddressSpace->PML4) {
WriteControlRegister(3, CAST(size_t, &AddressSpace->PML4));
}
}
void MapVirtualMemory(address_space_t* AddressSpace, void* VirtualAddress, size_t PhysicalAddress, mapflags_t Flag) {
//bool MapGlobally = false;
size_t Virtual = (size_t)VirtualAddress;
//ASSERT(AddressSpace != NULL);
TicketAttemptLock(&AddressSpace->Lock);
size_t Flags = PAGE_PRESENT;
if(Flag & MAP_WRITE)
Flags |= MAP_WRITE;
if(Virtual < USER_REGION)
Flags |= PAGE_USER;
//TODO: Global mapping
size_t* Pagetable = AddressSpace->PML4;
for(int Level = 4; Level > 1; Level--) {
size_t* Entry = &Pagetable[(Virtual >> (12u + 9u * (Level - 1))) & 0x1FFu];
if(!(*Entry & PAGE_PRESENT)) {
directptr_t Pointer = PhysAllocateZeroMem(PAGE_SIZE);
*Entry = (size_t)(((char*)Pointer) + DIRECT_REGION);
}
*Entry |= Flags;
Pagetable = (size_t*)(((char*)(*Entry & 0x7ffffffffffff000ull) + DIRECT_REGION));
}
size_t* Entry = &Pagetable[(Virtual >> 12u) & 0x1FFu];
*Entry = Flags | PhysicalAddress;
if(AddressSpace != NULL) {
TicketUnlock(&AddressSpace->Lock);
}
}
void UnmapVirtualMemory(address_space_t* AddressSpace, void* VirtualAddress){
//ASSERT(AddressSpace != NULL);
TicketAttemptLock(&AddressSpace->Lock);
size_t* Entry;
GetPageFromTables(AddressSpace, (size_t)VirtualAddress, &Entry);
*Entry = 0;
InvalidatePage((size_t)VirtualAddress);
if(AddressSpace != NULL) {
TicketUnlock(&AddressSpace->Lock);
}
}
void CacheVirtualMemory(address_space_t* AddressSpace, void* VirtualAddress, pagecache_t Cache) {
//ASSERT(AddressSpace != NULL);
TicketAttemptLock(&AddressSpace->Lock);
size_t* Entry;
GetPageFromTables(AddressSpace, (size_t)VirtualAddress, &Entry);
*Entry &= ~((1 << 6) | (1 << 2) | (1 << 3));
*Entry |= GetCachingAttribute(Cache);
InvalidatePage((size_t)VirtualAddress);
if(AddressSpace != NULL) {
TicketUnlock(&AddressSpace->Lock);
}
}
void* AllocateMemory(size_t Bits) {
TicketAttemptLock(&AllocatorLock);
void* Result = AllocatorMalloc(Allocator, Bits);
if(Result == NULL) {
if(!ExpandAllocator(Bits)) {
TicketUnlock(&AllocatorLock);
return 0ULL;
}
Result = AllocatorMalloc(Allocator, Bits);
}
if(Result != NULL) {
memset(Result, 0, Bits);
}
TicketUnlock(&AllocatorLock);
return Result;
}
void* ReallocateMemory(void* Address, size_t NewSize) {
TicketAttemptLock(&AllocatorLock);
void* Result = AllocatorRealloc(Allocator, Address, NewSize);
if(Result == NULL) {
if(!ExpandAllocator(NewSize)) {
TicketUnlock(&AllocatorLock);
return 0ULL;
}
Result = AllocatorRealloc(Allocator, Address, NewSize);
}
TicketUnlock(&AllocatorLock);
return Result;
}
void FreeMemory(void* Address) {
TicketAttemptLock(&AllocatorLock);
AllocatorFree(Allocator, Address);
TicketUnlock(&AllocatorLock);
}
void* AllocateKernelStack() {
void* StackAddress = NULL;
size_t StackSize = PAGE_SIZE * 4;
TicketAttemptLock(&StackLock);
if(ListIsEmpty(&StackFreeList)) {
StackAddress = StackPointer;
StackPointer = (void*)(((char*)StackPointer) + (4*KiB) + StackSize);
for(size_t i = 0; i < (StackSize / PAGE_SIZE); i++) {
directptr_t NewStack;
NewStack = PhysAllocateZeroMem(PAGE_SIZE);
MapVirtualMemory(&KernelAddressSpace, (void*)((size_t)StackAddress + i * PAGE_SIZE), (size_t)((char*)NewStack) - DIRECT_REGION, MAP_WRITE);
}
} else {
list_entry_t* StackEntry = StackFreeList.Next;
ListRemove(StackEntry);
memset(StackEntry, 0, StackSize);
StackAddress = (void*)StackEntry;
}
TicketUnlock(&StackLock);
StackAddress = (void*)((size_t)StackAddress + StackSize);
StackAddress = (void*)((size_t)StackAddress - sizeof(size_t) * 2);
return StackAddress;
}
void FreeKernelStack(void* StackAddress) {
TicketAttemptLock(&StackLock);
list_entry_t* ListEntry = (list_entry_t*)(((size_t)(StackAddress) + (sizeof(size_t) * 2)) - (PAGE_SIZE * 4));
ListAdd(&StackFreeList, ListEntry);
TicketUnlock(&StackLock);
}