Cucumber/Loader/src/load.c

209 lines
6.3 KiB
C

#include "types.h"
#include "io.h"
#include "context.h"
#include "elf.h"
#include "paging.h"
#include "multiboot.h"
extern u64 omg64;
extern u64 _end;
extern u64 _start;
static inline void cpuid(u32 code, u32 *a, u32 *d) {
__asm__ volatile("cpuid":"=a"(*a),"=d"(*d):"0"(code):"ecx","ebx");
}
TMULTIBOOT_INFO *g_Multiboot;
T_LOAD_CONTEXT g_Context;
extern u64 GDT;
extern u32 ldrEntryLow;
extern u32 ldrEntryHigh;
u32 load(void *Multiboot, unsigned int Magic)
{
clear();
puts("Cucumber x86-64 loader...\n");
g_Multiboot = Multiboot;
if (Magic != 0x2BADB002)
{
puts("Error: not booted via Multiboot!\n");
return 0;
}
u32 CPUID_A, CPUID_D;
cpuid(0x80000001, &CPUID_A, &CPUID_D);
u8 SupportFor64 = (CPUID_D & (1 << 29)) > 0;
if (!SupportFor64)
{
puts("Error: You CPU does not support long mode!\n");
return 0;
}
if (!(g_Multiboot->Flags & MULTIBOOT_INFO_MODS))
{
puts("Error: no 64-bit kernel loaded!\n");
puts(" (did you forget the module line in GRUB?)\n");
return 0;
}
if (!g_Multiboot->ModulesCount)
{
puts("Error: No kernel specified! Can't boot non-existant code, sorry!\n");
return 0;
}
if (g_Multiboot->ModulesCount != 1)
{
puts("Error: just one module is enough. Don't load a ton of them.\n");
return 0;
}
u32 KernelStart = g_Multiboot->Modules[0].ModuleStart;
u32 KernelEnd = g_Multiboot->Modules[0].ModuleEnd;
printf("Kernel is at 0x%x - 0x%x\n", KernelStart, KernelEnd);
struct elf_header *Header = (struct elf_header *)KernelStart;
if (Header->Identification.Magic != 0x464C457F)
{
puts("Error: Module is not an ELF file!\n");
return 0;
}
if (Header->Identification.Class != 2)
{
puts("Error: Module is not a 64-bit ELF file!\n");
return 0;
}
if (!Header->Entry)
{
puts("Error: Kernel does not have entry point!\n");
return 0;
}
printf("Kernel entry: 0x%x\n", Header->Entry);
if (Header->SectionHeaderEntrySize != sizeof(struct elf_section_header))
{
puts("Error: Weird section header entry size!\n");
return 0;
}
struct elf_section_header *Sections = (struct elf_section_header *)((u32)Header + (u32)Header->SectionHeaderOffset);
struct elf_section_header *StringSection = &Sections[Header->SectionEntryStrings];
printf("Kernel ELF has %i sections.", Header->NumSectionHeaderEntries);
// Loop through ELF sections to find physical space occupied by them
u64 StartPhysical = 0;
u64 EndPhysical = 0;
for (u16 i = 0; i < Header->NumSectionHeaderEntries; i++)
{
u64 PhysicalAddress = (u32)Header + Sections[i].Offset;
u64 VirtualAddress = Sections[i].Address;
if (VirtualAddress)
{
if (!StartPhysical)
StartPhysical = PhysicalAddress;
u64 EndAddress = StartPhysical + Sections[i].Size;
if (EndAddress > EndPhysical)
EndPhysical = EndAddress;
}
}
u32 FreeSpaceStart = (u32)&_end;
if (EndPhysical > FreeSpaceStart)
FreeSpaceStart = EndPhysical;
if (KernelEnd > FreeSpaceStart)
FreeSpaceStart = KernelEnd;
if (FreeSpaceStart % 0x1000)
FreeSpaceStart = (FreeSpaceStart + 0x1000) & 0xFFFFF000;
u32 KernelApproximateSize = FreeSpaceStart - StartPhysical;
if (FreeSpaceStart + KernelApproximateSize > 0x00EFFFFF)
{
puts("Kernel will probably not fit in extended memory. Failing.\n");
for(;;) {}
}
printf("Allocating frames from 0x%x\n", FreeSpaceStart);
paging_setup(FreeSpaceStart);
// allocate idnetity mapping of low & extended memory (up to 0x00EFFFFF)
paging_map_address(0, 0, 0x00EFFFFF);
// map the kernel sections
for (u16 i = 0; i < Header->NumSectionHeaderEntries; i++)
{
u64 PhysicalAddress = (u32)Header + Sections[i].Offset;
u64 VirtualAddress = Sections[i].Address;
u64 Size = Sections[i].Size;
s8* Name = (s8*)((u32)Header + (u32)StringSection->Offset + (u32)Sections[i].Name);
if (VirtualAddress)
{
// allocate space for that section...
u64 SizeAligned = Size;
if (SizeAligned % 0x1000)
SizeAligned = (SizeAligned + 0x1000) & 0xFFFFF000;
u8 *Destination = paging_allocate(VirtualAddress, SizeAligned);
printf(" -> %s at %X", Name, VirtualAddress);
if (!(Sections[i].Type & SHT_NOBITS))
{
// not .bss - copy data
u8 *Source = (u8 *)((u32)PhysicalAddress);
for (u32 j = 0; j < Size; j++)
Destination[j] = Source[j];
printf(" (copied %i bytes from 0x%x)\n", Size, PhysicalAddress);
}
else
{
for (u32 j = 0; j < Size; j++)
Destination[j] = 0;
printf(" (zeroed %i bytes)\n", Size);
}
}
}
g_Context.ReservedPhysicalStart = (u32)&_start;
g_Context.ReservedPhysicalEnd = paging_get_last_frame();
s8 *LoaderName = "Cucumber x86-64 loader";
u8 i = 0;
while (*LoaderName)
{
g_Context.LoaderName[i] = *LoaderName;
i++;
LoaderName++;
}
g_Context.VGATextModeUsed = 1;
g_Context.MultibootUsed = 1;
g_Context.MultibootHeader = (u32)Multiboot;
g_Context.KernelELF = (u64)KernelStart;
g_Context.KernelELFSize = (u64)(KernelEnd - KernelStart);
printf("Load context at 0x%x\n", (u64)&g_Context);
__asm__ volatile ("movl %cr4, %eax; bts $5, %eax; movl %eax, %cr4");
__asm__ volatile ("movl %%eax, %%cr3" :: "a" (paging_get_pml4()));
puts("Here it goes, enabling long mode...\n");
__asm__ volatile( "movl $0xc0000080, %%ecx;\n"
"rdmsr;\n"
"orl $0x100, %%eax;\n"
"wrmsr;\n"
"movl %%cr0, %%ebx;\n"
"bts $31, %%ebx;\n"
"movl %%ebx, %%cr0;":::"eax","ebx","ecx");
puts("Now in 32-bit compability mode, jumping to the kernel...\n");
io_update_load_context(&g_Context);
// for (;;) {}
ldrEntryLow = Header->Entry & 0xFFFFFFFF;
ldrEntryHigh = Header->Entry >> 32;
return 1;
}