mod context;
mod gdt;
mod init;
mod interrupt;
mod io;
mod user;

pub(self) mod percpu;

pub use self::context::*;
pub use self::gdt::*;
pub use self::init::*;
pub use self::interrupt::*;
pub use self::io::*;
pub use self::user::*;
pub use percpu_macros::define_percpu;

use core::arch::asm;

#[inline(always)]
pub fn flush_tlb(vaddr: usize) {
    unsafe {
        asm!(
            "invlpg ({})",
            in(reg) vaddr,
            options(att_syntax)
        );
    }
}

#[inline(always)]
pub fn flush_tlb_all() {
    unsafe {
        asm!(
            "mov %cr3, %rax",
            "mov %rax, %cr3",
            out("rax") _,
            options(att_syntax)
        );
    }
}

#[inline(always)]
pub fn get_root_page_table() -> usize {
    let cr3: usize;
    unsafe {
        asm!(
            "mov %cr3, {0}",
            out(reg) cr3,
            options(att_syntax)
        );
    }
    cr3
}

#[inline(always)]
pub fn set_root_page_table(pfn: usize) {
    unsafe {
        asm!(
            "mov {0}, %cr3",
            in(reg) pfn,
            options(att_syntax)
        );
    }
}

#[inline(always)]
pub fn get_page_fault_address() -> usize {
    let cr2: usize;
    unsafe {
        asm!(
            "mov %cr2, {}",
            out(reg) cr2,
            options(att_syntax)
        );
    }
    cr2
}

#[inline(always)]
pub fn halt() {
    unsafe {
        asm!("hlt", options(att_syntax, nostack));
    }
}

#[inline(always)]
pub fn pause() {
    unsafe {
        asm!("pause", options(att_syntax, nostack));
    }
}

#[inline(always)]
pub fn freeze() -> ! {
    loop {
        interrupt::disable_irqs();
        halt();
    }
}

#[inline(always)]
pub fn rdmsr(msr: u32) -> u64 {
    let edx: u32;
    let eax: u32;

    unsafe {
        asm!(
            "rdmsr",
            in("ecx") msr,
            out("eax") eax,
            out("edx") edx,
            options(att_syntax),
        );
    }

    (edx as u64) << 32 | eax as u64
}

#[inline(always)]
pub fn wrmsr(msr: u32, value: u64) {
    let eax = value as u32;
    let edx = (value >> 32) as u32;

    unsafe {
        asm!(
            "wrmsr",
            in("ecx") msr,
            in("eax") eax,
            in("edx") edx,
            options(att_syntax),
        );
    }
}
