summaryrefslogtreecommitdiff
path: root/kernel/src/interrupts/apic.rs
blob: 519caf25bed1652681955941f067bfff8f3957d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use x86_64::registers::model_specific::Msr;

#[cfg(target_arch = "x86_64")]
const APIC_FLAG: u64 = 0x0800;
const APIC_BASE_MSR: u32 = 0x1b;

pub fn is_x2apic() -> bool {
    let info: u32;
    unsafe {
        asm!("cpuid" : "={ecx}" (info) : "{eax}" (1) : "memory");
    }
    info & (1 << 21) != 0
}

pub fn is_apic() -> bool {
    let info: u32;
    unsafe {
        asm!("cpuid" : "={edx}" (info) : "{eax}" (1) : "memory");
    }
    info & (1 << 9) != 0
}

pub fn set_apic_base(v: u64) {
    unsafe { Msr::new(APIC_BASE_MSR).write(v) }
}

#[repr(C)]
pub struct InterruptCommand {
    low: u32,
    _rsv: [u32; 3],
    high: u32
}

#[repr(isize)]
pub enum ApicRegister {
    ApicId=8,
    ApicVer=12,
    TaskPriority=32,
    ArbitrationPriority=36,
    ProcessorPriority=40,
    EndOfInterrupt=44,
    LogicalDst=52,
    DstFmt=56,
    SpuriousInterruptVec=60,
    InService=64,
    TriggerMode=96,
    InterruptRequest=128,
    ErrorStatus=160,
    InterruptCommand=192,
    InterruptCommandHigh=196,
    LvtTimer=200,
    LvtThermalSensor=204,
    LvtPerformanceMonitor=208,
    LvtLint1=212,
    LvtError=220,
    TimerInitialCount=224,
    TimerCurrentCount=228,
    TimerDivideConfig=248,
    ExtApicFeature=256,
    ExtApicControl=260,
    SpecificEndOfInterrupt=264,
    InterruptEnable=288,
    ExtInterruptLocalVectorTable=320,
}

#[repr(C)]
pub struct ApicRegisters {
    regs32: *mut [u32; 1024],
}

impl ApicRegisters {
    fn from_base_apic(base_apic: u64) -> Self {
        Self { regs32:
            ((base_apic >> 12) & 0xfffffffff) as *mut [u32; 1024]
        }
    }

    fn ptr(&self) -> *const u32 { self.regs32 as *const u32 }
    fn ptr_mut(&mut self) -> *mut u32 { self.regs32 as *mut u32 }

    pub fn get(&self, register: ApicRegister) -> u32 {
        unsafe { self.ptr().offset(register as isize).read_volatile() }
    }

    pub fn set(&mut self, register: ApicRegister, val: u32) {
        unsafe { self.ptr_mut().offset(register as isize).write_volatile(val) }
    }

    pub fn set_interrupt_command(&mut self, dst: u8, v: u8) {
        let (dst, v): (u32, u32) = (dst.into(), v.into());
        self.set(ApicRegister::InterruptCommandHigh, dst << 24);
        self.set(ApicRegister::InterruptCommand, v)
    }
}

pub struct Apic {
    base_apic: u64,
    regs: ApicRegisters,
}

impl Apic {
    pub fn new() -> Option<Self> {
        use core::fmt::Write;
        let mut stdout = crate::io::vga_text::OStream::new();
        stdout.clear();

        if !is_apic() { return None; }
        let mut base_apic = unsafe { Msr::new(APIC_BASE_MSR).read() };
        let mut regs = ApicRegisters::from_base_apic(base_apic);

        let spurious = regs.get(ApicRegister::SpuriousInterruptVec) | 0x1ff;

        unsafe { Msr::new(APIC_BASE_MSR).write(base_apic | APIC_FLAG) };

        regs.set(ApicRegister::SpuriousInterruptVec, spurious);

        Some(Self {
            base_apic, regs
        })
    }
}