Kernel: Finally managed to get 64-bit working

I had weird problems with interrupts but everything should work now
This commit is contained in:
Bananymous 2023-01-25 19:05:47 +02:00
parent c4670f49d4
commit b315fdc27f
10 changed files with 798 additions and 0 deletions

View File

@ -0,0 +1,8 @@
ARCH_CFLAGS=
ARCH_CPPFLAGS=
KERNEL_ARCH_CFLAGS=
KERNEL_ARCH_CPPFLAGS=
ARCH_FREEOBJS=\
ARCH_HOSTEDOBJS=\

236
kernel/arch/x86_64/IDT.cpp Normal file
View File

@ -0,0 +1,236 @@
#include <BAN/Errors.h>
#include <kernel/IDT.h>
#include <kernel/InterruptController.h>
#include <kernel/kmalloc.h>
#include <kernel/Panic.h>
#define REGISTER_ISR_HANDLER(i) register_interrupt_handler(i, isr ## i)
#define REGISTER_IRQ_HANDLER(i) register_interrupt_handler(IRQ_VECTOR_BASE + i, irq ## i)
namespace IDT
{
struct GateDescriptor
{
uint16_t offset1;
uint16_t selector;
uint8_t IST;
uint8_t flags;
uint16_t offset2;
uint32_t offset3;
uint32_t reserved;
} __attribute__((packed));
struct IDTR
{
uint16_t size;
uint64_t offset;
} __attribute__((packed));
static IDTR s_idtr;
static GateDescriptor* s_idt = nullptr;
static void (*s_irq_handlers[0x10])() { nullptr };
static const char* isr_exceptions[] =
{
"Division Error",
"Debug",
"Non-maskable Interrupt",
"Breakpoint",
"Overflow",
"Bound Range Exception",
"Invalid Opcode",
"Device Not Available",
"Double Fault",
"Coprocessor Segment Overrun",
"Invalid TSS",
"Segment Not Present",
"Stack-Segment Fault",
"General Protection Fault",
"Page Fault",
"Unknown Exception 0x0F",
"x87 Floating-Point Exception",
"Alignment Check",
"Machine Check",
"SIMD Floating-Point Exception",
"Virtualization Exception",
"Control Protection Exception",
"Unknown Exception 0x16",
"Unknown Exception 0x17",
"Unknown Exception 0x18",
"Unknown Exception 0x19",
"Unknown Exception 0x1A",
"Unknown Exception 0x1B",
"Hypervisor Injection Exception",
"VMM Communication Exception",
"Security Exception",
"Unkown Exception 0x1F",
};
extern "C" void cpp_isr_handler(uint64_t isr, uint64_t error)
{
uint64_t rax, rbx, rcx, rdx, rsp, rbp;
uint64_t cr0, cr2, cr3, cr4;
asm volatile("":"=a"(rax),"=b"(rbx),"=c"(rcx),"=d"(rdx));
asm volatile("movq %%rsp, %%rax":"=a"(rsp));
asm volatile("movq %%rbp, %%rax":"=a"(rbp));
asm volatile("movq %%cr0, %%rax":"=a"(cr0));
asm volatile("movq %%cr2, %%rax":"=a"(cr2));
asm volatile("movq %%cr3, %%rax":"=a"(cr3));
asm volatile("movq %%cr4, %%rax":"=a"(cr4));
Kernel::Panic(
"{} (error code: 0x{16H})\r\n"
"Register dump\r\n"
"rax=0x{16H}, rbx=0x{16H}, rcx=0x{16H}, rdx=0x{16H}\r\n"
"rsp=0x{16H}, rbp=0x{16H}\r\n"
"CR0=0x{16H}, CR2=0x{16H}, CR3=0x{16H}, CR4=0x{16H}\r\n",
isr_exceptions[isr], error, rax, rbx, rcx, rdx, rsp, rbp, cr0, cr2, cr3, cr4
);
}
extern "C" void cpp_irq_handler(uint64_t irq)
{
if (s_irq_handlers[irq])
s_irq_handlers[irq]();
else
dprintln("no handler for irq 0x{2H}\n", irq);
InterruptController::Get().EOI(irq);
}
static void flush_idt()
{
asm volatile("lidt %0"::"m"(s_idtr));
}
static void register_interrupt_handler(uint8_t index, void (*f)())
{
GateDescriptor& descriptor = s_idt[index];
descriptor.offset1 = (uint16_t)((uint64_t)f >> 0);
descriptor.offset2 = (uint16_t)((uint64_t)f >> 16);
descriptor.offset3 = (uint32_t)((uint64_t)f >> 32);
descriptor.selector = 0x08;
descriptor.IST = 0;
descriptor.flags = 0x8E;
}
void register_irq_handler(uint8_t irq, void (*f)())
{
s_irq_handlers[irq] = f;
}
extern "C" void isr0();
extern "C" void isr1();
extern "C" void isr2();
extern "C" void isr3();
extern "C" void isr4();
extern "C" void isr5();
extern "C" void isr6();
extern "C" void isr7();
extern "C" void isr8();
extern "C" void isr9();
extern "C" void isr10();
extern "C" void isr11();
extern "C" void isr12();
extern "C" void isr13();
extern "C" void isr14();
extern "C" void isr15();
extern "C" void isr16();
extern "C" void isr17();
extern "C" void isr18();
extern "C" void isr19();
extern "C" void isr20();
extern "C" void isr21();
extern "C" void isr22();
extern "C" void isr23();
extern "C" void isr24();
extern "C" void isr25();
extern "C" void isr26();
extern "C" void isr27();
extern "C" void isr28();
extern "C" void isr29();
extern "C" void isr30();
extern "C" void isr31();
extern "C" void irq0();
extern "C" void irq1();
extern "C" void irq2();
extern "C" void irq3();
extern "C" void irq4();
extern "C" void irq5();
extern "C" void irq6();
extern "C" void irq7();
extern "C" void irq8();
extern "C" void irq9();
extern "C" void irq10();
extern "C" void irq11();
extern "C" void irq12();
extern "C" void irq13();
extern "C" void irq14();
extern "C" void irq15();
void initialize()
{
s_idt = (GateDescriptor*)kmalloc_eternal(0x100 * sizeof(GateDescriptor));
memset(s_idt, 0x00, 0x100 * sizeof(GateDescriptor));
s_idtr.offset = (uint64_t)s_idt;
s_idtr.size = 0x100 * sizeof(GateDescriptor) - 1;
REGISTER_ISR_HANDLER(0);
REGISTER_ISR_HANDLER(1);
REGISTER_ISR_HANDLER(2);
REGISTER_ISR_HANDLER(3);
REGISTER_ISR_HANDLER(4);
REGISTER_ISR_HANDLER(5);
REGISTER_ISR_HANDLER(6);
REGISTER_ISR_HANDLER(7);
REGISTER_ISR_HANDLER(8);
REGISTER_ISR_HANDLER(9);
REGISTER_ISR_HANDLER(10);
REGISTER_ISR_HANDLER(11);
REGISTER_ISR_HANDLER(12);
REGISTER_ISR_HANDLER(13);
REGISTER_ISR_HANDLER(14);
REGISTER_ISR_HANDLER(15);
REGISTER_ISR_HANDLER(16);
REGISTER_ISR_HANDLER(17);
REGISTER_ISR_HANDLER(18);
REGISTER_ISR_HANDLER(19);
REGISTER_ISR_HANDLER(20);
REGISTER_ISR_HANDLER(21);
REGISTER_ISR_HANDLER(22);
REGISTER_ISR_HANDLER(23);
REGISTER_ISR_HANDLER(24);
REGISTER_ISR_HANDLER(25);
REGISTER_ISR_HANDLER(26);
REGISTER_ISR_HANDLER(27);
REGISTER_ISR_HANDLER(28);
REGISTER_ISR_HANDLER(29);
REGISTER_ISR_HANDLER(30);
REGISTER_ISR_HANDLER(31);
REGISTER_IRQ_HANDLER(0);
REGISTER_IRQ_HANDLER(1);
REGISTER_IRQ_HANDLER(2);
REGISTER_IRQ_HANDLER(3);
REGISTER_IRQ_HANDLER(4);
REGISTER_IRQ_HANDLER(5);
REGISTER_IRQ_HANDLER(6);
REGISTER_IRQ_HANDLER(7);
REGISTER_IRQ_HANDLER(8);
REGISTER_IRQ_HANDLER(9);
REGISTER_IRQ_HANDLER(10);
REGISTER_IRQ_HANDLER(11);
REGISTER_IRQ_HANDLER(12);
REGISTER_IRQ_HANDLER(13);
REGISTER_IRQ_HANDLER(14);
REGISTER_IRQ_HANDLER(15);
flush_idt();
}
}

155
kernel/arch/x86_64/MMU.cpp Normal file
View File

@ -0,0 +1,155 @@
#include <BAN/Errors.h>
#include <kernel/kmalloc.h>
#include <kernel/MMU.h>
#define PRESENT (1 << 0)
#define READ_WRITE (1 << 1)
#define PAGE_SIZE 0x1000
#define PAGE_MASK ~(PAGE_SIZE - 1)
#define CLEANUP_STRUCTURE(s) \
for (uint64_t i = 0; i < 512; i++) \
if (s[i] & PRESENT) \
goto cleanup_done; \
kfree(s)
static MMU* s_instance = nullptr;
void MMU::Intialize()
{
ASSERT(s_instance == nullptr);
s_instance = new MMU();
}
MMU& MMU::Get()
{
ASSERT(s_instance);
return *s_instance;
}
static uint64_t* allocate_page_aligned_page()
{
void* page = kmalloc(PAGE_SIZE, PAGE_SIZE);
ASSERT(page);
memset(page, 0, PAGE_SIZE);
return (uint64_t*)page;
}
MMU::MMU()
{
// Identity map from 4 KiB -> 4 MiB
m_highest_paging_struct = allocate_page_aligned_page();
uint64_t* pdpt = allocate_page_aligned_page();
m_highest_paging_struct[0] = (uint64_t)pdpt | READ_WRITE | PRESENT;
uint64_t* pd = allocate_page_aligned_page();
pdpt[0] = (uint64_t)pd | READ_WRITE | PRESENT;
for (uint32_t i = 0; i < 2; i++)
{
uint64_t* pt = allocate_page_aligned_page();
for (uint64_t j = 0; j < 512; j++)
pt[j] = (i << 21) | (j << 12) | READ_WRITE | PRESENT;
pd[i] = (uint64_t)pt | READ_WRITE | PRESENT;
}
// Unmap 0 -> 4 KiB
uint64_t* pt1 = (uint64_t*)(pd[0] & PAGE_MASK);
pt1[0] = 0;
// Load the new pml4
asm volatile("movq %0, %%cr3" :: "r"(m_highest_paging_struct));
}
void MMU::AllocatePage(uintptr_t address)
{
ASSERT((address >> 48) == 0);
address &= PAGE_MASK;
uint64_t pml4e = (address >> 39) & 0x1FF;
uint64_t pdpte = (address >> 30) & 0x1FF;
uint64_t pde = (address >> 21) & 0x1FF;
uint64_t pte = (address >> 12) & 0x1FF;
uint64_t* pml4 = m_highest_paging_struct;
if (!(pml4[pml4e] & PRESENT))
{
uint64_t* pdpt = allocate_page_aligned_page();
pml4[pml4e] = (uint64_t)pdpt | READ_WRITE | PRESENT;
}
uint64_t* pdpt = (uint64_t*)(pml4[pml4e] & PAGE_MASK);
if (!(pdpt[pdpte] & PRESENT))
{
uint64_t* pd = allocate_page_aligned_page();
pdpt[pdpte] = (uint64_t)pd | READ_WRITE | PRESENT;
}
uint64_t* pd = (uint64_t*)(pdpt[pdpte] & PAGE_MASK);
if (!(pd[pde] & PRESENT))
{
uint64_t* pt = allocate_page_aligned_page();
pd[pde] = (uint64_t)pt | READ_WRITE | PRESENT;
}
uint64_t* pt = (uint64_t*)(pd[pde] & PAGE_MASK);
if (!(pt[pte] & PRESENT))
pt[pte] = address | READ_WRITE | PRESENT;
asm volatile("invlpg (%0)" :: "r"(address) : "memory");
}
void MMU::AllocateRange(uintptr_t address, ptrdiff_t size)
{
uintptr_t s_page = address & PAGE_MASK;
uintptr_t e_page = (address + size - 1) & PAGE_MASK;
for (uintptr_t page = s_page; page <= e_page; page += PAGE_SIZE)
AllocatePage(page);
}
void MMU::UnAllocatePage(uintptr_t address)
{
ASSERT((address >> 48) == 0);
address &= PAGE_MASK;
uint64_t pml4e = (address >> 39) & 0x1FF;
uint64_t pdpte = (address >> 30) & 0x1FF;
uint64_t pde = (address >> 21) & 0x1FF;
uint64_t pte = (address >> 12) & 0x1FF;
uint64_t* pml4 = m_highest_paging_struct;
ASSERT(pml4[pml4e] & PRESENT);
uint64_t* pdpt = (uint64_t*)(pml4[pml4e] & PAGE_MASK);
ASSERT(pdpt[pdpte] & PRESENT);
uint64_t* pd = (uint64_t*)(pdpt[pdpte] & PAGE_MASK);
ASSERT(pd[pde] & PRESENT);
uint64_t* pt = (uint64_t*)(pd[pde] & PAGE_MASK);
ASSERT(pt[pte] & PRESENT);
pt[pte] = 0;
CLEANUP_STRUCTURE(pt);
pd[pde] = 0;
CLEANUP_STRUCTURE(pd);
pdpt[pdpte] = 0;
CLEANUP_STRUCTURE(pdpt);
pml4[pml4e] = 0;
cleanup_done:
asm volatile("invlpg (%0)" :: "r"(address) : "memory");
}
void MMU::UnAllocateRange(uintptr_t address, ptrdiff_t size)
{
uintptr_t s_page = address & PAGE_MASK;
uintptr_t e_page = (address + size - 1) & PAGE_MASK;
for (uintptr_t page = s_page; page <= e_page; page += PAGE_SIZE)
UnAllocatePage(page);
}

199
kernel/arch/x86_64/boot.S Normal file
View File

@ -0,0 +1,199 @@
# Declare constants for the multiboot header
.set ALIGN, 1<<0 # align loaded modules on page boundaries
.set MEMINFO, 1<<1 # provide memory map
.set VIDEOINFO, 1<<2 # provide video info
.set MB_FLAGS, ALIGN | MEMINFO | VIDEOINFO # this is the Multiboot 'flag' field
.set MB_MAGIC, 0x1BADB002 # 'magic number' lets bootloader find the header
.set MB_CHECKSUM, -(MB_MAGIC + MB_FLAGS) #checksum of above, to prove we are multiboot
.set PG_PRESENT, 1<<0
.set PG_READ_WRITE, 1<<1
.set PG_PAGE_SIZE, 1<<7
.code32
# Multiboot header
.section .multiboot, "aw"
.align 4
.long MB_MAGIC
.long MB_FLAGS
.long MB_CHECKSUM
.skip 20
.long 0
.long 800
.long 600
.long 32
.section .bss, "aw", @nobits
# Create stack
stack_bottom:
.skip 16384
stack_top:
.global g_kernel_cmdline
g_kernel_cmdline:
.skip 4096
# Reserve memory for paging structures,
# we will identity map first 4 MiB
# 0 MiB -> 1 MiB: bootloader stuff
# 1 MiB -> 2 MiB: kernel
# 2 MiB -> 3 MiB: kmalloc
# 3 MiB -> 4 MiB: kmalloc_eternal
.align 4096
boot_pml4:
.skip 512 * 8
boot_pdpt1:
.skip 512 * 8
boot_pd1:
.skip 512 * 8
.section .text, "a"
.global g_multiboot_info
g_multiboot_info:
.skip 8
.global g_multiboot_magic
g_multiboot_magic:
.skip 8
boot_gdt:
.quad 0 // null
.quad (1<<53) | (1<<47) | (1<<44) | (1<<43) // kernel code P,S,E,L (present, code/data, executable, long mode)
boot_gdtr:
.short . - boot_gdt - 1
.quad boot_gdt
has_cpuid:
pushfl
pushfl
xorl $0x00200000, (%esp)
popfl
pushfl
popl %eax
xorl (%esp), %eax
popfl
testl $0x00200000, %eax
ret
is_64_bit:
movl $0x80000000, %eax
cpuid
cmpl $0x80000001, %eax
jl .no_extension
movl $0x80000001, %eax
cpuid
testl $(1 << 29), %edx
ret
.no_extension:
cmpl %eax, %eax
ret
check_requirements:
call has_cpuid
jz .exit
call is_64_bit
jz .exit
ret
.exit:
jmp system_halt
copy_kernel_commandline:
pushl %esi
pushl %edi
movl g_multiboot_info, %esi
addl $16, %esi
movl (%esi), %esi
movl $1024, %ecx
movl $g_kernel_cmdline, %edi
rep movsl
popl %edi
popl %esi
ret
enable_sse:
movl %cr0, %eax
andw $0xFFFB, %ax
orw $0x0002, %ax
movl %eax, %cr0
movl %cr4, %eax
orw $0x0600, %ax
movl %eax, %cr4
ret
initialize_paging:
# identity map first 4 MiB
movl $(0x00000000 + PG_PAGE_SIZE + PG_READ_WRITE + PG_PRESENT), boot_pd1 + 0
movl $(0x00200000 + PG_PAGE_SIZE + PG_READ_WRITE + PG_PRESENT), boot_pd1 + 8
# set pdpte1 and pml4e1
movl $(boot_pd1 + PG_READ_WRITE + PG_PRESENT), boot_pdpt1
movl $(boot_pdpt1 + PG_READ_WRITE + PG_PRESENT), boot_pml4
# enable PAE
movl %cr4, %ecx
orl $0x20, %ecx
movl %ecx, %cr4
# set long mode enable bit
movl $0x100, %eax
movl $0x000, %edx
movl $0xC0000080, %ecx
wrmsr
# set address of paging structures
movl $boot_pml4, %ecx
movl %ecx, %cr3
# enable paging
movl %cr0, %ecx
orl $0x80000000, %ecx
movl %ecx, %cr0
ret
.global _start
.type _start, @function
_start:
# Initialize stack and multiboot info
movl $stack_top, %esp
movl %eax, g_multiboot_magic
movl %ebx, g_multiboot_info
call copy_kernel_commandline
call check_requirements
call enable_sse
call initialize_paging
# flush gdt and jump to 64 bit
lgdt boot_gdtr
ljmpl $0x08, $long_mode
.code64
long_mode:
# clear segment registers
movw $0x00, %ax
movw %ax, %ss
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
# call global constuctors
call _init
# call to the kernel itself (clear ebp for stacktrace)
xorq %rbp, %rbp
call kernel_main
# call global destructors
call _fini
system_halt:
xchgw %bx, %bx
cli
1: hlt
jmp 1b

16
kernel/arch/x86_64/crti.S Normal file
View File

@ -0,0 +1,16 @@
/* x86-64 crti.s */
.section .init
.global _init
.type _init, @function
_init:
pushq %rbp
movq %rsp, %rbp
/* gcc will nicely put the contents of crtbegin.o's .init section here. */
.section .fini
.global _fini
.type _fini, @function
_fini:
pushq %rbp
movq %rsp, %rbp
/* gcc will nicely put the contents of crtbegin.o's .fini section here. */

10
kernel/arch/x86_64/crtn.S Normal file
View File

@ -0,0 +1,10 @@
/* x86-64 crtn.s */
.section .init
/* gcc will nicely put the contents of crtend.o's .init section here. */
popq %rbp
ret
.section .fini
/* gcc will nicely put the contents of crtend.o's .fini section here. */
popq %rbp
ret

View File

@ -0,0 +1,128 @@
.macro pushaq
pushq %rax
pushq %rbx
pushq %rcx
pushq %rdx
pushq %rbp
pushq %rdi
pushq %rsi
pushq %r8
pushq %r9
pushq %r10
pushq %r11
pushq %r12
pushq %r13
pushq %r14
pushq %r15
.endm
.macro popaq
popq %r15
popq %r14
popq %r13
popq %r12
popq %r11
popq %r10
popq %r9
popq %r8
popq %rsi
popq %rdi
popq %rbp
popq %rdx
popq %rcx
popq %rbx
popq %rax
.endm
isr_stub:
pushaq
movq 120(%rsp), %rdi
movq 128(%rsp), %rsi
call cpp_isr_handler
popaq
addq $16, %rsp
iretq
irq_stub:
pushaq
movq 120(%rsp), %rdi
call cpp_irq_handler
popaq
addq $16, %rsp
iretq
.macro isr n
.global isr\n
isr\n:
cli
pushq $0
pushq $\n
jmp isr_stub
.endm
.macro isr_err n
.global isr\n
isr\n:
cli
pushq $\n
jmp isr_stub
.endm
.macro irq n
.global irq\n
irq\n:
cli
pushq $0
pushq $\n
jmp irq_stub
.endm
isr 0
isr 1
isr 2
isr 3
isr 4
isr 5
isr 6
isr 7
isr_err 8
isr 9
isr_err 10
isr_err 11
isr_err 12
isr_err 13
isr_err 14
isr 15
isr 16
isr_err 17
isr 18
isr 19
isr 20
isr 21
isr 22
isr 23
isr 24
isr 25
isr 26
isr 27
isr 28
isr 29
isr 30
isr 31
irq 0
irq 1
irq 2
irq 3
irq 4
irq 5
irq 6
irq 7
irq 8
irq 9
irq 10
irq 11
irq 12
irq 13
irq 14
irq 15

View File

@ -0,0 +1,27 @@
ENTRY (_start)
SECTIONS
{
. = 0x00100000;
.text BLOCK(4K) : ALIGN(4K)
{
*(.multiboot)
*(.text)
}
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
.data BLOCK(4K) : ALIGN(4K)
{
*(.data)
}
.bss BLOCK(4K) : ALIGN(4K)
{
*(COMMON)
*(.bss)
}
g_kernel_end = .;
}

View File

@ -0,0 +1,11 @@
KERNEL_ARCH_CFLAGS=-mcmodel=large -mno-red-zone -mno-mmx
KERNEL_ARCH_CPPFLAGS=
KERNEL_ARCH_LDFLAGS=-z max-page-size=4096
KERNEL_ARCH_LIBS=
KERNEL_ARCH_OBJS= \
$(ARCHDIR)/boot.o \
$(ARCHDIR)/IDT.o \
$(ARCHDIR)/MMU.o \
$(ARCHDIR)/interrupts.o \

View File

@ -0,0 +1,8 @@
ARCH_CFLAGS=
ARCH_CPPFLAGS=
KERNEL_ARCH_CFLAGS=
KERNEL_ARCH_CPPFLAGS=
ARCH_FREEOBJS=\
ARCH_HOSTEDOBJS=\