initial apic implementaion (Commented out / NOT WORKING)
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
use core::arch::x86_64::__cpuid;
|
||||
|
||||
use x86_64::{PhysAddr, instructions::port::Port, registers::model_specific::Msr};
|
||||
|
||||
use super::cpu::model_specific_registers::*;
|
||||
|
||||
const IA32_APIC_BASE_MSR: u32 = 0x1b;
|
||||
const IA32_APIC_BASE_MSR_BSP: u64 = 0x100;
|
||||
const IA32_APIC_BASE_MSR_ENABLE: u64 = 0x800;
|
||||
|
||||
const CPUID_FEAT_EDX_APIC: u64 = 1 << 9; // the cpuid instruction will return this flag if it supports APIC
|
||||
|
||||
fn set_apic_base(apic: PhysAddr) {
|
||||
let rax = (apic.as_u64() & 0xfffff0000) | IA32_APIC_BASE_MSR_ENABLE;
|
||||
cpu_set_msr(IA32_APIC_BASE_MSR, rax);
|
||||
}
|
||||
|
||||
fn get_apic_base() -> PhysAddr {
|
||||
let mut value: u64 = 0;
|
||||
cpu_get_msr(IA32_APIC_BASE_MSR, &mut value);
|
||||
PhysAddr::new(value & 0xfffff0000)
|
||||
}
|
||||
|
||||
fn write_apic_register(reg: u8, value: u32) {
|
||||
let apic_base = get_apic_base().as_u64();
|
||||
let reg_addr = (apic_base & 0xFFFFF0000) + reg as u64;
|
||||
unsafe { *(reg_addr as *mut u32) = value };
|
||||
}
|
||||
|
||||
fn read_apic_register(reg: u8) -> u32 {
|
||||
let apic_base = get_apic_base().as_u64();
|
||||
let reg_addr = (apic_base & 0xFFFFF0000) + reg as u64;
|
||||
unsafe { *(reg_addr as *const u32) }
|
||||
}
|
||||
|
||||
pub fn check_apic() -> bool {
|
||||
let res = unsafe { __cpuid(1) };
|
||||
res.edx as u64 & CPUID_FEAT_EDX_APIC != 0
|
||||
}
|
||||
|
||||
pub fn enable_apic() {
|
||||
set_apic_base(get_apic_base());
|
||||
|
||||
write_apic_register(0xF0, read_apic_register(0xF0) | 0x100);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
pub mod model_specific_registers {
|
||||
use core::arch::x86_64::__cpuid;
|
||||
use spin::Lazy;
|
||||
use x86_64::registers::model_specific::Msr;
|
||||
|
||||
const CPUID_FLAG_MSR: u32 = 1 << 5;
|
||||
static EDX: Lazy<u32> = Lazy::new(|| unsafe { __cpuid(1).edx });
|
||||
|
||||
pub fn cpu_has_msr() -> bool {
|
||||
*EDX & CPUID_FLAG_MSR != 0
|
||||
}
|
||||
|
||||
pub fn cpu_get_msr(msr: u32, value: &mut u64) {
|
||||
let msr = Msr::new(msr);
|
||||
unsafe {
|
||||
*value = msr.read();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cpu_set_msr(msr: u32, value: u64) {
|
||||
let mut msr = Msr::new(msr);
|
||||
unsafe {
|
||||
msr.write(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
pub mod io;
|
||||
pub mod kalloc;
|
||||
pub mod scheduling;
|
||||
|
||||
pub mod apic;
|
||||
pub mod cpu;
|
||||
pub mod pic;
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
use x86_64::instructions::port::Port;
|
||||
|
||||
const CMD_INIT: u8 = 0x11;
|
||||
const CMD_END_OF_INT: u8 = 0x20;
|
||||
const MODE_8086: u8 = 0x01;
|
||||
|
||||
struct Pic {
|
||||
offset: u8,
|
||||
data: Port<u8>,
|
||||
command: Port<u8>,
|
||||
}
|
||||
|
||||
impl Pic {
|
||||
/// Are we in charge of handling the specified interrupt?
|
||||
/// (Each PIC handles 8 interrupts.)
|
||||
const fn handles_interrupt(&self, interrupt_id: u8) -> bool {
|
||||
self.offset <= interrupt_id && interrupt_id < self.offset + 8
|
||||
}
|
||||
|
||||
/// Notify us that an interrupt has been handled and that we're ready
|
||||
/// for more.
|
||||
unsafe fn end_of_interrupt(&mut self) {
|
||||
unsafe { self.command.write(CMD_END_OF_INT) };
|
||||
}
|
||||
|
||||
/// Reads the interrupt mask of this PIC.
|
||||
unsafe fn read_mask(&mut self) -> u8 {
|
||||
unsafe { self.data.read() }
|
||||
}
|
||||
|
||||
/// Writes the interrupt mask of this PIC.
|
||||
unsafe fn write_mask(&mut self, mask: u8) {
|
||||
unsafe { self.data.write(mask) }
|
||||
}
|
||||
}
|
||||
|
||||
/// A pair of chained PICs. This is the standard setup on x86.
|
||||
pub struct ChainedPics {
|
||||
pics: [Pic; 2],
|
||||
}
|
||||
|
||||
impl ChainedPics {
|
||||
pub const unsafe fn new(offset1: u8, offset2: u8) -> Self {
|
||||
ChainedPics {
|
||||
pics: [
|
||||
Pic {
|
||||
offset: offset1,
|
||||
command: Port::new(0x20),
|
||||
data: Port::new(0x21),
|
||||
},
|
||||
Pic {
|
||||
offset: offset2,
|
||||
command: Port::new(0xA0),
|
||||
data: Port::new(0xA1),
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
/// .
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// .
|
||||
pub const unsafe fn new_contiguous(primary_offset: u8) -> Self {
|
||||
unsafe { Self::new(primary_offset, primary_offset + 8) }
|
||||
}
|
||||
|
||||
/// Returns the initialize of this [`ChainedPics`].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// .
|
||||
pub unsafe fn initialize(&mut self) {
|
||||
unsafe {
|
||||
let mut wait_port: Port<u8> = Port::new(0x80);
|
||||
let mut wait = || wait_port.write(0);
|
||||
|
||||
// Save our original interrupt masks, because I'm too lazy to
|
||||
// figure out reasonable values. We'll restore these when we're
|
||||
// done.
|
||||
let saved_masks = self.read_masks();
|
||||
|
||||
// Tell each PIC that we're going to send it a three-byte
|
||||
// initialization sequence on its data port.
|
||||
self.pics[0].command.write(CMD_INIT);
|
||||
wait();
|
||||
self.pics[1].command.write(CMD_INIT);
|
||||
wait();
|
||||
|
||||
// Byte 1: Set up our base offsets.
|
||||
self.pics[0].data.write(self.pics[0].offset);
|
||||
wait();
|
||||
self.pics[1].data.write(self.pics[1].offset);
|
||||
wait();
|
||||
|
||||
// Byte 2: Configure chaining between PIC1 and PIC2.
|
||||
self.pics[0].data.write(4);
|
||||
wait();
|
||||
self.pics[1].data.write(2);
|
||||
wait();
|
||||
|
||||
// Byte 3: Set our mode.
|
||||
self.pics[0].data.write(MODE_8086);
|
||||
wait();
|
||||
self.pics[1].data.write(MODE_8086);
|
||||
wait();
|
||||
|
||||
// Restore our saved masks.
|
||||
self.write_masks(saved_masks[0], saved_masks[1])
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the interrupt masks of both PICs.
|
||||
pub unsafe fn read_masks(&mut self) -> [u8; 2] {
|
||||
unsafe { [self.pics[0].read_mask(), self.pics[1].read_mask()] }
|
||||
}
|
||||
|
||||
/// Writes the interrupt masks of both PICs.
|
||||
pub unsafe fn write_masks(&mut self, mask1: u8, mask2: u8) {
|
||||
unsafe {
|
||||
self.pics[0].write_mask(mask1);
|
||||
self.pics[1].write_mask(mask2);
|
||||
}
|
||||
}
|
||||
|
||||
/// Disables both PICs by masking all interrupts.
|
||||
pub unsafe fn disable(&mut self) {
|
||||
unsafe { self.write_masks(u8::MAX, u8::MAX) }
|
||||
}
|
||||
|
||||
/// Do we handle this interrupt?
|
||||
pub fn handles_interrupt(&self, interrupt_id: u8) -> bool {
|
||||
self.pics.iter().any(|p| p.handles_interrupt(interrupt_id))
|
||||
}
|
||||
|
||||
/// Figure out which (if any) PICs in our chain need to know about this
|
||||
/// interrupt. This is tricky, because all interrupts from `pics[1]`
|
||||
/// get chained through `pics[0]`.
|
||||
pub unsafe fn notify_end_of_interrupt(&mut self, interrupt_id: u8) {
|
||||
if self.handles_interrupt(interrupt_id) {
|
||||
if self.pics[1].handles_interrupt(interrupt_id) {
|
||||
unsafe {
|
||||
self.pics[1].end_of_interrupt();
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
self.pics[0].end_of_interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user