commit - 650c79ed1d1a5f16ff3ad46e4a32c1a75d608124
commit + 86eeeaadbb2bf02bbf0095e1e915e69bea6db6e0
blob - /dev/null
blob + d9319cf663549a05056320f2c6b8a9c2f3a1eb7a (mode 644)
--- /dev/null
+++ src/gdt.rs
+// 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
+// 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
+// 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
+// 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);
+ }
+ }
+}