From 893b174dc9e5c33e83d7711a85864f27e30281cd Mon Sep 17 00:00:00 2001 From: Yuke Peng Date: Tue, 27 Jun 2023 04:56:16 -0700 Subject: [PATCH] Support x2APIC --- boot/src/main.rs | 3 +- .../src/arch/x86/kernel/{ => apic}/ioapic.rs | 2 +- .../src/arch/x86/kernel/apic/mod.rs | 86 ++++++++++++++ .../src/arch/x86/kernel/apic/x2apic.rs | 78 +++++++++++++ .../src/arch/x86/kernel/apic/xapic.rs | 107 ++++++++++++++++++ .../jinux-frame/src/arch/x86/kernel/mod.rs | 3 +- .../jinux-frame/src/arch/x86/kernel/xapic.rs | 94 --------------- framework/jinux-frame/src/arch/x86/mod.rs | 19 ++-- .../jinux-frame/src/arch/x86/timer/apic.rs | 35 +++--- .../jinux-frame/src/arch/x86/timer/hpet.rs | 2 +- .../jinux-frame/src/arch/x86/timer/mod.rs | 5 +- framework/jinux-frame/src/config.rs | 2 +- 12 files changed, 308 insertions(+), 128 deletions(-) rename framework/jinux-frame/src/arch/x86/kernel/{ => apic}/ioapic.rs (97%) create mode 100644 framework/jinux-frame/src/arch/x86/kernel/apic/mod.rs create mode 100644 framework/jinux-frame/src/arch/x86/kernel/apic/x2apic.rs create mode 100644 framework/jinux-frame/src/arch/x86/kernel/apic/xapic.rs delete mode 100644 framework/jinux-frame/src/arch/x86/kernel/xapic.rs diff --git a/boot/src/main.rs b/boot/src/main.rs index dc73e5606..cb47f03b1 100644 --- a/boot/src/main.rs +++ b/boot/src/main.rs @@ -11,8 +11,9 @@ const COMMON_ARGS: &[&str] = &[ "--no-reboot", "-machine", "q35", + "-enable-kvm", "-cpu", - "Icelake-Server", + "Icelake-Server,+x2apic", "-m", "2G", "-device", diff --git a/framework/jinux-frame/src/arch/x86/kernel/ioapic.rs b/framework/jinux-frame/src/arch/x86/kernel/apic/ioapic.rs similarity index 97% rename from framework/jinux-frame/src/arch/x86/kernel/ioapic.rs rename to framework/jinux-frame/src/arch/x86/kernel/apic/ioapic.rs index 52ce67a4e..c7222a762 100644 --- a/framework/jinux-frame/src/arch/x86/kernel/ioapic.rs +++ b/framework/jinux-frame/src/arch/x86/kernel/apic/ioapic.rs @@ -3,7 +3,7 @@ use log::info; use spin::{Mutex, Once}; use x86::apic::ioapic::IoApic; -use super::acpi::ACPI_TABLES; +use crate::arch::x86::kernel::acpi::ACPI_TABLES; pub struct IoApicWrapper { io_apic: IoApic, diff --git a/framework/jinux-frame/src/arch/x86/kernel/apic/mod.rs b/framework/jinux-frame/src/arch/x86/kernel/apic/mod.rs new file mode 100644 index 000000000..8455db43f --- /dev/null +++ b/framework/jinux-frame/src/arch/x86/kernel/apic/mod.rs @@ -0,0 +1,86 @@ +use alloc::sync::Arc; +use log::info; +use spin::{Mutex, Once}; + +pub mod ioapic; +pub mod x2apic; +pub mod xapic; + +pub static APIC_INSTANCE: Once>> = Once::new(); + +pub trait Apic: ApicTimer + Sync + Send { + fn id(&self) -> u32; + + fn version(&self) -> u32; + + /// End of Interrupt, this function will inform APIC that this interrupt has been processed. + fn eoi(&mut self); +} + +pub trait ApicTimer: Sync + Send { + /// Set the initial timer count, the APIC timer will count down from this value. + fn set_timer_init_count(&mut self, value: u64); + + /// Get the current count of the timer. + /// The interval can be expressed by the expression: `init_count` - `current_count`. + fn timer_current_count(&self) -> u64; + + /// Set the timer register in the APIC. + /// Bit 0-7: The interrupt vector of timer interrupt. + /// Bit 12: Delivery Status, 0 for Idle, 1 for Send Pending. + /// Bit 16: Mask bit. + /// Bit 17-18: Timer Mode, 0 for One-shot, 1 for Periodic, 2 for TSC-Deadline. + fn set_lvt_timer(&mut self, value: u64); + + /// Set timer divide config register. + fn set_timer_div_config(&mut self, div_config: DivideConfig); +} + +#[derive(Debug)] +pub enum ApicInitError { + /// No x2APIC or xAPIC found. + NoApic, +} + +#[derive(Debug)] +#[repr(u32)] +pub enum DivideConfig { + Divide1 = 0b1011, + Divide2 = 0b0000, + Divide4 = 0b0001, + Divide8 = 0b0010, + Divide16 = 0b0011, + Divide32 = 0b1000, + Divide64 = 0b1001, + Divide128 = 0b1010, +} + +pub fn init() -> Result<(), ApicInitError> { + crate::arch::x86::kernel::pic::disable_temp(); + if let Some(mut x2apic) = x2apic::X2Apic::new() { + x2apic.enable(); + let version = x2apic.version(); + info!( + "x2APIC ID:{:x}, Version:{:x}, Max LVT:{:x}", + x2apic.id(), + version & 0xff, + (version >> 16) & 0xff + ); + APIC_INSTANCE.call_once(|| Arc::new(Mutex::new(x2apic))); + Ok(()) + } else if let Some(mut xapic) = xapic::XApic::new() { + xapic.enable(); + let version = xapic.version(); + info!( + "xAPIC ID:{:x}, Version:{:x}, Max LVT:{:x}", + xapic.id(), + version & 0xff, + (version >> 16) & 0xff + ); + APIC_INSTANCE.call_once(|| Arc::new(Mutex::new(xapic))); + Ok(()) + } else { + log::warn!("Not found x2APIC or xAPIC"); + Err(ApicInitError::NoApic) + } +} diff --git a/framework/jinux-frame/src/arch/x86/kernel/apic/x2apic.rs b/framework/jinux-frame/src/arch/x86/kernel/apic/x2apic.rs new file mode 100644 index 000000000..bfd35ad45 --- /dev/null +++ b/framework/jinux-frame/src/arch/x86/kernel/apic/x2apic.rs @@ -0,0 +1,78 @@ +use x86::msr::{ + rdmsr, wrmsr, IA32_APIC_BASE, IA32_X2APIC_APICID, IA32_X2APIC_CUR_COUNT, IA32_X2APIC_DIV_CONF, + IA32_X2APIC_EOI, IA32_X2APIC_INIT_COUNT, IA32_X2APIC_LVT_TIMER, IA32_X2APIC_SIVR, + IA32_X2APIC_VERSION, +}; + +use super::ApicTimer; + +pub struct X2Apic {} + +impl X2Apic { + pub(crate) fn new() -> Option { + if !Self::has_x2apic() { + return None; + } + Some(Self {}) + } + + fn has_x2apic() -> bool { + // x2apic::X2APIC::new() + let value = unsafe { core::arch::x86_64::__cpuid(1) }; + value.ecx & 0x20_0000 != 0 + } + + pub fn enable(&mut self) { + // Enable + unsafe { + // Enable x2APIC mode globally + let mut base = rdmsr(IA32_APIC_BASE); + base = base | 0b1100_0000_0000; // Enable x2APIC and xAPIC + wrmsr(IA32_APIC_BASE, base); + + // Set SVR, Enable APIC and set Spurious Vector to 15 (Reserved irq number) + let svr: u64 = 1 << 8 | 15; + wrmsr(IA32_X2APIC_SIVR, svr); + } + } +} + +impl super::Apic for X2Apic { + fn id(&self) -> u32 { + unsafe { rdmsr(IA32_X2APIC_APICID) as u32 } + } + + fn version(&self) -> u32 { + unsafe { rdmsr(IA32_X2APIC_VERSION) as u32 } + } + + fn eoi(&mut self) { + unsafe { + wrmsr(IA32_X2APIC_EOI, 0); + } + } +} + +impl ApicTimer for X2Apic { + fn set_timer_init_count(&mut self, value: u64) { + unsafe { + wrmsr(IA32_X2APIC_INIT_COUNT, value); + } + } + + fn timer_current_count(&self) -> u64 { + unsafe { rdmsr(IA32_X2APIC_CUR_COUNT) } + } + + fn set_lvt_timer(&mut self, value: u64) { + unsafe { + wrmsr(IA32_X2APIC_LVT_TIMER, value); + } + } + + fn set_timer_div_config(&mut self, div_config: super::DivideConfig) { + unsafe { + wrmsr(IA32_X2APIC_DIV_CONF, div_config as u64); + } + } +} diff --git a/framework/jinux-frame/src/arch/x86/kernel/apic/xapic.rs b/framework/jinux-frame/src/arch/x86/kernel/apic/xapic.rs new file mode 100644 index 000000000..58aee5370 --- /dev/null +++ b/framework/jinux-frame/src/arch/x86/kernel/apic/xapic.rs @@ -0,0 +1,107 @@ +use crate::vm; +use spin::{Mutex, Once}; +use x86::apic::xapic; + +use super::ApicTimer; + +const IA32_APIC_BASE_MSR: u32 = 0x1B; +const IA32_APIC_BASE_MSR_BSP: u32 = 0x100; // Processor is a BSP +const IA32_APIC_BASE_MSR_ENABLE: u64 = 0x800; + +const APIC_LVT_MASK_BITS: u32 = 1 << 16; + +pub static XAPIC_INSTANCE: Once> = Once::new(); + +#[derive(Debug)] +pub struct XApic { + mmio_region: &'static mut [u32], +} + +impl XApic { + pub fn new() -> Option { + if !Self::has_xapic() { + return None; + } + let address = vm::paddr_to_vaddr(get_apic_base_address()); + let region: &'static mut [u32] = unsafe { &mut *(address as *mut [u32; 256]) }; + Some(Self { + mmio_region: region, + }) + } + + /// Read a register from the MMIO region. + fn read(&self, offset: u32) -> u32 { + assert!(offset as usize % 4 == 0); + let index = offset as usize / 4; + unsafe { core::ptr::read_volatile(&self.mmio_region[index]) } + } + + /// write a register in the MMIO region. + fn write(&mut self, offset: u32, val: u32) { + assert!(offset as usize % 4 == 0); + let index = offset as usize / 4; + unsafe { core::ptr::write_volatile(&mut self.mmio_region[index], val) } + } + + pub fn enable(&mut self) { + // Enable xAPIC + set_apic_base_address(get_apic_base_address()); + + // Set SVR, Enable APIC and set Spurious Vector to 15 (Reserved irq number) + let svr: u32 = 1 << 8 | 15; + self.write(xapic::XAPIC_SVR, svr); + } + + pub fn has_xapic() -> bool { + let value = unsafe { core::arch::x86_64::__cpuid(1) }; + value.edx & 0x100 != 0 + } +} + +impl super::Apic for XApic { + fn id(&self) -> u32 { + self.read(xapic::XAPIC_ID) + } + + fn version(&self) -> u32 { + self.read(xapic::XAPIC_VERSION) + } + + fn eoi(&mut self) { + self.write(xapic::XAPIC_EOI, 0); + } +} + +impl ApicTimer for XApic { + fn set_timer_init_count(&mut self, value: u64) { + self.write(xapic::XAPIC_TIMER_INIT_COUNT, value as u32); + } + + fn timer_current_count(&self) -> u64 { + self.read(xapic::XAPIC_TIMER_CURRENT_COUNT) as u64 + } + + fn set_lvt_timer(&mut self, value: u64) { + self.write(xapic::XAPIC_LVT_TIMER, value as u32); + } + + fn set_timer_div_config(&mut self, div_config: super::DivideConfig) { + self.write(xapic::XAPIC_TIMER_DIV_CONF, div_config as u32); + } +} + +/// set APIC base address and enable it +fn set_apic_base_address(address: usize) { + unsafe { + x86_64::registers::model_specific::Msr::new(IA32_APIC_BASE_MSR) + .write(address as u64 | IA32_APIC_BASE_MSR_ENABLE); + } +} + +/// get APIC base address +fn get_apic_base_address() -> usize { + unsafe { + (x86_64::registers::model_specific::Msr::new(IA32_APIC_BASE_MSR).read() & 0xf_ffff_f000) + as usize + } +} diff --git a/framework/jinux-frame/src/arch/x86/kernel/mod.rs b/framework/jinux-frame/src/arch/x86/kernel/mod.rs index 23f3323ae..3b2b3328f 100644 --- a/framework/jinux-frame/src/arch/x86/kernel/mod.rs +++ b/framework/jinux-frame/src/arch/x86/kernel/mod.rs @@ -4,6 +4,5 @@ //! pub mod acpi; -pub mod ioapic; +pub mod apic; pub mod pic; -pub mod xapic; diff --git a/framework/jinux-frame/src/arch/x86/kernel/xapic.rs b/framework/jinux-frame/src/arch/x86/kernel/xapic.rs deleted file mode 100644 index cbd09859c..000000000 --- a/framework/jinux-frame/src/arch/x86/kernel/xapic.rs +++ /dev/null @@ -1,94 +0,0 @@ -use crate::vm; -use log::debug; -use spin::{Mutex, Once}; -use x86::apic::xapic; - -const IA32_APIC_BASE_MSR: u32 = 0x1B; -const IA32_APIC_BASE_MSR_BSP: u32 = 0x100; // Processor is a BSP -const IA32_APIC_BASE_MSR_ENABLE: u64 = 0x800; - -const APIC_LVT_MASK_BITS: u32 = 1 << 16; - -pub static XAPIC_INSTANCE: Once> = Once::new(); - -#[derive(Debug)] -pub struct Xapic { - mmio_region: &'static mut [u32], -} - -impl Xapic { - pub fn new(address: usize) -> Self { - let region: &'static mut [u32] = unsafe { &mut *(address as *mut [u32; 256]) }; - Self { - mmio_region: region, - } - } - - /// Read a register from the MMIO region. - pub fn read(&self, offset: u32) -> u32 { - assert!(offset as usize % 4 == 0); - let index = offset as usize / 4; - unsafe { core::ptr::read_volatile(&self.mmio_region[index]) } - } - - /// write a register in the MMIO region. - pub fn write(&mut self, offset: u32, val: u32) { - assert!(offset as usize % 4 == 0); - let index = offset as usize / 4; - unsafe { core::ptr::write_volatile(&mut self.mmio_region[index], val) } - } -} - -pub fn has_apic() -> bool { - let value = unsafe { core::arch::x86_64::__cpuid(1) }; - value.edx & 0x100 != 0 -} - -pub fn init() { - super::pic::disable_temp(); - - let mut apic = Xapic::new(vm::paddr_to_vaddr(get_apic_base_address())); - // enable apic - set_apic_base_address(get_apic_base_address()); - - let spurious = apic.read(xapic::XAPIC_SVR); - apic.write(xapic::XAPIC_SVR, spurious | (0x100)); - let apic_id = apic.read(xapic::XAPIC_ID) >> 24; - let apic_ver = apic.read(xapic::XAPIC_VERSION); - - debug!( - "APIC ID:{:x}, Version:{:x}, Max LVT:{:x}", - apic_id, - apic_ver & 0xff, - (apic_ver >> 16) & 0xff - ); - - debug!("spurious:{:x}", spurious); - - XAPIC_INSTANCE.call_once(|| Mutex::new(apic)); -} - -#[inline(always)] -pub fn ack() { - XAPIC_INSTANCE - .get() - .unwrap() - .lock() - .write(xapic::XAPIC_EOI, 0); -} - -/// set APIC base address and enable it -fn set_apic_base_address(address: usize) { - unsafe { - x86_64::registers::model_specific::Msr::new(IA32_APIC_BASE_MSR) - .write(address as u64 | IA32_APIC_BASE_MSR_ENABLE); - } -} - -/// get APIC base address -fn get_apic_base_address() -> usize { - unsafe { - (x86_64::registers::model_specific::Msr::new(IA32_APIC_BASE_MSR).read() & 0xf_ffff_f000) - as usize - } -} diff --git a/framework/jinux-frame/src/arch/x86/mod.rs b/framework/jinux-frame/src/arch/x86/mod.rs index f3b2724ac..ec224e057 100644 --- a/framework/jinux-frame/src/arch/x86/mod.rs +++ b/framework/jinux-frame/src/arch/x86/mod.rs @@ -9,6 +9,7 @@ pub(crate) mod timer; use alloc::fmt; use core::fmt::Write; +use kernel::apic::ioapic; use log::info; pub(crate) fn before_all_init() { @@ -21,12 +22,14 @@ pub(crate) fn after_all_init() { irq::init(); device::serial::callback_init(); kernel::acpi::init(); - if kernel::xapic::has_apic() { - kernel::ioapic::init(); - kernel::xapic::init(); - } else { - info!("No apic exists, using pic instead"); - kernel::pic::enable(); + match kernel::apic::init() { + Ok(_) => { + ioapic::init(); + } + Err(err) => { + info!("APIC init error:{:?}", err); + kernel::pic::enable(); + } } timer::init(); // Some driver like serial may use PIC @@ -35,7 +38,9 @@ pub(crate) fn after_all_init() { pub(crate) fn interrupts_ack() { kernel::pic::ack(); - kernel::xapic::ack(); + if let Some(apic) = kernel::apic::APIC_INSTANCE.get() { + apic.lock().eoi(); + } } struct Stdout; diff --git a/framework/jinux-frame/src/arch/x86/timer/apic.rs b/framework/jinux-frame/src/arch/x86/timer/apic.rs index f6302c671..e5d7e04ac 100644 --- a/framework/jinux-frame/src/arch/x86/timer/apic.rs +++ b/framework/jinux-frame/src/arch/x86/timer/apic.rs @@ -1,19 +1,19 @@ use log::info; use trapframe::TrapFrame; -use crate::arch::x86::kernel::{pic, xapic::XAPIC_INSTANCE}; +use crate::arch::x86::kernel::{ + apic::{DivideConfig, APIC_INSTANCE}, + pic, +}; use crate::config; -use x86::apic::xapic; - pub fn init() { - let mut apic_lock = XAPIC_INSTANCE.get().unwrap().lock(); + let mut apic_lock = APIC_INSTANCE.get().unwrap().lock(); let handle = unsafe { crate::trap::IrqLine::acquire(super::TIMER_IRQ_NUM) }; let a = handle.on_active(init_function); // divide by 64 - apic_lock.write(xapic::XAPIC_TIMER_DIV_CONF, 0b1001); - apic_lock.write(xapic::XAPIC_TIMER_INIT_COUNT, 0xFFFF_FFFF); - // apic_lock.lvt_timer_register.write(timer::TIMER_IRQ_NUM as u32); + apic_lock.set_timer_div_config(DivideConfig::Divide64); + apic_lock.set_timer_init_count(0xFFFF_FFFF); drop(apic_lock); // init pic for now, disable it after the APIC Timer init is done @@ -34,13 +34,13 @@ pub fn init() { fn init_function(trap_frame: &TrapFrame) { static mut IN_TIME: u8 = 0; - static mut FIRST_TIME_COUNT: u32 = 0; + static mut FIRST_TIME_COUNT: u64 = 0; unsafe { if IS_FINISH || IN_TIME == 0 { // drop the first entry, since it may not be the time we want IN_TIME += 1; - let apic_lock = XAPIC_INSTANCE.get().unwrap().lock(); - let remain_ticks = apic_lock.read(xapic::XAPIC_TIMER_CURRENT_COUNT); + let apic_lock = APIC_INSTANCE.get().unwrap().lock(); + let remain_ticks = apic_lock.timer_current_count(); FIRST_TIME_COUNT = 0xFFFF_FFFF - remain_ticks; pic::ack(); return; @@ -48,17 +48,14 @@ pub fn init() { } pic::disable_temp(); // stop APIC Timer, get the number of tick we need - let mut apic_lock = XAPIC_INSTANCE.get().unwrap().lock(); - let remain_ticks = apic_lock.read(xapic::XAPIC_TIMER_CURRENT_COUNT); - apic_lock.write(xapic::XAPIC_TIMER_INIT_COUNT, 0); + let mut apic_lock = APIC_INSTANCE.get().unwrap().lock(); + let remain_ticks = apic_lock.timer_current_count(); + apic_lock.set_timer_init_count(0); let ticks = unsafe { 0xFFFF_FFFF - remain_ticks - FIRST_TIME_COUNT }; // periodic mode, divide 64, freq: TIMER_FREQ Hz - apic_lock.write(xapic::XAPIC_TIMER_INIT_COUNT, ticks as u32); - apic_lock.write( - xapic::XAPIC_LVT_TIMER, - super::TIMER_IRQ_NUM as u32 | (1 << 17), - ); - apic_lock.write(xapic::XAPIC_TIMER_DIV_CONF, 0b1001); + apic_lock.set_timer_init_count(ticks); + apic_lock.set_lvt_timer(super::TIMER_IRQ_NUM as u64 | (1 << 17)); + apic_lock.set_timer_div_config(DivideConfig::Divide64); info!( "APIC Timer ticks count:{:x}, remain ticks: {:x},Timer Freq:{} Hz", diff --git a/framework/jinux-frame/src/arch/x86/timer/hpet.rs b/framework/jinux-frame/src/arch/x86/timer/hpet.rs index 60b184b07..5369b9958 100644 --- a/framework/jinux-frame/src/arch/x86/timer/hpet.rs +++ b/framework/jinux-frame/src/arch/x86/timer/hpet.rs @@ -7,7 +7,7 @@ use volatile::{ Volatile, }; -use crate::arch::x86::kernel::{acpi::ACPI_TABLES, ioapic}; +use crate::arch::x86::kernel::{acpi::ACPI_TABLES, apic::ioapic}; static HPET_INSTANCE: Once = Once::new(); const OFFSET_ID_REGISTER: usize = 0x000; diff --git a/framework/jinux-frame/src/arch/x86/timer/mod.rs b/framework/jinux-frame/src/arch/x86/timer/mod.rs index a7c220224..a83f82054 100644 --- a/framework/jinux-frame/src/arch/x86/timer/mod.rs +++ b/framework/jinux-frame/src/arch/x86/timer/mod.rs @@ -10,6 +10,7 @@ use spin::{Mutex, Once}; use trapframe::TrapFrame; use crate::arch::x86::kernel; +use crate::config::TIMER_FREQ; use crate::trap::IrqAllocateHandle; pub const TIMER_IRQ_NUM: u8 = 32; @@ -19,7 +20,7 @@ static TIMER_IRQ: Once = Once::new(); pub fn init() { TIMEOUT_LIST.call_once(|| Mutex::new(BinaryHeap::new())); - if kernel::xapic::has_apic() { + if kernel::apic::APIC_INSTANCE.is_completed() { apic::init(); } else { pit::init(); @@ -131,5 +132,5 @@ where /// The time since the system boots up. /// The currently returned results are in milliseconds. pub fn read_monotonic_milli_seconds() -> u64 { - TICK.load(Ordering::SeqCst) + TICK.load(Ordering::SeqCst) * (1000 / TIMER_FREQ) } diff --git a/framework/jinux-frame/src/config.rs b/framework/jinux-frame/src/config.rs index 6975a8920..9c6985dcc 100644 --- a/framework/jinux-frame/src/config.rs +++ b/framework/jinux-frame/src/config.rs @@ -18,4 +18,4 @@ pub const KVA_START: usize = (usize::MAX) << PAGE_SIZE_BITS; pub const DEFAULT_LOG_LEVEL: Level = Level::Error; /// This value represent the base timer frequency in Hz -pub const TIMER_FREQ: u64 = 1000; +pub const TIMER_FREQ: u64 = 500;