elf: Add support for higher half kernels
diff --git a/src/bootsect/gdt.inc b/src/bootsect/gdt.inc
index 15045d27..d4d6b6a4 100644
--- a/src/bootsect/gdt.inc
+++ b/src/bootsect/gdt.inc
@@ -56,4 +56,20 @@ db 10010010b ; Access
db 11001111b ; Granularity
db 0x00 ; Base (high 8 bits)
+; 64 bit code
+dw 0x0000 ; Limit
+dw 0x0000 ; Base (low 16 bits)
+db 0x00 ; Base (mid 8 bits)
+db 10011010b ; Access
+db 00100000b ; Granularity
+db 0x00 ; Base (high 8 bits)
+
+; 64 bit data
+dw 0x0000 ; Limit
+dw 0x0000 ; Base (low 16 bits)
+db 0x00 ; Base (mid 8 bits)
+db 10010010b ; Access
+db 00000000b ; Granularity
+db 0x00 ; Base (high 8 bits)
+
.GDTEnd:
diff --git a/src/lib/elf.c b/src/lib/elf.c
index dcfb7974..1cbcbb5c 100644
--- a/src/lib/elf.c
+++ b/src/lib/elf.c
@@ -1,5 +1,6 @@
#include <stdint.h>
#include <stddef.h>
+#include <stdbool.h>
#include <lib/blib.h>
#include <lib/libc.h>
#include <lib/elf.h>
@@ -46,9 +47,11 @@ struct elf_phdr {
uint64_t p_align;
};
-int echfs_read(struct echfs_file_handle *file, void *buf, uint64_t loc, uint64_t count);
+#define FIXED_HIGHER_HALF_OFFSET ((uint64_t)0xffffffff80000000)
int elf_load(struct echfs_file_handle *fd) {
+ bool elf_higher_half = false;
+
struct elf_hdr hdr;
echfs_read(fd, &hdr, 0, sizeof(struct elf_hdr));
@@ -75,6 +78,12 @@ int elf_load(struct echfs_file_handle *fd) {
if (phdr.p_type != PT_LOAD)
continue;
+ if (phdr.p_vaddr & (1ull << 63)) {
+ print("elf: This is a higher half kernel!\n");
+ elf_higher_half = true;
+ phdr.p_vaddr -= FIXED_HIGHER_HALF_OFFSET;
+ }
+
echfs_read(fd, (void *)(uint32_t)phdr.p_vaddr,
phdr.p_offset, phdr.p_filesz);
@@ -86,10 +95,63 @@ int elf_load(struct echfs_file_handle *fd) {
}
}
+ volatile struct {
+ uint64_t pml4[512];
+ uint64_t pml3_lo[512];
+ uint64_t pml3_hi[512];
+ uint64_t pml2_0gb[512];
+ uint64_t pml2_1gb[512];
+ uint64_t pml2_2gb[512];
+ uint64_t pml2_3gb[512];
+ } *pagemap = (void *)0x10000;
+
+ // first, zero out the pagemap
+ for (uint64_t *p = (uint64_t *)pagemap; p < &pagemap->pml3_hi[512]; p++)
+ *p = 0;
+
+ pagemap->pml4[511] = (uint64_t)(size_t)pagemap->pml3_hi | 0x03;
+ pagemap->pml4[0] = (uint64_t)(size_t)pagemap->pml3_lo | 0x03;
+ pagemap->pml3_hi[510] = (uint64_t)(size_t)pagemap->pml2_0gb | 0x03;
+ pagemap->pml3_hi[511] = (uint64_t)(size_t)pagemap->pml2_1gb | 0x03;
+ pagemap->pml3_lo[0] = (uint64_t)(size_t)pagemap->pml2_0gb | 0x03;
+ pagemap->pml3_lo[1] = (uint64_t)(size_t)pagemap->pml2_1gb | 0x03;
+ pagemap->pml3_lo[2] = (uint64_t)(size_t)pagemap->pml2_2gb | 0x03;
+ pagemap->pml3_lo[3] = (uint64_t)(size_t)pagemap->pml2_3gb | 0x03;
+
+ // populate the page directories
+ for (size_t i = 0; i < 512 * 4; i++)
+ (&pagemap->pml2_0gb[0])[i] = (i * 0x1000) | 0x03 | (1 << 7);
+
+ uint32_t entry_point = elf_higher_half
+ ? (uint32_t)(hdr.entry - FIXED_HIGHER_HALF_OFFSET)
+ : (uint32_t)hdr.entry;
+
asm volatile (
- "jmp %0\n\t"
+ "cli\n\t"
+ "mov cr3, eax\n\t"
+ "mov eax, cr4\n\t"
+ "or eax, 1 << 5 | 1 << 7\n\t"
+ "mov cr4, eax\n\t"
+ "mov ecx, 0xc0000080\n\t"
+ "rdmsr\n\t"
+ "or eax, 1 << 8\n\t"
+ "wrmsr\n\t"
+ "mov eax, cr0\n\t"
+ "or eax, 1 << 31\n\t"
+ "mov cr0, eax\n\t"
+ "jmp 0x28:1f\n\t"
+ "1: .code64\n\t"
+ "mov ax, 0x30\n\t"
+ "mov ds, ax\n\t"
+ "mov es, ax\n\t"
+ "mov fs, ax\n\t"
+ "mov gs, ax\n\t"
+ "mov ss, ax\n\t"
+ "jmp rbx\n\t"
+ ".code32\n\t"
:
- : "r" ((uint32_t)hdr.entry)
- : "memory"
+ : "a" (pagemap), "b" (entry_point)
);
+
+ for (;;);
}
diff --git a/test/linker.ld b/test/linker.ld
index 92cdcf66..a1f8fbd3 100644
--- a/test/linker.ld
+++ b/test/linker.ld
@@ -1,7 +1,7 @@
ENTRY(_start)
SECTIONS {
- . = 0x100000;
+ . = 0xffffffff80100000;
.text : {
*(.text*)
diff --git a/test/test.asm b/test/test.asm
index f378f478..68ce5412 100644
--- a/test/test.asm
+++ b/test/test.asm
@@ -6,9 +6,8 @@
section .text
; Entry point
-bits 32
global _start
_start:
- mov eax, 0xdeadbeef
+ mov rax, 0xcafebabedeadbeef
jmp $
