Rewrote loader to support more complex sections.

This fixes relying on UB to expect elf sections to be page-aligned in
file.
master
q3k 2013-07-09 10:40:35 +02:00
parent 7381cb0a5a
commit 5a94b26f14
9 changed files with 368 additions and 181 deletions

View File

@ -18,8 +18,9 @@ LD:=ld
# -O2 sets -foptimize-sibling-calls which breaks code...
CFLAGS:=-Wall -Werror -nostdlib -std=c99 -g -ffreestanding
CFLAGS+=-I ./include -O2 -fno-optimize-sibling-calls
CFLAGS+= -fno-builtin -nostdinc -target i586-elf
CFLAGS+=-I ./include -O1 -fno-optimize-sibling-calls
CFLAGS+= -fno-builtin -nostdinc -target i586-elf -nostdlib
LFLAGS:=-nostdlib -nostartfiles -nodefaultlibs -m elf_i386
.PHONY: all clean loader.bin emulate hdd.img

View File

@ -26,6 +26,9 @@ struct elf_header {
u16 SectionEntryStrings;
};
#define SHT_PROGBITS 1
#define SHT_NOBITS 8
struct elf_section_header {
u32 Name;
u32 Type;

View File

@ -82,6 +82,32 @@ void move_cursor(u8 X, u8 Y)
outb(0x3D5, (u8)(Position >> 8 & 0xFF));
}
void puti(s32 Number)
{
s32 Sign, i;
if ((Sign = Number) < 0)
Number = -Number;
u8 szString[21];
i = 0;
do {
szString[i++] = Number % 10 + '0';
} while (( Number /= 10) > 0);
if (Sign < 0)
szString[i] = '-';
else
i--;
for (s32 j = i; j >= 0; j--)
{
putch(szString[j]);
}
}
void putch(s8 Character)
{
volatile u8 *VideoMemory = (u8 *)0xB8000;
@ -149,9 +175,72 @@ void print_hex(u64 Number)
}
}
void update_load_context(T_LOAD_CONTEXT* Context)
void io_update_load_context(T_LOAD_CONTEXT* Context)
{
Context->VGACurrentLine = stdio_current_line;
Context->VGACursorX = stdio_cur_x;
Context->VGACursorY = stdio_cur_y;
}
u32 strlen(const s8 *szString)
{
const s8 *s;
for (s = szString; *s; ++s)
{}
return s - szString;
}
#define va_start(v,l) __builtin_va_start(v,l)
#define va_arg(v,l) __builtin_va_arg(v,l)
#define va_end(v) __builtin_va_end(v)
#define va_copy(d,s) __builtin_va_copy(d,s)
typedef __builtin_va_list va_list;
void printf(const s8 *szFormat, ...)
{
va_list ap;
va_start(ap, szFormat);
u32 Offset = 0;
while (Offset < strlen(szFormat))
{
if (szFormat[Offset] == '%')
{
switch (szFormat[Offset + 1])
{
case '%':
putch('%');
break;
case 'c':
putch(va_arg(ap, u32));
break;
case 's':
puts(va_arg(ap, s8*));
break;
case 'i':
puti(va_arg(ap, s64));
break;
case 'u':
puti(va_arg(ap, u64));
break;
case 'X':
case 'x':
{
u64 bData = va_arg(ap, u64);
print_hex(bData);
break;
}
default:
printf("printf: Unknown escape character %c!\n", szFormat[Offset + 1]);
}
Offset += 2;
}
else
{
putch(szFormat[Offset]);
Offset++;
}
}
va_end(ap);
}

View File

@ -16,6 +16,7 @@ void clear(void);
void dump_nibble(u8 Nibble);
void print_hex_32(u32 Number);
void print_hex(u64 Number);
void update_load_context(T_LOAD_CONTEXT *Context);
void io_update_load_context(T_LOAD_CONTEXT *Context);
void printf(const s8 *szFormat, ...);
#endif

View File

@ -2,6 +2,8 @@
#include "io.h"
#include "context.h"
#include "elf.h"
#include "paging.h"
#include "multiboot.h"
extern u64 omg64;
extern u64 _end;
@ -12,111 +14,7 @@ static inline void cpuid(u32 code, u32 *a, u32 *d) {
__asm__ volatile("cpuid":"=a"(*a),"=d"(*d):"0"(code):"ecx","ebx");
}
// A simple PAE paging structure so we can jump into 64-bit.
// This will be replaced later on by the kernel code.
u64 pml4[512] __attribute__((aligned(0x1000)));
u64 page_dir_ptr_tab_low[512] __attribute__((aligned(0x1000)));
u64 page_dir_low[512] __attribute__((aligned(0x1000)));
u64 page_tab_low[512] __attribute__((aligned(0x1000)));
u64 page_dir_ptr_tab_high[512] __attribute__((aligned(0x1000)));
u64 page_dir_high[512] __attribute__((aligned(0x1000)));
u64 page_tab_high[512] __attribute__((aligned(0x1000)));
#define GET_PML4_ENTRY(x) (((u64)x >> 39) & 0x1FF)
#define GET_PDP_ENTRY(x) (((u64)x >> 30) & 0x1FF)
#define GET_DIR_ENTRY(x) (((u64)x >> 21) & 0x1FF)
#define GET_TAB_ENTRY(x) (((u64)x >> 12) & 0x1FF)
#define GET_OFFSET(x) (x & 0xFFF)
u32 create_ia32e_paging(u64 KernelPhysicalStart, u64 KernelVirtualStart, u64 KernelSize)
{
puts("Clearing paging structures...\n");
for (u16 i = 0; i < 512; i++)
{
pml4[i] = 0;
page_dir_ptr_tab_low[i] = 0;
page_dir_low[i] = 0;
page_tab_low[i] = 0;
page_dir_ptr_tab_high[i] = 0;
page_dir_high[i] = 0;
page_tab_high[i] = 0;
}
puts("Setting up identity paging for first 2MiB...\n");
pml4[GET_PML4_ENTRY(0)] = (u32)page_dir_ptr_tab_low | 3;
page_dir_ptr_tab_low[GET_PDP_ENTRY(0)] = (u32)page_dir_low | 3;
page_dir_low[GET_DIR_ENTRY(0)] = (u32)page_tab_low | 3;
u64 Address = 0;
for (u16 i = 0; i < 512; i++)
{
page_tab_low[i] = Address | 3;
Address += 0x1000;
}
puts("Setting up paging for the kernel...\n");
u16 NumPages = KernelSize / 0x1000;
puts(" (0x");
print_hex(NumPages);
puts(" pages)\n");
if (NumPages > 512)
{
puts("Error: Kernel size > 2MiB not implemented!");
return 1;
}
puts("Kernel PML4: "); print_hex(GET_PML4_ENTRY(KernelVirtualStart)); puts("\n");
puts("Kernel PDP: "); print_hex(GET_PDP_ENTRY(KernelVirtualStart)); puts("\n");
puts("Kernel DIR: "); print_hex(GET_DIR_ENTRY(KernelVirtualStart)); puts("\n");
if (GET_PML4_ENTRY(KernelVirtualStart) != 0)
{
// We're NOT mapping the same PML4 entry as for identity mapping...
puts("Different PML4...\n");
pml4[GET_PML4_ENTRY(KernelVirtualStart)] = (u32)page_dir_ptr_tab_high | 3;
puts("Setting pml4["); print_hex_32(GET_PML4_ENTRY(KernelVirtualStart)); puts("] to "); print_hex_32((u32)page_dir_ptr_tab_high | 3); puts(".\n");
page_dir_ptr_tab_high[GET_PDP_ENTRY(KernelVirtualStart)] = (u32)page_dir_high | 3;
puts("Setting pdp["); print_hex_32(GET_PDP_ENTRY(KernelVirtualStart)); puts("] to "); print_hex_32((u32)page_dir_high | 3); puts(".\n");
page_dir_high[GET_DIR_ENTRY(KernelVirtualStart)] = (u32)page_tab_high | 3;
puts("Setting dir["); print_hex_32(GET_DIR_ENTRY(KernelVirtualStart)); puts("] to "); print_hex_32((u32)page_tab_high | 3); puts(".\n");
}
else if (GET_PDP_ENTRY(KernelVirtualStart) != 0)
{
// We're NOT mapping the same page directory pointer table entry as for identity paging...
puts("Different PDPT... (");
print_hex(GET_PDP_ENTRY(KernelVirtualStart));
puts(")\n");
page_dir_ptr_tab_low[GET_PDP_ENTRY(KernelVirtualStart)] = (u32)page_dir_high | 3;
page_dir_high[GET_DIR_ENTRY(KernelVirtualStart)] = (u32)page_tab_high | 3;
}
else if (GET_DIR_ENTRY(KernelVirtualStart) != 0)
{
// We're NOT mapping the same page directory entry as for identity paging...
puts("Different DIR...\n");
page_dir_low[GET_DIR_ENTRY(KernelVirtualStart)] = (u32)page_tab_high | 3;
}
else
{
puts("Error: kernel overlaps 2MiB identity paging!\n");
return 1;
}
Address = KernelPhysicalStart;
for (u16 i = GET_TAB_ENTRY(KernelVirtualStart); i < GET_TAB_ENTRY(KernelVirtualStart) + 512; i++)
{
page_tab_high[i] = Address | 3;
Address += 0x1000;
}
return 0;
}
u64 g_multiboot_header;
TMULTIBOOT_INFO *g_Multiboot;
T_LOAD_CONTEXT g_Context;
extern u64 GDT;
extern u32 ldrEntryLow;
@ -126,18 +24,8 @@ u32 load(void *Multiboot, unsigned int Magic)
{
clear();
puts("Cucumber x86-64 loader...\n");
puts("GDT: \n");
for (u32 i = 0; i < 5; i++)
{
puts(" ");
print_hex(*(&GDT + i));
puts("\n");
}
g_multiboot_header = (u32)Multiboot;
g_Multiboot = Multiboot;
if (Magic != 0x2BADB002)
{
puts("Error: not booted via Multiboot!\n");
@ -146,46 +34,40 @@ u32 load(void *Multiboot, unsigned int Magic)
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;
}
u32 Flags = *((u32*)Multiboot);
u8 ModulesPresent = (Flags & (1 << 3)) > 0;
if (!ModulesPresent)
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;
}
u32 ModulesCount = *((u32*)Multiboot + 5);
u32 ModulesAddress = *((u32*)Multiboot + 6);
if (ModulesCount == 0)
if (!g_Multiboot->ModulesCount)
{
puts("Error: No kernel specified! Can't boot non-existant code, sorry!\n");
return 0;
}
if (ModulesCount != 1)
if (g_Multiboot->ModulesCount != 1)
{
puts("Error: just one module is enough. Don't load a ton of them.\n");
return 0;
}
puts("Kernel is @");
print_hex(ModulesAddress);
u32 KernelStart = g_Multiboot->Modules[0].ModuleStart;
u32 KernelEnd = g_Multiboot->Modules[0].ModuleEnd;
puts("Kernel is ");
print_hex(KernelStart);
puts("-");
print_hex(KernelEnd);
puts(".\n");
struct elf_header *Header = *((struct elf_header **)ModulesAddress);
struct elf_header *Header = (struct elf_header *)KernelStart;
if (Header->Identification.Magic != 0x464C457F)
{
puts("Error: Module is not an ELF file!\n");
@ -219,62 +101,86 @@ u32 load(void *Multiboot, unsigned int Magic)
//u32 *Strings = (u32*)(ModulesAddress + (u32)StringSection->Offset);
puts("0x");
puts("Kernel ELF has 0x");
print_hex(Header->NumSectionHeaderEntries);
puts(" ELF sections.\n");
puts(" sections.\n");
u64 ContinuityTest = 0;
// Loop through ELF sections to find physical space occupied by them
u64 StartPhysical = 0;
u64 StartVirtual = 0;
u64 Size = 0;
u64 EndPhysical = 0;
for (u16 i = 0; i < Header->NumSectionHeaderEntries; i++)
{
s8* Name = (s8*)((u32)Header + (u32)StringSection->Offset + (u32)Sections[i].Name);
u64 PhysicalAddress = (u32)Header + Sections[i].Offset;
u64 VirtualAddress = Sections[i].Address;
if (VirtualAddress)
{
if (!StartVirtual)
StartVirtual = VirtualAddress;
if (!StartPhysical)
StartPhysical = PhysicalAddress;
puts("-> Section ");
puts(Name);
puts(", 0x");
print_hex(PhysicalAddress);
puts(" will be located at 0x");
print_hex(VirtualAddress);
puts(".\n");
if (ContinuityTest && VirtualAddress != ContinuityTest)
{
puts("Error: kernel is not continuous!\n");
puts("Previous section ended at 0x"); print_hex(ContinuityTest); puts("\n");
return 0;
}
ContinuityTest = VirtualAddress + Sections[i].Size;
Size += Sections[i].Size;
u64 EndAddress = StartPhysical + Sections[i].Size;
if (EndAddress > EndPhysical)
EndPhysical = EndAddress;
}
}
puts("\nPaging setup:\n 0x");
print_hex(StartVirtual);
puts(" => 0x");
print_hex(StartPhysical);
puts("\n (0x");
print_hex(Size);
puts(" bytes)\n");
u32 FreeSpaceStart = (u32)&_end;
if (EndPhysical > FreeSpaceStart)
FreeSpaceStart = EndPhysical;
if (KernelEnd > FreeSpaceStart)
FreeSpaceStart = KernelEnd;
if (FreeSpaceStart % 0x1000)
FreeSpaceStart = (FreeSpaceStart + 0x1000) & 0xFFFFF000;
g_Context.KernelPhysicalStart = FreeSpaceStart;
g_Context.KernelPhysicalStart = StartPhysical;
g_Context.KernelPhysicalEnd = StartPhysical + Size;
u32 KernelApproximateSize = FreeSpaceStart - StartPhysical;
if (FreeSpaceStart + KernelApproximateSize > 0x00EFFFFF)
{
puts("Kernel will probably not fit in extended memory. Failing.\n");
for(;;) {}
}
puts("Paging frames will be allocated from 0x"); print_hex(FreeSpaceStart); puts("\n");
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);
puts(" -> "); puts(Name); puts(" at "); print_hex(VirtualAddress);
if (!(Sections[i].Type & SHT_NOBITS))
{
// not .bss - copy data
puts(" (copy from "); print_hex(PhysicalAddress); puts(")\n");
u8 *Source = (u8 *)((u32)PhysicalAddress);
for (u32 j = 0; j < Size; j++)
Destination[j] = Source[j];
}
else
{
for (u32 j = 0; j < Size; j++)
Destination[j] = 0;
puts(" (zeroed out)\n");
}
}
}
g_Context.KernelPhysicalEnd = paging_get_last_frame();
s8 *LoaderName = "Cucumber x86-64 loader";
u8 i = 0;
while (*LoaderName)
{
@ -292,15 +198,10 @@ u32 load(void *Multiboot, unsigned int Magic)
print_hex((u32)&g_Context);
puts(".\n");
if (create_ia32e_paging(StartPhysical, StartVirtual, Size))
{
puts("Could not create paging structure, for some reason... Failing.\n");
for (;;) {}
}
__asm__ volatile ("movl %cr4, %eax; bts $5, %eax; movl %eax, %cr4");
__asm__ volatile ("movl %%eax, %%cr3" :: "a" (pml4));
__asm__ volatile ("movl %%eax, %%cr3" :: "a" (paging_get_pml4()));
puts("CR3 is now pointing to PML4 (0x");
print_hex((u32)pml4);
print_hex((u32)paging_get_pml4());
puts(")\n");
puts("Here it goes, enabling long mode...\n");
@ -314,8 +215,8 @@ u32 load(void *Multiboot, unsigned int Magic)
"movl %%ebx, %%cr0;":::"eax","ebx","ecx");
puts("Now in 32-bit compability mode, jumping to the kernel...\n");
update_load_context(&g_Context);
io_update_load_context(&g_Context);
// for (;;) {}
ldrEntryLow = Header->Entry & 0xFFFFFFFF;
ldrEntryHigh = Header->Entry >> 32;
return 1;

37
Loader/src/multiboot.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef __MULTIBOOT_H__
#define __MULTIBOOT_H__
#include "types.h"
typedef struct
{
u32 ModuleStart;
u32 ModuleEnd;
s8 *String;
u32 Reserved;
} __attribute__((packed)) TMULTIBOOT_MODULE;
typedef struct {
u32 Flags;
u32 MemLower;
u32 MemUpper;
u32 BootDevice;
u32 CommandLines;
u32 ModulesCount;
TMULTIBOOT_MODULE *Modules;
} __attribute__((packed)) TMULTIBOOT_INFO;
#define MULTIBOOT_INFO_MEMORY 0x00000001
#define MULTIBOOT_INFO_BOOTDEV 0x00000002
#define MULTIBOOT_INFO_CMDLINE 0x00000004
#define MULTIBOOT_INFO_MODS 0x00000008
#define MULTIBOOT_INFO_AOUT_SYMS 0x00000010
#define MULTIBOOT_INFO_ELF_SHDR 0X00000020
#define MULTIBOOT_INFO_MEM_MAP 0x00000040
#define MULTIBOOT_INFO_DRIVE_INFO 0x00000080
#define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100
#define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200
#define MULTIBOOT_INFO_APM_TABLE 0x00000400
#define MULTIBOOT_INFO_VIDEO_INFO 0x00000800
#endif

134
Loader/src/paging.c Normal file
View File

@ -0,0 +1,134 @@
#include "paging.h"
#include "context.h"
#include "io.h"
u32 g_FramePointer;
u64 *g_PML4;
void _zero_paging_structure(u64 *Structure)
{
for (unsigned i = 0; i < 512; i++)
Structure[i] = 0;
}
u32 _allocate_frame_4k(void)
{
u32 Frame = g_FramePointer;
if (Frame > 0xEFFFFF)
{
puts("BUG: Out of extended memory.\n");
for (;;) {}
}
g_FramePointer += 0x1000;
return Frame;
}
void paging_setup(u32 AllocateFramesFrom)
{
g_FramePointer = AllocateFramesFrom;
// set up PML4
g_PML4 = (u64*)_allocate_frame_4k();
_zero_paging_structure(g_PML4);
}
u8 dbg = 0;
void paging_map_page(u64 Virtual, u64 Physical)
{
if (Virtual % 0x1000 || Physical % 0x1000)
{
puts("BUG: Requsted allocation of unaligned address.\n");
print_hex(Virtual);
for (;;) {}
}
// printf("Mapping %x\n", Virtual);
u64 PML4I = GET_PML4_ENTRY(Virtual);
u64 PDPI = GET_PDP_ENTRY(Virtual);
u64 DIRI = GET_DIR_ENTRY(Virtual);
u64 TABI = GET_TAB_ENTRY(Virtual);
// printf("%x %i/%i/%i/%i\n", Virtual, PML4I, PDPI, DIRI, TABI);
u64 *PDP = (u64 *)(g_PML4[PML4I] & 0xFFFFF000);
if (PDP == 0)
{
// printf("-> Creating PDP @%i\n", PML4I);
PDP = (u64 *)_allocate_frame_4k();
_zero_paging_structure(PDP);
g_PML4[PML4I] = ((u64)PDP) | 3;
}
u64 *Directory = (u64 *)(PDP[PDPI] & 0xFFFFF000);
if (Directory == 0)
{
// printf("-> Creating Dirctory @%i/%i\n", PML4I, PDPI);
Directory = (u64 *)_allocate_frame_4k();
_zero_paging_structure(Directory);
PDP[PDPI] = ((u64)Directory) | 3;
printf("P%x[%i] < d%x\n", (u64)PDP, PDPI, (u64)Directory);
dbg = 0;
}
u64 *Table = (u64 *)(Directory[DIRI] & 0xFFFFF000);
if (Table == 0)
{
if (dbg < 3)
{
printf("P%x[%i] == D%x\n", (u64)PDP, PDPI, (u64)Directory);
dbg += 1;
}
// printf("P@ %x D@ %x, De %x\n", (u64)PDP, (u64)Directory, Directory[DIRI]);
// printf("-> Creating Table @%i/%i/%i\n", PML4I, PDPI, DIRI);
Table = (u64 *)_allocate_frame_4k();
// printf("aT %x\n", Table);
_zero_paging_structure(Table);
Directory[DIRI] = ((u64)Table) | 3;
// printf("De <- %x\n", Directory[DIRI]);
}
Table[TABI] = ((u64)Physical) | 3;
}
void paging_map_address(u64 PhysicalStart, u64 VirtualStart, u64 Size)
{
if (VirtualStart % 0x1000 || PhysicalStart % 0x1000)
{
puts("BUG: Requsted allocation of unaligned address.\n");
for (;;) {}
}
u64 AlignedSize = Size;
if (AlignedSize % 0x1000)
AlignedSize = (AlignedSize + 0x1000) & 0xFFFFF000;
for (u64 i = 0; i < AlignedSize; i += 0x1000)
{
paging_map_page(VirtualStart + i, PhysicalStart + i);
}
}
void *paging_allocate(u64 VirtualStart, u64 Size)
{
// let's first allocate all the pages for the actual data
u32 Start = 0;
for (u64 i = 0; i < Size; i += 0x1000)
{
u64 Frame = _allocate_frame_4k();
if (!Start)
Start = Frame;
}
// and now let's map the data (and allocate frames for mapping structures)
u32 Frame = Start;
for (u64 Virtual = VirtualStart; (Virtual - VirtualStart) < Size; Virtual += 0x1000)
{
paging_map_page(Virtual, Frame);
Frame += 0x1000;
}
return (void *)Start;
}
u32 paging_get_last_frame(void)
{
return g_FramePointer;
}
void *paging_get_pml4(void)
{
return g_PML4;
}

View File

@ -0,0 +1,18 @@
#ifndef __PAGING_H__
#define __PAGING_G__
#include "types.h"
#define GET_PML4_ENTRY(x) (((u64)x >> 39) & 0x1FF)
#define GET_PDP_ENTRY(x) (((u64)x >> 30) & 0x1FF)
#define GET_DIR_ENTRY(x) (((u64)x >> 21) & 0x1FF)
#define GET_TAB_ENTRY(x) (((u64)x >> 12) & 0x1FF)
#define GET_OFFSET(x) (x & 0xFFF)
void paging_setup(u32 AllocateFramesFrom);
void paging_map_address(u64 PhysicalStart, u64 VirtualStart, u64 Size);
void *paging_allocate(u64 VirtualStart, u64 Size);
u32 paging_get_last_frame(void);
void *paging_get_pml4(void);
#endif

View File

@ -3,8 +3,11 @@
typedef unsigned char u8;
typedef unsigned short u16;
typedef short s16;
typedef unsigned int u32;
typedef int s32;
typedef unsigned long long u64;
typedef long long s64;
typedef char s8;
#endif