diff --git a/CMakeLists.txt b/CMakeLists.txt index 24de326..c7b9d93 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ SET(src_files ${CMAKE_SOURCE_DIR}/chroma/system/memory/abstract_allocator.c ${CMAKE_SOURCE_DIR}/chroma/system/memory/physmem.c ${CMAKE_SOURCE_DIR}/chroma/system/drivers/keyboard.c + ${CMAKE_SOURCE_DIR}/chroma/system/drivers/elf.c ) SET(lib_files diff --git a/chroma/inc/kernel/boot/boot.h b/chroma/inc/kernel/boot/boot.h index b012a80..2607484 100644 --- a/chroma/inc/kernel/boot/boot.h +++ b/chroma/inc/kernel/boot/boot.h @@ -11,6 +11,30 @@ extern "C" { #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" /* minimum protocol level: @@ -108,30 +132,6 @@ typedef struct { } __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 } diff --git a/chroma/kernel.c b/chroma/kernel.c index 2d8d1f0..4aa0cdb 100644 --- a/chroma/kernel.c +++ b/chroma/kernel.c @@ -20,48 +20,13 @@ address_space_t KernelAddressSpace; int Main(void) { KernelAddressSpace = (address_space_t) {0}; - KernelLocation = 0x112600; 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] Initrd is physically at 0x%p, and is %d bytes long.\r\n", bootldr.initrd_ptr, bootldr.initrd_size); - - SerialPrintf("[ boot] Searching for kernel... Constants start at 0x%p\r\n", &_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 = 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] Initrd's header is 0x%p\r\n", FIXENDIAN32(*((volatile uint32_t*)(bootldr.initrd_ptr)))); + + ParseKernelHeader(bootldr.initrd_ptr); SerialPrintf("[ boot] The bootloader has put the paging tables at 0x%p.\r\n", ReadControlRegister(3)); diff --git a/chroma/system/drivers/elf.c b/chroma/system/drivers/elf.c new file mode 100644 index 0000000..1529c6f --- /dev/null +++ b/chroma/system/drivers/elf.c @@ -0,0 +1,91 @@ +#include +/************************ + *** 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; + +} +