zodiac 0.1.0

Rust OS framework that offers safe interfaces for kernel development
Documentation
use spin::Once;
use x86_64::{
    PrivilegeLevel, VirtAddr,
    registers::{
        control::{Efer, EferFlags},
        model_specific::{LStar, SFMask, Star},
        rflags::RFlags,
    },
    structures::gdt::SegmentSelector,
};

use crate::hal::{cpu::Cpu, smp::CPUS};

pub type SyscallHandler = fn(
    syscall_id: usize,
    arg1: usize,
    arg2: usize,
    arg3: usize,
    arg4: usize,
    arg5: usize,
    arg6: usize,
) -> isize;

static SYSCALL_HANDLER: Once<SyscallHandler> = Once::new();

/// Set the syscall handler. So that it will be called when syscalls are invoked.
pub fn set_syscall_handler(handler: SyscallHandler) {
    SYSCALL_HANDLER.call_once(|| handler);
}

pub fn init() {
    SFMask::write(RFlags::INTERRUPT_FLAG);
    LStar::write(VirtAddr::from_ptr(syscall_handler as *const ()));

    CPUS.with_cpu_info(Cpu::current(), |cpu_info| {
        Star::write(
            SegmentSelector::new(
                cpu_info.user_code_selector() as u16 >> 3,
                PrivilegeLevel::Ring3,
            ),
            SegmentSelector::new(
                cpu_info.user_data_selector() as u16 >> 3,
                PrivilegeLevel::Ring3,
            ),
            SegmentSelector::new(
                cpu_info.kernel_code_selector() as u16 >> 3,
                PrivilegeLevel::Ring0,
            ),
            SegmentSelector::new(
                cpu_info.kernel_data_selector() as u16 >> 3,
                PrivilegeLevel::Ring0,
            ),
        )
        .unwrap();
    });

    unsafe {
        Efer::write(Efer::read() | EferFlags::SYSTEM_CALL_EXTENSIONS);
    }
}

#[unsafe(naked)]
unsafe extern "C" fn syscall_handler() {
    core::arch::naked_asm!(
        "push rcx",
        "push r11",

        "mov rcx, r10",

        "call {syscall_matcher}",

        "pop r11",
        "pop rcx",

        "sysretq",
        syscall_matcher = sym syscall_matcher,
    );
}

#[allow(unused_variables)]
pub extern "C" fn syscall_matcher(
    arg1: usize,
    arg2: usize,
    arg3: usize,
    arg4: usize,
    arg5: usize,
    arg6: usize,
) -> isize {
    let syscall_index: usize;
    unsafe { core::arch::asm!("mov {0}, rax", out(reg) syscall_index) };

    match SYSCALL_HANDLER.get() {
        Some(handler) => handler(syscall_index, arg1, arg2, arg3, arg4, arg5, arg6),
        None => panic!("No syscall handler"),
    }
}