Support x2APIC

This commit is contained in:
Yuke Peng
2023-06-27 04:56:16 -07:00
committed by Tate, Hongliang Tian
parent 0e8b2aff7b
commit 893b174dc9
12 changed files with 308 additions and 128 deletions

View File

@ -11,8 +11,9 @@ const COMMON_ARGS: &[&str] = &[
"--no-reboot", "--no-reboot",
"-machine", "-machine",
"q35", "q35",
"-enable-kvm",
"-cpu", "-cpu",
"Icelake-Server", "Icelake-Server,+x2apic",
"-m", "-m",
"2G", "2G",
"-device", "-device",

View File

@ -3,7 +3,7 @@ use log::info;
use spin::{Mutex, Once}; use spin::{Mutex, Once};
use x86::apic::ioapic::IoApic; use x86::apic::ioapic::IoApic;
use super::acpi::ACPI_TABLES; use crate::arch::x86::kernel::acpi::ACPI_TABLES;
pub struct IoApicWrapper { pub struct IoApicWrapper {
io_apic: IoApic, io_apic: IoApic,

View File

@ -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<Arc<Mutex<dyn Apic + 'static>>> = 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)
}
}

View File

@ -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<Self> {
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);
}
}
}

View File

@ -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<Mutex<XApic>> = Once::new();
#[derive(Debug)]
pub struct XApic {
mmio_region: &'static mut [u32],
}
impl XApic {
pub fn new() -> Option<Self> {
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
}
}

View File

@ -4,6 +4,5 @@
//! //!
pub mod acpi; pub mod acpi;
pub mod ioapic; pub mod apic;
pub mod pic; pub mod pic;
pub mod xapic;

View File

@ -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<Mutex<Xapic>> = 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
}
}

View File

@ -9,6 +9,7 @@ pub(crate) mod timer;
use alloc::fmt; use alloc::fmt;
use core::fmt::Write; use core::fmt::Write;
use kernel::apic::ioapic;
use log::info; use log::info;
pub(crate) fn before_all_init() { pub(crate) fn before_all_init() {
@ -21,12 +22,14 @@ pub(crate) fn after_all_init() {
irq::init(); irq::init();
device::serial::callback_init(); device::serial::callback_init();
kernel::acpi::init(); kernel::acpi::init();
if kernel::xapic::has_apic() { match kernel::apic::init() {
kernel::ioapic::init(); Ok(_) => {
kernel::xapic::init(); ioapic::init();
} else { }
info!("No apic exists, using pic instead"); Err(err) => {
kernel::pic::enable(); info!("APIC init error:{:?}", err);
kernel::pic::enable();
}
} }
timer::init(); timer::init();
// Some driver like serial may use PIC // Some driver like serial may use PIC
@ -35,7 +38,9 @@ pub(crate) fn after_all_init() {
pub(crate) fn interrupts_ack() { pub(crate) fn interrupts_ack() {
kernel::pic::ack(); kernel::pic::ack();
kernel::xapic::ack(); if let Some(apic) = kernel::apic::APIC_INSTANCE.get() {
apic.lock().eoi();
}
} }
struct Stdout; struct Stdout;

View File

@ -1,19 +1,19 @@
use log::info; use log::info;
use trapframe::TrapFrame; 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 crate::config;
use x86::apic::xapic;
pub fn init() { 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 handle = unsafe { crate::trap::IrqLine::acquire(super::TIMER_IRQ_NUM) };
let a = handle.on_active(init_function); let a = handle.on_active(init_function);
// divide by 64 // divide by 64
apic_lock.write(xapic::XAPIC_TIMER_DIV_CONF, 0b1001); apic_lock.set_timer_div_config(DivideConfig::Divide64);
apic_lock.write(xapic::XAPIC_TIMER_INIT_COUNT, 0xFFFF_FFFF); apic_lock.set_timer_init_count(0xFFFF_FFFF);
// apic_lock.lvt_timer_register.write(timer::TIMER_IRQ_NUM as u32);
drop(apic_lock); drop(apic_lock);
// init pic for now, disable it after the APIC Timer init is done // 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) { fn init_function(trap_frame: &TrapFrame) {
static mut IN_TIME: u8 = 0; static mut IN_TIME: u8 = 0;
static mut FIRST_TIME_COUNT: u32 = 0; static mut FIRST_TIME_COUNT: u64 = 0;
unsafe { unsafe {
if IS_FINISH || IN_TIME == 0 { if IS_FINISH || IN_TIME == 0 {
// drop the first entry, since it may not be the time we want // drop the first entry, since it may not be the time we want
IN_TIME += 1; IN_TIME += 1;
let apic_lock = XAPIC_INSTANCE.get().unwrap().lock(); let apic_lock = APIC_INSTANCE.get().unwrap().lock();
let remain_ticks = apic_lock.read(xapic::XAPIC_TIMER_CURRENT_COUNT); let remain_ticks = apic_lock.timer_current_count();
FIRST_TIME_COUNT = 0xFFFF_FFFF - remain_ticks; FIRST_TIME_COUNT = 0xFFFF_FFFF - remain_ticks;
pic::ack(); pic::ack();
return; return;
@ -48,17 +48,14 @@ pub fn init() {
} }
pic::disable_temp(); pic::disable_temp();
// stop APIC Timer, get the number of tick we need // stop APIC Timer, get the number of tick we need
let mut apic_lock = XAPIC_INSTANCE.get().unwrap().lock(); let mut apic_lock = APIC_INSTANCE.get().unwrap().lock();
let remain_ticks = apic_lock.read(xapic::XAPIC_TIMER_CURRENT_COUNT); let remain_ticks = apic_lock.timer_current_count();
apic_lock.write(xapic::XAPIC_TIMER_INIT_COUNT, 0); apic_lock.set_timer_init_count(0);
let ticks = unsafe { 0xFFFF_FFFF - remain_ticks - FIRST_TIME_COUNT }; let ticks = unsafe { 0xFFFF_FFFF - remain_ticks - FIRST_TIME_COUNT };
// periodic mode, divide 64, freq: TIMER_FREQ Hz // periodic mode, divide 64, freq: TIMER_FREQ Hz
apic_lock.write(xapic::XAPIC_TIMER_INIT_COUNT, ticks as u32); apic_lock.set_timer_init_count(ticks);
apic_lock.write( apic_lock.set_lvt_timer(super::TIMER_IRQ_NUM as u64 | (1 << 17));
xapic::XAPIC_LVT_TIMER, apic_lock.set_timer_div_config(DivideConfig::Divide64);
super::TIMER_IRQ_NUM as u32 | (1 << 17),
);
apic_lock.write(xapic::XAPIC_TIMER_DIV_CONF, 0b1001);
info!( info!(
"APIC Timer ticks count:{:x}, remain ticks: {:x},Timer Freq:{} Hz", "APIC Timer ticks count:{:x}, remain ticks: {:x},Timer Freq:{} Hz",

View File

@ -7,7 +7,7 @@ use volatile::{
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<Hpet> = Once::new(); static HPET_INSTANCE: Once<Hpet> = Once::new();
const OFFSET_ID_REGISTER: usize = 0x000; const OFFSET_ID_REGISTER: usize = 0x000;

View File

@ -10,6 +10,7 @@ use spin::{Mutex, Once};
use trapframe::TrapFrame; use trapframe::TrapFrame;
use crate::arch::x86::kernel; use crate::arch::x86::kernel;
use crate::config::TIMER_FREQ;
use crate::trap::IrqAllocateHandle; use crate::trap::IrqAllocateHandle;
pub const TIMER_IRQ_NUM: u8 = 32; pub const TIMER_IRQ_NUM: u8 = 32;
@ -19,7 +20,7 @@ static TIMER_IRQ: Once<IrqAllocateHandle> = Once::new();
pub fn init() { pub fn init() {
TIMEOUT_LIST.call_once(|| Mutex::new(BinaryHeap::new())); TIMEOUT_LIST.call_once(|| Mutex::new(BinaryHeap::new()));
if kernel::xapic::has_apic() { if kernel::apic::APIC_INSTANCE.is_completed() {
apic::init(); apic::init();
} else { } else {
pit::init(); pit::init();
@ -131,5 +132,5 @@ where
/// The time since the system boots up. /// The time since the system boots up.
/// The currently returned results are in milliseconds. /// The currently returned results are in milliseconds.
pub fn read_monotonic_milli_seconds() -> u64 { pub fn read_monotonic_milli_seconds() -> u64 {
TICK.load(Ordering::SeqCst) TICK.load(Ordering::SeqCst) * (1000 / TIMER_FREQ)
} }

View File

@ -18,4 +18,4 @@ pub const KVA_START: usize = (usize::MAX) << PAGE_SIZE_BITS;
pub const DEFAULT_LOG_LEVEL: Level = Level::Error; pub const DEFAULT_LOG_LEVEL: Level = Level::Error;
/// This value represent the base timer frequency in Hz /// This value represent the base timer frequency in Hz
pub const TIMER_FREQ: u64 = 1000; pub const TIMER_FREQ: u64 = 500;