Kernel: Unify IDT and GDT code between x86_64 and x86_32

The code is pretty much the same, so there are just couple macros
differiating initialization.
This commit is contained in:
Bananymous 2024-03-26 13:59:09 +02:00
parent af050cc729
commit 1943c3e7a1
12 changed files with 335 additions and 147 deletions

View File

@ -33,6 +33,8 @@ set(KERNEL_SOURCES
kernel/FS/TmpFS/FileSystem.cpp
kernel/FS/TmpFS/Inode.cpp
kernel/FS/VirtualFileSystem.cpp
kernel/GDT.cpp
kernel/IDT.cpp
kernel/Input/KeyboardLayout.cpp
kernel/Input/KeyEvent.cpp
kernel/Input/PS2/Controller.cpp
@ -105,8 +107,6 @@ if("${BANAN_ARCH}" STREQUAL "x86_64")
set(KERNEL_SOURCES
${KERNEL_SOURCES}
arch/x86_64/boot.S
arch/x86_64/GDT.cpp
arch/x86_64/IDT.cpp
arch/x86_64/interrupts.S
arch/x86_64/PageTable.cpp
arch/x86_64/Signal.S
@ -124,8 +124,7 @@ elseif("${BANAN_ARCH}" STREQUAL "i686")
set(KERNEL_SOURCES
${KERNEL_SOURCES}
arch/i686/boot.S
arch/i686/GDT.cpp
arch/i686/IDT.cpp
arch/i686/interrupts.S
arch/i686/PageTable.cpp
arch/i686/Signal.S
arch/i686/Syscall.S

View File

@ -1,21 +0,0 @@
#include <kernel/GDT.h>
namespace Kernel
{
GDT* GDT::create()
{
ASSERT_NOT_REACHED();
}
void GDT::write_entry(uint8_t, uint32_t, uint32_t, uint8_t, uint8_t)
{
ASSERT_NOT_REACHED();
}
void GDT::write_tss()
{
ASSERT_NOT_REACHED();
}
}

View File

@ -1,31 +0,0 @@
#include <kernel/IDT.h>
namespace Kernel
{
IDT* IDT::create()
{
ASSERT_NOT_REACHED();
}
[[noreturn]] void IDT::force_triple_fault()
{
ASSERT_NOT_REACHED();
}
void IDT::register_irq_handler(uint8_t, Interruptable*)
{
ASSERT_NOT_REACHED();
}
void IDT::register_interrupt_handler(uint8_t, void (*)())
{
ASSERT_NOT_REACHED();
}
void IDT::register_syscall_handler(uint8_t, void (*)())
{
ASSERT_NOT_REACHED();
}
}

View File

@ -0,0 +1,134 @@
isr_stub:
ud2
pusha
call cpp_isr_handler
popa
iret
irq_stub:
ud2
pusha
call cpp_irq_handler
popa
iret
// arguments in EAX, EBX, ECX, EDX, ESI, EDI
.global syscall_asm
syscall_asm:
ud2
pusha
pushl %esp
addl $36, (%esp)
pushl %edi
pushl %esi
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax
call cpp_syscall_handler
addl $60, %esp
popl %edi
popl %esi
popl %ebp
addl $4, %esp
popl %ebx
popl %edx
popl %ecx
addl $4, %esp
iret
.macro isr n
.global isr\n
isr\n:
pushl $0
pushl $\n
jmp isr_stub
.endm
.macro isr_err n
.global isr\n
isr\n:
pushl $\n
jmp isr_stub
.endm
.macro irq n
.global irq\n
irq\n:
pushl $0
pushl $\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
irq 16
irq 17
irq 18
irq 19
irq 20
irq 21
irq 22
irq 23
irq 24
irq 25
irq 26
irq 27
irq 28
irq 29
irq 30
irq 31
irq 32

View File

@ -1,56 +0,0 @@
#include <kernel/GDT.h>
#include <kernel/Debug.h>
#include <string.h>
namespace Kernel
{
GDT* GDT::create()
{
auto* gdt = new GDT();
ASSERT(gdt);
gdt->write_entry(0x00, 0x00000000, 0x00000, 0x00, 0x0); // null
gdt->write_entry(0x08, 0x00000000, 0xFFFFF, 0x9A, 0xA); // kernel code
gdt->write_entry(0x10, 0x00000000, 0xFFFFF, 0x92, 0xC); // kernel data
gdt->write_entry(0x18, 0x00000000, 0xFFFFF, 0xFA, 0xA); // user code
gdt->write_entry(0x20, 0x00000000, 0xFFFFF, 0xF2, 0xC); // user data
gdt->write_tss();
return gdt;
}
void GDT::write_entry(uint8_t offset, uint32_t base, uint32_t limit, uint8_t access, uint8_t flags)
{
ASSERT(offset % sizeof(SegmentDescriptor) == 0);
uint8_t idx = offset / sizeof(SegmentDescriptor);
auto& desc = m_gdt[idx];
desc.base1 = (base >> 0) & 0xFFFF;
desc.base2 = (base >> 16) & 0xFF;
desc.base3 = (base >> 24) & 0xFF;
desc.limit1 = (limit >> 0) & 0xFFFF;
desc.limit2 = (limit >> 16) & 0x0F;
desc.access = access & 0xFF;
desc.flags = flags & 0x0F;
}
void GDT::write_tss()
{
memset(&m_tss, 0x00, sizeof(TaskStateSegment));
m_tss.iopb = sizeof(TaskStateSegment);
uint64_t base = reinterpret_cast<uint64_t>(&m_tss);
write_entry(0x28, (uint32_t)base, sizeof(TaskStateSegment), 0x89, 0x0);
auto& desc = m_gdt[0x30 / sizeof(SegmentDescriptor)];
desc.low = base >> 32;
desc.high = 0;
}
}

View File

@ -2,12 +2,14 @@
#include <BAN/Array.h>
#include <BAN/NoCopyMove.h>
#include <kernel/Arch.h>
#include <stdint.h>
namespace Kernel
{
#if ARCH(x86_64)
struct TaskStateSegment
{
uint32_t reserved1;
@ -26,6 +28,54 @@ namespace Kernel
uint16_t reserved4;
uint16_t iopb;
} __attribute__((packed));
static_assert(sizeof(TaskStateSegment) == 104);
#elif ARCH(i686)
struct TaskStateSegment
{
uint16_t link;
uint16_t __reserved0;
uint32_t esp0;
uint16_t ss0;
uint16_t __reserved1;
uint32_t esp1;
uint16_t ss1;
uint16_t __reserved2;
uint32_t esp2;
uint16_t ss2;
uint16_t __reserved3;
uint32_t cr3;
uint32_t eip;
uint32_t eflags;
uint32_t eax;
uint32_t ecx;
uint32_t edx;
uint32_t ebx;
uint32_t esp;
uint32_t ebp;
uint32_t esi;
uint32_t edi;
uint16_t es;
uint16_t __reserved4;
uint16_t cs;
uint16_t __reserved5;
uint16_t ss;
uint16_t __reserved6;
uint16_t ds;
uint16_t __reserved7;
uint16_t fs;
uint16_t __reserved8;
uint16_t gs;
uint16_t __reserved9;
uint16_t ldtr;
uint16_t __reserved10;
uint16_t __reserved11;
uint16_t iopb;
uint32_t ssp;
};
static_assert(sizeof(TaskStateSegment) == 108);
#else
#error
#endif
union SegmentDescriptor
{
@ -38,20 +88,20 @@ namespace Kernel
uint8_t limit2 : 4;
uint8_t flags : 4;
uint8_t base3;
} __attribute__((packed));
};
struct
{
uint32_t low;
uint32_t high;
} __attribute__((packed));
} __attribute__((packed));
};
};
static_assert(sizeof(SegmentDescriptor) == 8);
struct GDTR
{
uint16_t size;
uint64_t address;
uintptr_t address;
} __attribute__((packed));
class GDT
@ -60,7 +110,7 @@ namespace Kernel
BAN_NON_MOVABLE(GDT);
public:
static GDT* create();
static GDT* create(void* processor);
void load() { flush_gdt(); flush_tss(); }
static constexpr inline bool is_user_segment(uint8_t segment)
@ -68,9 +118,13 @@ namespace Kernel
return (segment & 3) == 3;
}
void set_tss_stack(uintptr_t rsp)
void set_tss_stack(uintptr_t sp)
{
m_tss.rsp0 = rsp;
#if ARCH(x86_64)
m_tss.rsp0 = sp;
#elif ARCH(i686)
m_tss.esp0 = sp;
#endif
}
private:
@ -86,15 +140,21 @@ namespace Kernel
void flush_tss()
{
asm volatile("ltr %0" :: "rm"((uint16_t)0x28) : "memory");
asm volatile("ltr %0" :: "rm"(m_tss_offset) : "memory");
}
private:
#if ARCH(x86_64)
BAN::Array<SegmentDescriptor, 7> m_gdt; // null, kernel code, kernel data, user code, user data, tss low, tss high
static constexpr uint16_t m_tss_offset = 0x28;
#elif ARCH(i686)
BAN::Array<SegmentDescriptor, 7> m_gdt; // null, kernel code, kernel data, user code, user data, processor data, tss
static constexpr uint16_t m_tss_offset = 0x30;
#endif
TaskStateSegment m_tss;
const GDTR m_gdtr {
.size = m_gdt.size() * sizeof(SegmentDescriptor) - 1,
.address = reinterpret_cast<uint64_t>(m_gdt.data())
.address = reinterpret_cast<uintptr_t>(m_gdt.data())
};
};

View File

@ -2,6 +2,7 @@
#include <BAN/Array.h>
#include <BAN/NoCopyMove.h>
#include <kernel/Arch.h>
#include <kernel/Interruptable.h>
#include <stdint.h>
@ -12,21 +13,36 @@ constexpr uint8_t IRQ_IPI = 32;
namespace Kernel
{
#if ARCH(x86_64)
struct GateDescriptor
{
uint16_t offset1;
uint16_t offset0;
uint16_t selector;
uint8_t IST;
uint8_t flags;
uint16_t offset2;
uint32_t offset3;
uint16_t offset1;
uint32_t offset2;
uint32_t reserved;
} __attribute__((packed));
};
static_assert(sizeof(GateDescriptor) == 16);
#elif ARCH(i686)
struct GateDescriptor
{
uint16_t offset0;
uint16_t selector;
uint8_t reserved;
uint8_t flags;
uint16_t offset1;
};
static_assert(sizeof(GateDescriptor) == 8);
#else
#error
#endif
struct IDTR
{
uint16_t size;
uint64_t offset;
uintptr_t offset;
} __attribute__((packed));
class IDT

View File

@ -18,6 +18,7 @@ namespace Kernel
virtual bool is_in_service(uint8_t) = 0;
static void initialize(bool force_pic);
static bool is_initialized();
static InterruptController& get();
virtual void initialize_multiprocessor() = 0;

69
kernel/kernel/GDT.cpp Normal file
View File

@ -0,0 +1,69 @@
#include <kernel/GDT.h>
#include <kernel/Processor.h>
#include <string.h>
namespace Kernel
{
GDT* GDT::create([[maybe_unused]] void* processor)
{
auto* gdt = new GDT();
ASSERT(gdt);
#if ARCH(x86_64)
constexpr uint8_t code_flags = 0xA;
constexpr uint8_t data_flags = 0xC;
#elif ARCH(i686)
constexpr uint8_t code_flags = 0xC;
constexpr uint8_t data_flags = 0xC;
#endif
gdt->write_entry(0x00, 0x00000000, 0x00000, 0x00, 0x0); // null
gdt->write_entry(0x08, 0x00000000, 0xFFFFF, 0x9A, code_flags); // kernel code
gdt->write_entry(0x10, 0x00000000, 0xFFFFF, 0x92, data_flags); // kernel data
gdt->write_entry(0x18, 0x00000000, 0xFFFFF, 0xFA, code_flags); // user code
gdt->write_entry(0x20, 0x00000000, 0xFFFFF, 0xF2, data_flags); // user data
#if ARCH(i686)
gdt->write_entry(0x28, reinterpret_cast<uint32_t>(processor), sizeof(Processor), 0x92, 0x4); // processor data
#endif
gdt->write_tss();
return gdt;
}
void GDT::write_entry(uint8_t offset, uint32_t base, uint32_t limit, uint8_t access, uint8_t flags)
{
ASSERT(offset % sizeof(SegmentDescriptor) == 0);
uint8_t idx = offset / sizeof(SegmentDescriptor);
auto& desc = m_gdt[idx];
desc.base1 = (base >> 0) & 0xFFFF;
desc.base2 = (base >> 16) & 0xFF;
desc.base3 = (base >> 24) & 0xFF;
desc.limit1 = (limit >> 0) & 0xFFFF;
desc.limit2 = (limit >> 16) & 0x0F;
desc.access = access & 0xFF;
desc.flags = flags & 0x0F;
}
void GDT::write_tss()
{
memset(&m_tss, 0x00, sizeof(TaskStateSegment));
m_tss.iopb = sizeof(TaskStateSegment);
uintptr_t base = reinterpret_cast<uintptr_t>(&m_tss);
write_entry(m_tss_offset, (uint32_t)base, sizeof(TaskStateSegment), 0x89, 0x0);
#if ARCH(x86_64)
auto& desc = m_gdt[(m_tss_offset + 8) / sizeof(SegmentDescriptor)];
desc.low = base >> 32;
desc.high = 0;
#endif
}
}

View File

@ -141,21 +141,16 @@ namespace Kernel
"Unkown Exception 0x1F",
};
extern "C" void cpp_isr_handler(uint64_t isr, uint64_t error, InterruptStack& interrupt_stack, const Registers* regs)
extern "C" void cpp_isr_handler(uint32_t isr, uint32_t error, InterruptStack& interrupt_stack, const Registers* regs)
{
if (g_paniced)
{
dprintln("Processor {} halted", Processor::current_id());
InterruptController::get().broadcast_ipi();
if (InterruptController::is_initialized())
InterruptController::get().broadcast_ipi();
asm volatile("cli; 1: hlt; jmp 1b");
}
#if __enable_sse
bool from_userspace = (interrupt_stack.cs & 0b11) == 0b11;
if (from_userspace)
Thread::current().save_sse();
#endif
pid_t tid = Scheduler::current_tid();
pid_t pid = tid ? Process::current().pid() : 0;
@ -209,11 +204,19 @@ namespace Kernel
#if __enable_sse
else if (isr == ISR::DeviceNotAvailable)
{
#if ARCH(x86_64)
asm volatile(
"movq %cr0, %rax;"
"andq $~(1 << 3), %rax;"
"movq %rax, %cr0;"
);
#elif ARCH(i686)
asm volatile(
"movl %cr0, %eax;"
"andl $~(1 << 3), %eax;"
"movl %eax, %cr0;"
);
#endif
if (auto* current = &Thread::current(); current != Thread::sse_thread())
{
if (auto* sse = Thread::sse_thread())
@ -302,7 +305,8 @@ done:
if (g_paniced)
{
dprintln("Processor {} halted", Processor::current_id());
InterruptController::get().broadcast_ipi();
if (InterruptController::is_initialized())
InterruptController::get().broadcast_ipi();
asm volatile("cli; 1: hlt; jmp 1b");
}
@ -332,14 +336,17 @@ done:
void IDT::register_interrupt_handler(uint8_t index, void (*handler)())
{
auto& descriptor = m_idt[index];
descriptor.offset1 = (uint16_t)((uint64_t)handler >> 0);
descriptor.offset2 = (uint16_t)((uint64_t)handler >> 16);
descriptor.offset3 = (uint32_t)((uint64_t)handler >> 32);
auto& desc = m_idt[index];
memset(&desc, 0, sizeof(GateDescriptor));
descriptor.selector = 0x08;
descriptor.IST = 0;
descriptor.flags = 0x8E;
desc.offset0 = (uint16_t)((uintptr_t)handler >> 0);
desc.offset1 = (uint16_t)((uintptr_t)handler >> 16);
#if ARCH(x86_64)
desc.offset2 = (uint32_t)((uintptr_t)handler >> 32);
#endif
desc.selector = 0x08;
desc.flags = 0x8E;
}
void IDT::register_syscall_handler(uint8_t index, void (*handler)())

View File

@ -40,6 +40,11 @@ namespace Kernel
s_instance->m_using_apic = false;
}
bool InterruptController::is_initialized()
{
return s_instance;
}
void InterruptController::enter_acpi_mode()
{
#if ARCH(x86_64)

View File

@ -38,7 +38,7 @@ namespace Kernel
processor.m_stack = kmalloc(s_stack_size, 4096, true);
ASSERT(processor.m_stack);
processor.m_gdt = GDT::create();
processor.m_gdt = GDT::create(&processor);
ASSERT(processor.m_gdt);
processor.m_idt = IDT::create();
@ -52,14 +52,19 @@ namespace Kernel
auto id = read_processor_id();
auto& processor = s_processors[id];
ASSERT(processor.m_gdt);
processor.m_gdt->load();
// initialize GS
#if ARCH(x86_64)
// set gs base to pointer to this processor
uint64_t ptr = reinterpret_cast<uint64_t>(&processor);
uint32_t ptr_hi = ptr >> 32;
uint32_t ptr_lo = ptr & 0xFFFFFFFF;
asm volatile("wrmsr" :: "d"(ptr_hi), "a"(ptr_lo), "c"(MSR_IA32_GS_BASE));
ASSERT(processor.m_gdt);
processor.gdt().load();
#elif ARCH(i686)
asm volatile("movw $0x28, %%ax; movw %%ax, %%gs" ::: "ax");
#endif
ASSERT(processor.m_idt);
processor.idt().load();