From a9aa31f087ced04c58e12680e9678f39886414aa Mon Sep 17 00:00:00 2001
From: Erin <erin@erindesu.cz>
Date: Thu, 8 Dec 2022 01:26:43 +0100
Subject: [PATCH] Added GDT, IDT, sloop and some logging

---
 Cargo.lock                          |  1 +
 ableos/src/arch/x86_64/gdt.rs       |  1 +
 kernel/Cargo.toml                   |  1 +
 kernel/src/arch/x86_64/allocator.rs |  1 +
 kernel/src/arch/x86_64/gdt.rs       | 57 +++++++++++++++++++++++++++++
 kernel/src/arch/x86_64/idt.rs       | 52 ++++++++++++++++++++++++++
 kernel/src/arch/x86_64/memory.rs    |  2 +
 kernel/src/arch/x86_64/mod.rs       | 13 ++++++-
 kernel/src/kmain.rs                 |  3 +-
 kernel/src/lib.rs                   | 27 +++++++++++++-
 10 files changed, 154 insertions(+), 4 deletions(-)
 create mode 100644 kernel/src/arch/x86_64/gdt.rs
 create mode 100644 kernel/src/arch/x86_64/idt.rs

diff --git a/Cargo.lock b/Cargo.lock
index 3a8d5b2..819793c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -586,6 +586,7 @@ dependencies = [
  "limine",
  "linked_list_allocator",
  "log",
+ "pic8259",
  "slab",
  "spin 0.9.4",
  "uart_16550",
diff --git a/ableos/src/arch/x86_64/gdt.rs b/ableos/src/arch/x86_64/gdt.rs
index 5758117..b693293 100644
--- a/ableos/src/arch/x86_64/gdt.rs
+++ b/ableos/src/arch/x86_64/gdt.rs
@@ -39,6 +39,7 @@ pub fn init() {
     use x86_64::instructions::segmentation::{Segment, CS};
     use x86_64::instructions::tables::load_tss;
 
+    log::debug!("Initialising GDT");
     GDT.0.load();
     unsafe {
         CS::set_reg(GDT.1.code_selector);
diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml
index 2a111c8..830ba15 100644
--- a/kernel/Cargo.toml
+++ b/kernel/Cargo.toml
@@ -17,5 +17,6 @@ features = ["alloc"]
 
 [target.'cfg(target_arch = "x86_64")'.dependencies]
 limine = "0.1"
+pic8259 = "0.10"
 uart_16550 = "0.2"
 x86_64 = "0.14"
diff --git a/kernel/src/arch/x86_64/allocator.rs b/kernel/src/arch/x86_64/allocator.rs
index d92cb45..8cf17c1 100644
--- a/kernel/src/arch/x86_64/allocator.rs
+++ b/kernel/src/arch/x86_64/allocator.rs
@@ -12,6 +12,7 @@ use crate::allocator::{HEAP_SIZE, HEAP_START};
 static ALLOCATOR: LockedHeap = LockedHeap::empty();
 
 pub unsafe fn init_alloc() -> Result<(), MapToError<Size4KiB>> {
+    log::info!("Initialising kernel heap allocator");
     let page_range = Page::range_inclusive(
         Page::containing_address(VirtAddr::new(HEAP_START as u64)),
         Page::containing_address(VirtAddr::new(HEAP_START as u64) + HEAP_SIZE - 1u64),
diff --git a/kernel/src/arch/x86_64/gdt.rs b/kernel/src/arch/x86_64/gdt.rs
new file mode 100644
index 0000000..63ef734
--- /dev/null
+++ b/kernel/src/arch/x86_64/gdt.rs
@@ -0,0 +1,57 @@
+use spin::Lazy;
+use x86_64::{
+    structures::{
+        gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector},
+        tss::TaskStateSegment,
+    },
+    VirtAddr,
+};
+
+pub const DOUBLE_FAULT_IX: u16 = 0;
+
+pub unsafe fn init() {
+    use x86_64::instructions::segmentation::{Segment, CS, DS};
+    use x86_64::instructions::tables::load_tss;
+
+    log::info!("Initialising GDT");
+    GDT.0.load();
+    unsafe {
+        CS::set_reg(GDT.1.kcode);
+        DS::set_reg(GDT.1.kdata);
+        load_tss(GDT.1.tss);
+    }
+}
+
+struct Selectors {
+    kcode: SegmentSelector,
+    kdata: SegmentSelector,
+    tss: SegmentSelector,
+    udata: SegmentSelector,
+    ucode: SegmentSelector,
+}
+
+static TSS: Lazy<TaskStateSegment> = Lazy::new(|| {
+    let mut tss = TaskStateSegment::new();
+    tss.interrupt_stack_table[usize::from(DOUBLE_FAULT_IX)] = {
+        const SIZE: usize = 5 * 1024;
+        let stack = unsafe {
+            alloc::alloc::alloc_zeroed(
+                alloc::alloc::Layout::from_size_align(SIZE, 1).expect("stack pointer"),
+            )
+        };
+        VirtAddr::from_ptr(stack) + SIZE
+    };
+    tss
+});
+
+static GDT: Lazy<(GlobalDescriptorTable, Selectors)> = Lazy::new(|| {
+    let mut gdt = GlobalDescriptorTable::new();
+    let sels = Selectors {
+        kcode: gdt.add_entry(Descriptor::kernel_code_segment()),
+        kdata: gdt.add_entry(Descriptor::kernel_data_segment()),
+        tss: gdt.add_entry(Descriptor::tss_segment(&TSS)),
+        udata: gdt.add_entry(Descriptor::user_data_segment()),
+        ucode: gdt.add_entry(Descriptor::user_code_segment()),
+    };
+    (gdt, sels)
+});
diff --git a/kernel/src/arch/x86_64/idt.rs b/kernel/src/arch/x86_64/idt.rs
new file mode 100644
index 0000000..6eeb358
--- /dev/null
+++ b/kernel/src/arch/x86_64/idt.rs
@@ -0,0 +1,52 @@
+use pic8259::ChainedPics;
+use spin::{Lazy, Mutex};
+use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode};
+
+pub const PIC_1_OFFSET: u8 = 32;
+pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8;
+
+static PICS: Mutex<ChainedPics> =
+    Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) });
+
+pub unsafe fn init() {
+    log::info!("Initialising IDT");
+    IDT.load();
+    PICS.lock().initialize();
+    x86_64::instructions::interrupts::enable();
+}
+
+#[repr(u8)]
+enum Interrupt {
+    Timer = PIC_1_OFFSET,
+}
+
+static IDT: Lazy<InterruptDescriptorTable> = Lazy::new(|| {
+    let mut idt = InterruptDescriptorTable::new();
+    unsafe {
+        idt.double_fault
+            .set_handler_fn(double_fault)
+            .set_stack_index(super::gdt::DOUBLE_FAULT_IX);
+    }
+    idt.page_fault.set_handler_fn(page_fault);
+    idt[Interrupt::Timer as usize].set_handler_fn(timer);
+    idt
+});
+
+extern "x86-interrupt" fn double_fault(stack_frame: InterruptStackFrame, error_code: u64) -> ! {
+    panic!("Double fault: error code {error_code} \n{stack_frame:#?}")
+}
+
+extern "x86-interrupt" fn page_fault(
+    stack_frame: InterruptStackFrame,
+    error_code: PageFaultErrorCode,
+) {
+    panic!("Page fault ({error_code:?}): {stack_frame:?}")
+}
+
+extern "x86-interrupt" fn timer(_: InterruptStackFrame) {
+    unsafe { eoi(Interrupt::Timer as u8) };
+}
+
+unsafe fn eoi(i: u8) {
+    PICS.lock().notify_end_of_interrupt(i);
+}
diff --git a/kernel/src/arch/x86_64/memory.rs b/kernel/src/arch/x86_64/memory.rs
index 866bbde..f33e761 100644
--- a/kernel/src/arch/x86_64/memory.rs
+++ b/kernel/src/arch/x86_64/memory.rs
@@ -10,6 +10,7 @@ pub static FRAME_ALLOC: Once<Mutex<FrameAlloc>> = Once::new();
 
 /// Initialise page table
 pub unsafe fn init_pt(phys_base: VirtAddr) {
+    log::info!("Retrieving page table");
     PAGE_TABLE.call_once(|| {
         Mutex::new(OffsetPageTable::new(
             &mut *((phys_base
@@ -25,6 +26,7 @@ pub unsafe fn init_pt(phys_base: VirtAddr) {
 
 /// Initialise page frame allocator
 pub unsafe fn init_falloc(mmap: &'static [LimineMemmapEntry]) {
+    log::info!("Initialising frame allocator");
     FRAME_ALLOC.call_once(|| Mutex::new(FrameAlloc::new(mmap)));
 }
 
diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs
index 60b567f..921aa25 100644
--- a/kernel/src/arch/x86_64/mod.rs
+++ b/kernel/src/arch/x86_64/mod.rs
@@ -4,6 +4,8 @@ use uart_16550::SerialPort;
 use x86_64::VirtAddr;
 
 mod allocator;
+mod gdt;
+mod idt;
 mod memory;
 
 static SERIAL_CONSOLE: Mutex<SerialPort> = Mutex::new(unsafe { SerialPort::new(0x3f8) });
@@ -34,6 +36,8 @@ unsafe extern "C" fn _kernel_start() -> ! {
     );
 
     allocator::init_alloc().expect("tried to initialise allocator");
+    gdt::init();
+    idt::init();
 
     crate::kmain::kmain()
 }
@@ -41,5 +45,12 @@ unsafe extern "C" fn _kernel_start() -> ! {
 /// Format args to serial console
 pub fn serial_fmt(args: core::fmt::Arguments<'_>) -> core::fmt::Result {
     use core::fmt::Write;
-    SERIAL_CONSOLE.lock().write_fmt(args)
+    x86_64::instructions::interrupts::without_interrupts(|| SERIAL_CONSOLE.lock().write_fmt(args))
+}
+
+/// Spin loop
+pub fn sloop() -> ! {
+    loop {
+        x86_64::instructions::hlt();
+    }
 }
diff --git a/kernel/src/kmain.rs b/kernel/src/kmain.rs
index 577589a..b898fb8 100644
--- a/kernel/src/kmain.rs
+++ b/kernel/src/kmain.rs
@@ -1,5 +1,6 @@
 //! AbleOS Kernel Entrypoint
 
 pub fn kmain() -> ! {
-    loop {}
+    log::trace!("Entered kmain");
+    crate::arch::sloop()
 }
diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs
index 6b17431..e83f633 100644
--- a/kernel/src/lib.rs
+++ b/kernel/src/lib.rs
@@ -1,14 +1,19 @@
 //! The ableOS kernel.
 
-#![feature(alloc_error_handler, prelude_import)]
+#![feature(
+    abi_x86_interrupt,
+    alloc_error_handler,
+    panic_info_message,
+    prelude_import
+)]
 #![no_std]
 
 extern crate alloc;
 
 pub mod allocator;
 pub mod arch;
-pub mod logger;
 pub mod kmain;
+pub mod logger;
 pub mod task;
 
 use versioning::Version;
@@ -22,5 +27,23 @@ pub const VERSION: Version = Version {
 
 #[panic_handler]
 fn panic(info: &core::panic::PanicInfo) -> ! {
+    // TODO: Better panic handler
+    let _ = crate::arch::serial_fmt(format_args!(
+        "\x1b[1m\x1b[4m\x1b[38;5;125mKernel Panic\x1b[0m\r\n",
+    ));
+
+    if let Some(loc) = info.location() {
+        let _ = crate::arch::serial_fmt(format_args!(
+            "Location: {} : {} / {}\r\n",
+            loc.file(),
+            loc.line(),
+            loc.column()
+        ));
+    }
+
+    if let Some(msg) = info.message() {
+        let _ = crate::arch::serial_fmt(format_args!("{msg}\r\n"));
+    }
+
     loop {}
 }