diff --git a/ostd/src/arch/x86/boot/smp.rs b/ostd/src/arch/x86/boot/smp.rs index 5a2596e01..55df78520 100644 --- a/ostd/src/arch/x86/boot/smp.rs +++ b/ostd/src/arch/x86/boot/smp.rs @@ -150,7 +150,7 @@ fn send_startup_to_all_aps() { (AP_BOOT_START_PA / PAGE_SIZE) as u8, ); // SAFETY: we are sending startup IPI to all APs. - apic::borrow(|apic| unsafe { apic.send_ipi(icr) }); + apic::with_borrow(|apic| unsafe { apic.send_ipi(icr) }); } fn send_init_to_all_aps() { @@ -165,7 +165,7 @@ fn send_init_to_all_aps() { 0, ); // SAFETY: we are sending init IPI to all APs. - apic::borrow(|apic| unsafe { apic.send_ipi(icr) }); + apic::with_borrow(|apic| unsafe { apic.send_ipi(icr) }); } fn send_init_deassert() { @@ -180,7 +180,7 @@ fn send_init_deassert() { 0, ); // SAFETY: we are sending deassert IPI to all APs. - apic::borrow(|apic| unsafe { apic.send_ipi(icr) }); + apic::with_borrow(|apic| unsafe { apic.send_ipi(icr) }); } /// Spin wait approximately `c` cycles. diff --git a/ostd/src/arch/x86/irq.rs b/ostd/src/arch/x86/irq.rs index 4c7aede15..e3dbd329a 100644 --- a/ostd/src/arch/x86/irq.rs +++ b/ostd/src/arch/x86/irq.rs @@ -173,7 +173,7 @@ pub(crate) unsafe fn send_ipi(cpu_id: u32, irq_num: u8) { apic::DeliveryMode::Fixed, irq_num, ); - apic::borrow(|apic| { + apic::with_borrow(|apic| { apic.send_ipi(icr); }); } diff --git a/ostd/src/arch/x86/kernel/apic/mod.rs b/ostd/src/arch/x86/kernel/apic/mod.rs index bf6a128d6..e06370d58 100644 --- a/ostd/src/arch/x86/kernel/apic/mod.rs +++ b/ostd/src/arch/x86/kernel/apic/mod.rs @@ -13,7 +13,7 @@ pub mod x2apic; pub mod xapic; cpu_local! { - static APIC_INSTANCE: Once>> = Once::new(); + static APIC_INSTANCE: RefCell>> = RefCell::new(None); } static APIC_TYPE: Once = Once::new(); @@ -24,23 +24,29 @@ static APIC_TYPE: Once = Once::new(); /// local APIC instance. During the execution of the closure, the interrupts /// are guaranteed to be disabled. /// +/// This function also lazily initializes the Local APIC instance. It does +/// enable the Local APIC if it is not enabled. +/// /// Example: /// ```rust /// use ostd::arch::x86::kernel::apic; /// -/// let ticks = apic::borrow(|apic| { +/// let ticks = apic::with_borrow(|apic| { /// let ticks = apic.timer_current_count(); /// apic.set_timer_init_count(0); /// ticks /// }); /// ``` -pub fn borrow(f: impl FnOnce(&mut (dyn Apic + 'static)) -> R) -> R { +pub fn with_borrow(f: impl FnOnce(&mut (dyn Apic + 'static)) -> R) -> R { let irq_guard = crate::trap::disable_local(); let apic_guard = APIC_INSTANCE.get_with(&irq_guard); + let mut apic_init_ref = apic_guard.borrow_mut(); // If it is not initialized, lazily initialize it. - if !apic_guard.is_completed() { - apic_guard.call_once(|| match APIC_TYPE.get().unwrap() { + let apic_ref = if let Some(apic_ref) = apic_init_ref.as_mut() { + apic_ref + } else { + *apic_init_ref = Some(match APIC_TYPE.get().unwrap() { ApicType::XApic => { let mut xapic = xapic::XApic::new().unwrap(); xapic.enable(); @@ -51,7 +57,7 @@ pub fn borrow(f: impl FnOnce(&mut (dyn Apic + 'static)) -> R) -> R { version & 0xff, (version >> 16) & 0xff ); - RefCell::new(Box::new(xapic)) + Box::new(xapic) } ApicType::X2Apic => { let mut x2apic = x2apic::X2Apic::new().unwrap(); @@ -63,13 +69,12 @@ pub fn borrow(f: impl FnOnce(&mut (dyn Apic + 'static)) -> R) -> R { version & 0xff, (version >> 16) & 0xff ); - RefCell::new(Box::new(x2apic)) + Box::new(x2apic) } }); - } - let apic_cell = apic_guard.get().unwrap(); - let mut apic_ref = apic_cell.borrow_mut(); + apic_init_ref.as_mut().unwrap() + }; let ret = f.call_once((apic_ref.as_mut(),)); diff --git a/ostd/src/arch/x86/mod.rs b/ostd/src/arch/x86/mod.rs index 11475f0c2..21cd13b91 100644 --- a/ostd/src/arch/x86/mod.rs +++ b/ostd/src/arch/x86/mod.rs @@ -108,10 +108,21 @@ pub(crate) fn init_on_bsp() { kernel::pic::init(); } +/// Architecture-specific initialization on the application processor. +/// +/// # Safety +/// +/// This function must be called only once on each application processor. +/// And it should be called after the BSP's call to [`init_on_bsp`]. +pub(crate) unsafe fn init_on_ap() { + // Trigger the initialization of the local APIC. + crate::arch::x86::kernel::apic::with_borrow(|_| {}); +} + pub(crate) fn interrupts_ack(irq_number: usize) { if !cpu::CpuException::is_cpu_exception(irq_number as u16) { kernel::pic::ack(); - kernel::apic::borrow(|apic| { + kernel::apic::with_borrow(|apic| { apic.eoi(); }); } diff --git a/ostd/src/arch/x86/timer/apic.rs b/ostd/src/arch/x86/timer/apic.rs index 8838357d1..a6fdc060c 100644 --- a/ostd/src/arch/x86/timer/apic.rs +++ b/ostd/src/arch/x86/timer/apic.rs @@ -54,7 +54,7 @@ fn is_tsc_deadline_mode_supported() -> bool { fn init_tsc_mode() -> IrqLine { let timer_irq = IrqLine::alloc().unwrap(); // Enable tsc deadline mode - apic::borrow(|apic| { + apic::with_borrow(|apic| { apic.set_lvt_timer(timer_irq.num() as u64 | (1 << 18)); }); let tsc_step = TSC_FREQ.load(Ordering::Relaxed) / TIMER_FREQ; @@ -81,7 +81,7 @@ fn init_periodic_mode() -> IrqLine { super::pit::enable_ioapic_line(irq.clone()); // Set APIC timer count - apic::borrow(|apic| { + apic::with_borrow(|apic| { apic.set_timer_div_config(DivideConfig::Divide64); apic.set_timer_init_count(0xFFFF_FFFF); }); @@ -99,7 +99,7 @@ fn init_periodic_mode() -> IrqLine { // Init APIC Timer let timer_irq = IrqLine::alloc().unwrap(); - apic::borrow(|apic| { + apic::with_borrow(|apic| { apic.set_timer_init_count(INIT_COUNT.load(Ordering::Relaxed)); apic.set_lvt_timer(timer_irq.num() as u64 | (1 << 17)); apic.set_timer_div_config(DivideConfig::Divide64); @@ -115,7 +115,7 @@ fn init_periodic_mode() -> IrqLine { if IN_TIME.load(Ordering::Relaxed) < CALLBACK_TIMES || IS_FINISH.load(Ordering::Acquire) { if IN_TIME.load(Ordering::Relaxed) == 0 { - let remain_ticks = apic::borrow(|apic| apic.timer_current_count()); + let remain_ticks = apic::with_borrow(|apic| apic.timer_current_count()); APIC_FIRST_COUNT.store(0xFFFF_FFFF - remain_ticks, Ordering::Relaxed); } IN_TIME.fetch_add(1, Ordering::Relaxed); @@ -124,7 +124,7 @@ fn init_periodic_mode() -> IrqLine { // Stop PIT and APIC Timer super::pit::disable_ioapic_line(); - let remain_ticks = apic::borrow(|apic| { + let remain_ticks = apic::with_borrow(|apic| { let remain_ticks = apic.timer_current_count(); apic.set_timer_init_count(0); remain_ticks diff --git a/ostd/src/boot/smp.rs b/ostd/src/boot/smp.rs index 6c4af1429..ef722663e 100644 --- a/ostd/src/boot/smp.rs +++ b/ostd/src/boot/smp.rs @@ -123,6 +123,13 @@ fn ap_early_entry(local_apic_id: u32) -> ! { unsafe { trapframe::init(); } + + // SAFETY: this function is only called once on this AP, after the BSP has + // done the architecture-specific initialization. + unsafe { + crate::arch::init_on_ap(); + } + crate::arch::irq::enable_local(); // SAFETY: this function is only called once on this AP.