Commit Diff


commit - 650c79ed1d1a5f16ff3ad46e4a32c1a75d608124
commit + 86eeeaadbb2bf02bbf0095e1e915e69bea6db6e0
blob - /dev/null
blob + d9319cf663549a05056320f2c6b8a9c2f3a1eb7a (mode 644)
--- /dev/null
+++ src/gdt.rs
@@ -0,0 +1,169 @@
+// vim: set tw=79 cc=80 ts=4 sw=4 sts=4 et :
+//
+// Copyright (c) 2025-2026 Murilo Ijanc' <murilo@ijanc.org>
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+
+use core::arch::asm;
+
+// Segment selectors: index * 8 + RPL
+pub const KERNEL_CODE: u16 = 0x08; // index 1, RPL 0
+pub const KERNEL_DATA: u16 = 0x10; // index 2, RPL 0
+pub const USER_DATA: u16 = 0x1B; // index 3, RPL 3
+pub const USER_CODE: u16 = 0x23; // index 4, RPL 3
+pub const TSS_SEL: u16 = 0x28; // index 5
+
+// GDT with 7 entries (TSS uses 2 entries = 16 bytes):
+//   [0] null
+//   [1] kernel code 64-bit
+//   [2] kernel data
+//   [3] user data   (DPL=3)
+//   [4] user code   (DPL=3, 64-bit)
+//   [5-6] TSS       (16-byte system descriptor)
+//
+// Order of user data before user code is required by
+// sysret which computes: SS = STAR[63:48]+8, CS = STAR[63:48]+16
+#[repr(C, align(16))]
+struct Gdt {
+    entries: [u64; 7],
+}
+
+static mut GDT: Gdt = Gdt {
+    entries: [
+        // [0] null
+        0x0000000000000000,
+        // [1] kernel code: P=1 DPL=0 S=1 Type=1010 L=1
+        0x00AF9A000000FFFF,
+        // [2] kernel data: P=1 DPL=0 S=1 Type=0010
+        0x00CF92000000FFFF,
+        // [3] user data: P=1 DPL=3 S=1 Type=0010
+        0x00CFF2000000FFFF,
+        // [4] user code: P=1 DPL=3 S=1 Type=1010 L=1
+        0x00AFFA000000FFFF,
+        // [5-6] TSS: filled at runtime
+        0,
+        0,
+    ],
+};
+
+// TSS: only rsp0 matters for Ring3→Ring0 transition
+#[repr(C, packed)]
+pub struct Tss {
+    reserved0: u32,
+    pub rsp0: u64,
+    rsp1: u64,
+    rsp2: u64,
+    reserved1: u64,
+    ist: [u64; 7],
+    reserved2: u64,
+    reserved3: u16,
+    iopb_offset: u16,
+}
+
+pub static mut TSS: Tss = Tss {
+    reserved0: 0,
+    rsp0: 0,
+    rsp1: 0,
+    rsp2: 0,
+    reserved1: 0,
+    ist: [0; 7],
+    reserved2: 0,
+    reserved3: 0,
+    iopb_offset: size_of::<Tss>() as u16,
+};
+
+#[repr(C, packed)]
+struct GdtPointer {
+    limit: u16,
+    base: u64,
+}
+
+pub fn init() {
+    // Install TSS descriptor into GDT entries [5] and [6]
+    let tss_addr = unsafe { (&raw const TSS) as u64 };
+    let tss_size = (size_of::<Tss>() - 1) as u64;
+
+    // TSS descriptor is 16 bytes (2 GDT entries):
+    // Low 8 bytes:
+    //   bits 15:0  = limit 15:0
+    //   bits 31:16 = base 15:0
+    //   bits 39:32 = base 23:16
+    //   bits 43:40 = type (0x9 = available 64-bit TSS)
+    //   bit 44     = 0 (system segment)
+    //   bits 46:45 = DPL (0)
+    //   bit 47     = P (1)
+    //   bits 51:48 = limit 19:16
+    //   bits 55:52 = flags (0)
+    //   bits 63:56 = base 31:24
+    // High 8 bytes:
+    //   bits 31:0  = base 63:32
+    //   bits 63:32 = reserved
+    let low: u64 = (tss_size & 0xFFFF)
+        | ((tss_addr & 0xFFFF) << 16)
+        | (((tss_addr >> 16) & 0xFF) << 32)
+        | (0x89u64 << 40) // P=1, DPL=0, type=9
+        | (((tss_size >> 16) & 0xF) << 48)
+        | (((tss_addr >> 24) & 0xFF) << 56);
+    let high: u64 = tss_addr >> 32;
+
+    unsafe {
+        GDT.entries[5] = low;
+        GDT.entries[6] = high;
+    }
+
+    let ptr = GdtPointer {
+        limit: (size_of::<Gdt>() - 1) as u16,
+        base: (&raw const GDT) as u64,
+    };
+
+    unsafe {
+        asm!("lgdt [{}]",
+            in(reg) &ptr,
+            options(readonly, nostack));
+
+        // Reload data segments
+        asm!(
+            "mov ds, {0:x}",
+            "mov ss, {0:x}",
+            "mov es, {0:x}",
+            in(reg) KERNEL_DATA,
+            options(nostack),
+        );
+
+        // Reload CS via far return
+        asm!(
+            "push {sel}",
+            "lea {tmp}, [rip + 2f]",
+            "push {tmp}",
+            "retfq",
+            "2:",
+            sel = in(reg) KERNEL_CODE as u64,
+            tmp = lateout(reg) _,
+        );
+
+        // Load TSS
+        asm!("ltr {0:x}",
+            in(reg) TSS_SEL,
+            options(nostack));
+    }
+}
+
+/// Set the kernel stack pointer in the TSS.
+/// Called during task switch to point to the current
+/// task's kernel stack.
+pub fn set_kernel_stack(rsp0: u64) {
+    unsafe {
+        TSS.rsp0 = rsp0;
+    }
+}
blob - /dev/null
blob + 364527ea744f608b52cc96e7384d7d509a5500af (mode 644)
--- /dev/null
+++ src/port.rs
@@ -0,0 +1,32 @@
+// vim: set tw=79 cc=80 ts=4 sw=4 sts=4 et :
+//
+// Copyright (c) 2025-2026 Murilo Ijanc' <murilo@ijanc.org>
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+
+use core::arch::asm;
+
+pub unsafe fn outb(port: u16, val: u8) {
+    unsafe {
+        asm!("out dx, al", in("dx") port, in("al") val);
+    }
+}
+
+pub unsafe fn inb(port: u16) -> u8 {
+    let val: u8;
+    unsafe {
+        asm!("in al, dx", out("al") val, in("dx") port);
+    }
+    val
+}
blob - /dev/null
blob + f15253162f4b2a0f2023ae7849abc638d09d38ef (mode 644)
--- /dev/null
+++ src/serial.rs
@@ -0,0 +1,48 @@
+// vim: set tw=79 cc=80 ts=4 sw=4 sts=4 et :
+//
+// Copyright (c) 2025-2026 Murilo Ijanc' <murilo@ijanc.org>
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+
+use crate::port::{inb, outb};
+
+const COM1: u16 = 0x3F8;
+
+pub fn init() {
+    unsafe {
+        outb(COM1 + 1, 0x00);
+        outb(COM1 + 3, 0x80);
+        outb(COM1 + 0, 0x03);
+        outb(COM1 + 1, 0x00);
+        outb(COM1 + 3, 0x03);
+        outb(COM1 + 2, 0xC7);
+        outb(COM1 + 4, 0x0B);
+    }
+}
+
+pub fn putc(c: u8) {
+    while unsafe { inb(COM1 + 5) } & 0x20 == 0 {}
+    unsafe {
+        outb(COM1, c);
+    }
+}
+
+pub fn print(s: &str) {
+    for b in s.bytes() {
+        if b == b'\n' {
+            putc(b'\r');
+        }
+        putc(b);
+    }
+}
blob - /dev/null
blob + 550361f9f9a0cb7faa313f49466386561a8bc713 (mode 644)
--- /dev/null
+++ src/vga.rs
@@ -0,0 +1,28 @@
+// vim: set tw=79 cc=80 ts=4 sw=4 sts=4 et :
+//
+// Copyright (c) 2025-2026 Murilo Ijanc' <murilo@ijanc.org>
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+
+const VGA_BUFFER: usize = 0xb8000;
+
+pub fn write(s: &str, color: u8) {
+    let vga = VGA_BUFFER as *mut u16;
+    for (i, b) in s.bytes().enumerate() {
+        unsafe {
+            vga.add(i)
+                .write_volatile((color as u16) << 8 | b as u16);
+        }
+    }
+}