170a4e555SLoGin use core::{
270a4e555SLoGin cell::RefCell,
370a4e555SLoGin hint::spin_loop,
470a4e555SLoGin ptr::{read_volatile, write_volatile},
570a4e555SLoGin };
670a4e555SLoGin
7*2eab6dd7S曾俊 use log::{debug, error, info};
8*2eab6dd7S曾俊
970a4e555SLoGin use crate::{
1070a4e555SLoGin mm::{
1170a4e555SLoGin mmio_buddy::{mmio_pool, MMIOSpaceGuard},
1270a4e555SLoGin percpu::PerCpu,
1370a4e555SLoGin PhysAddr, VirtAddr,
1470a4e555SLoGin },
1570a4e555SLoGin smp::core::smp_get_processor_id,
1670a4e555SLoGin };
1770a4e555SLoGin
18e2841179SLoGin use super::{hw_irq::ApicId, LVTRegister, LocalAPIC, LVT};
1970a4e555SLoGin
2070a4e555SLoGin /// per-cpu的xAPIC的MMIO空间起始地址
21e2841179SLoGin static mut XAPIC_INSTANCES: [RefCell<Option<XApic>>; PerCpu::MAX_CPU_NUM as usize] =
22e2841179SLoGin [const { RefCell::new(None) }; PerCpu::MAX_CPU_NUM as usize];
2370a4e555SLoGin
2470a4e555SLoGin #[inline(always)]
current_xapic_instance() -> &'static RefCell<Option<XApic>>2570a4e555SLoGin pub(super) fn current_xapic_instance() -> &'static RefCell<Option<XApic>> {
26e2841179SLoGin unsafe { &XAPIC_INSTANCES.as_ref()[smp_get_processor_id().data() as usize] }
2770a4e555SLoGin }
2870a4e555SLoGin
2970a4e555SLoGin /// TODO:统一变量
3070a4e555SLoGin /// @brief local APIC 寄存器地址偏移量
3170a4e555SLoGin #[derive(Debug)]
3270a4e555SLoGin #[allow(dead_code)]
3370a4e555SLoGin #[allow(non_camel_case_types)]
3470a4e555SLoGin #[repr(u32)]
3570a4e555SLoGin pub enum XApicOffset {
3670a4e555SLoGin // 定义各个寄存器的地址偏移量
3770a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_ID = 0x20,
3870a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_Version = 0x30,
3970a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_TPR = 0x80,
4070a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_APR = 0x90,
4170a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_PPR = 0xa0,
4270a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_EOI = 0xb0,
4370a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_RRD = 0xc0,
4470a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_LDR = 0xd0,
4570a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_DFR = 0xe0,
4670a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_SVR = 0xf0,
4770a4e555SLoGin
4870a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_ISR_31_0 = 0x100, // In-Service Register
4970a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_ISR_63_32 = 0x110,
5070a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_ISR_95_64 = 0x120,
5170a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_ISR_127_96 = 0x130,
5270a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_ISR_159_128 = 0x140,
5370a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_ISR_191_160 = 0x150,
5470a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_ISR_223_192 = 0x160,
5570a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_ISR_255_224 = 0x170,
5670a4e555SLoGin
5770a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_TMR_31_0 = 0x180, // Trigger Mode Register
5870a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_TMR_63_32 = 0x190,
5970a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_TMR_95_64 = 0x1a0,
6070a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_TMR_127_96 = 0x1b0,
6170a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_TMR_159_128 = 0x1c0,
6270a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_TMR_191_160 = 0x1d0,
6370a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_TMR_223_192 = 0x1e0,
6470a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_TMR_255_224 = 0x1f0,
6570a4e555SLoGin
6670a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_IRR_31_0 = 0x200, // Interrupt Request Register
6770a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_IRR_63_32 = 0x210,
6870a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_IRR_95_64 = 0x220,
6970a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_IRR_127_96 = 0x230,
7070a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_IRR_159_128 = 0x240,
7170a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_IRR_191_160 = 0x250,
7270a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_IRR_223_192 = 0x260,
7370a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_IRR_255_224 = 0x270,
7470a4e555SLoGin
7570a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_ESR = 0x280, // Error Status Register
7670a4e555SLoGin
7770a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_LVT_CMCI = 0x2f0, // Corrected Machine Check Interrupt Register
7870a4e555SLoGin
7970a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_ICR_31_0 = 0x300, // Interrupt Command Register
8070a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_ICR_63_32 = 0x310,
8170a4e555SLoGin
8270a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_LVT_TIMER = 0x320,
8370a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_LVT_THERMAL = 0x330,
8470a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_LVT_PERFORMANCE_MONITOR = 0x340,
8570a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_LVT_LINT0 = 0x350,
8670a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_LVT_LINT1 = 0x360,
8770a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_LVT_ERROR = 0x370,
8870a4e555SLoGin // 初始计数寄存器(定时器专用)
8970a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_INITIAL_COUNT_REG = 0x380,
9070a4e555SLoGin // 当前计数寄存器(定时器专用)
9170a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_CURRENT_COUNT_REG = 0x390,
9270a4e555SLoGin LOCAL_APIC_OFFSET_Local_APIC_CLKDIV = 0x3e0,
9370a4e555SLoGin }
9470a4e555SLoGin
95b5b571e0SLoGin impl From<XApicOffset> for u32 {
from(val: XApicOffset) -> Self96b5b571e0SLoGin fn from(val: XApicOffset) -> Self {
97b5b571e0SLoGin val as u32
9870a4e555SLoGin }
9970a4e555SLoGin }
10070a4e555SLoGin
10170a4e555SLoGin impl From<LVTRegister> for XApicOffset {
from(lvt: LVTRegister) -> Self10270a4e555SLoGin fn from(lvt: LVTRegister) -> Self {
10370a4e555SLoGin match lvt {
10470a4e555SLoGin LVTRegister::Timer => XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_LVT_TIMER,
10570a4e555SLoGin LVTRegister::Thermal => XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_LVT_THERMAL,
10670a4e555SLoGin LVTRegister::PerformanceMonitor => {
10770a4e555SLoGin XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_LVT_PERFORMANCE_MONITOR
10870a4e555SLoGin }
10970a4e555SLoGin LVTRegister::LINT0 => XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_LVT_LINT0,
11070a4e555SLoGin LVTRegister::LINT1 => XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_LVT_LINT1,
11170a4e555SLoGin LVTRegister::ErrorReg => XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_LVT_ERROR,
11270a4e555SLoGin LVTRegister::CMCI => XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_LVT_CMCI,
11370a4e555SLoGin }
11470a4e555SLoGin }
11570a4e555SLoGin }
11670a4e555SLoGin
11770a4e555SLoGin #[derive(Debug)]
11870a4e555SLoGin #[allow(dead_code)]
11970a4e555SLoGin pub struct XApic {
12070a4e555SLoGin /// 当前xAPIC的寄存器映射的虚拟地址。注意,每个CPU都有自己的xAPIC,所以这个地址是每个CPU都不一样的。
12170a4e555SLoGin apic_vaddr: VirtAddr,
12270a4e555SLoGin /// `apic_vaddr`与映射的空间起始位置之间的偏移量
12370a4e555SLoGin offset: usize,
12470a4e555SLoGin map_guard: MMIOSpaceGuard,
12570a4e555SLoGin xapic_base: PhysAddr,
12670a4e555SLoGin }
12770a4e555SLoGin
12870a4e555SLoGin impl XApic {
12970a4e555SLoGin /// 读取指定寄存器的值
13070a4e555SLoGin #[allow(dead_code)]
read(&self, reg: XApicOffset) -> u3213170a4e555SLoGin pub unsafe fn read(&self, reg: XApicOffset) -> u32 {
13270a4e555SLoGin read_volatile((self.apic_vaddr.data() + reg as usize) as *const u32)
13370a4e555SLoGin }
13470a4e555SLoGin
13570a4e555SLoGin /// 将指定的值写入寄存器
13670a4e555SLoGin #[allow(dead_code)]
write(&self, reg: XApicOffset, value: u32)13770a4e555SLoGin pub unsafe fn write(&self, reg: XApicOffset, value: u32) {
13870a4e555SLoGin write_volatile(
13970a4e555SLoGin (self.apic_vaddr.data() + (reg as u32) as usize) as *mut u32,
14070a4e555SLoGin value,
14170a4e555SLoGin );
14270a4e555SLoGin self.read(XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_ID); // 等待写操作完成,通过读取进行同步
14370a4e555SLoGin }
14470a4e555SLoGin }
14570a4e555SLoGin
14670a4e555SLoGin impl XApic {
14770a4e555SLoGin /// 创建新的XAPIC实例
14870a4e555SLoGin ///
14970a4e555SLoGin /// ## 参数
15070a4e555SLoGin ///
15170a4e555SLoGin /// - `xapic_base` - 当前核心的xAPIC的寄存器的物理地址
new(xapic_base: PhysAddr) -> Self15270a4e555SLoGin pub unsafe fn new(xapic_base: PhysAddr) -> Self {
15370a4e555SLoGin let offset = xapic_base.data() & 0xffff;
15470a4e555SLoGin let paddr = PhysAddr::new(xapic_base.data() & !0xffff);
15570a4e555SLoGin let g = mmio_pool()
15670a4e555SLoGin .create_mmio(4096)
15770a4e555SLoGin .expect("Fail to create MMIO for XAPIC");
15870a4e555SLoGin g.map_phys(paddr, 4096).expect("Fail to map MMIO for XAPIC");
15970a4e555SLoGin let addr = g.vaddr() + offset;
16070a4e555SLoGin
161*2eab6dd7S曾俊 debug!(
16270a4e555SLoGin "XAPIC: {:#x} -> {:#x}, offset={offset}",
16370a4e555SLoGin xapic_base.data(),
16470a4e555SLoGin addr.data()
16570a4e555SLoGin );
16670a4e555SLoGin
16770a4e555SLoGin let r = Self {
16870a4e555SLoGin apic_vaddr: addr,
16970a4e555SLoGin offset,
17070a4e555SLoGin map_guard: g,
17170a4e555SLoGin xapic_base,
17270a4e555SLoGin };
17370a4e555SLoGin
17470a4e555SLoGin return r;
17570a4e555SLoGin }
17670a4e555SLoGin }
17770a4e555SLoGin
17870a4e555SLoGin #[allow(dead_code)]
17970a4e555SLoGin const X1: u32 = 0x0000000B; // 将除数设置为1,即不除频率
18070a4e555SLoGin #[allow(dead_code)]
18170a4e555SLoGin const PERIODIC: u32 = 0x00020000; // 周期性模式
18270a4e555SLoGin #[allow(dead_code)]
18370a4e555SLoGin const ENABLE: u32 = 0x00000100; // 单元使能
18470a4e555SLoGin #[allow(dead_code)]
18570a4e555SLoGin const MASKED: u32 = 0x00010000; // 中断屏蔽
18670a4e555SLoGin const LEVEL: u32 = 0x00008000; // 电平触发
18770a4e555SLoGin const BCAST: u32 = 0x00080000; // 发送到所有APIC,包括自己
18870a4e555SLoGin const DELIVS: u32 = 0x00001000; // 传递状态
18970a4e555SLoGin const INIT: u32 = 0x00000500; // INIT/RESET
19070a4e555SLoGin
19170a4e555SLoGin //中断请求
19270a4e555SLoGin #[allow(dead_code)]
19370a4e555SLoGin const T_IRQ0: u32 = 32; // IRQ 0 对应于 T_IRQ 中断
19470a4e555SLoGin #[allow(dead_code)]
19570a4e555SLoGin const IRQ_TIMER: u32 = 0;
19670a4e555SLoGin #[allow(dead_code)]
19770a4e555SLoGin const IRQ_KBD: u32 = 1;
19870a4e555SLoGin #[allow(dead_code)]
19970a4e555SLoGin const IRQ_COM1: u32 = 4;
20070a4e555SLoGin #[allow(dead_code)]
20170a4e555SLoGin const IRQ_IDE: u32 = 14;
20270a4e555SLoGin #[allow(dead_code)]
20370a4e555SLoGin const IRQ_ERROR: u32 = 19;
20470a4e555SLoGin #[allow(dead_code)]
20570a4e555SLoGin const IRQ_SPURIOUS: u32 = 31;
20670a4e555SLoGin
20770a4e555SLoGin impl LocalAPIC for XApic {
20870a4e555SLoGin /// @brief 判断处理器是否支持apic
support() -> bool20970a4e555SLoGin fn support() -> bool {
21070a4e555SLoGin return x86::cpuid::CpuId::new()
21170a4e555SLoGin .get_feature_info()
21270a4e555SLoGin .expect("Fail to get CPU feature.")
21370a4e555SLoGin .has_apic();
21470a4e555SLoGin }
21570a4e555SLoGin
21670a4e555SLoGin /// @return true -> 函数运行成功
init_current_cpu(&mut self) -> bool21770a4e555SLoGin fn init_current_cpu(&mut self) -> bool {
21870a4e555SLoGin unsafe {
21970a4e555SLoGin // enable xapic
22070a4e555SLoGin x86::msr::wrmsr(x86::msr::APIC_BASE, (self.xapic_base.data() | 0x800) as u64);
22170a4e555SLoGin let val = x86::msr::rdmsr(x86::msr::APIC_BASE);
22270a4e555SLoGin if val & 0x800 != 0x800 {
223*2eab6dd7S曾俊 error!("xAPIC enable failed: APIC_BASE & 0x800 != 0x800");
22470a4e555SLoGin return false;
22570a4e555SLoGin }
22670a4e555SLoGin // 设置 Spurious Interrupt Vector Register
227b5b571e0SLoGin let val = self.read(XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_SVR);
22870a4e555SLoGin
229b5b571e0SLoGin self.write(XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_SVR, val | ENABLE);
23070a4e555SLoGin
231b5b571e0SLoGin let val = self.read(XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_SVR);
23270a4e555SLoGin if val & ENABLE == 0 {
233*2eab6dd7S曾俊 error!("xAPIC software enable failed.");
23470a4e555SLoGin
23570a4e555SLoGin return false;
23670a4e555SLoGin } else {
237*2eab6dd7S曾俊 info!("xAPIC software enabled.");
23870a4e555SLoGin }
23970a4e555SLoGin
24070a4e555SLoGin if val & 0x1000 != 0 {
241*2eab6dd7S曾俊 info!("xAPIC EOI broadcast suppression enabled.");
24270a4e555SLoGin }
24370a4e555SLoGin
24470a4e555SLoGin self.mask_all_lvt();
24570a4e555SLoGin
24670a4e555SLoGin // 清除错误状态寄存器(需要连续写入两次)
247b5b571e0SLoGin self.write(XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_ESR, 0);
248b5b571e0SLoGin self.write(XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_ESR, 0);
24970a4e555SLoGin
25070a4e555SLoGin // 确认任何未完成的中断
251b5b571e0SLoGin self.write(XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_EOI, 0);
25270a4e555SLoGin
25370a4e555SLoGin // 发送 Init Level De-Assert 信号以同步仲裁ID
254b5b571e0SLoGin self.write(XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_ICR_63_32, 0);
25570a4e555SLoGin self.write(
256b5b571e0SLoGin XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_ICR_31_0,
25770a4e555SLoGin BCAST | INIT | LEVEL,
25870a4e555SLoGin );
259b5b571e0SLoGin while self.read(XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_ICR_31_0) & DELIVS != 0 {
26070a4e555SLoGin spin_loop();
26170a4e555SLoGin }
26270a4e555SLoGin }
26370a4e555SLoGin
26470a4e555SLoGin true
26570a4e555SLoGin }
26670a4e555SLoGin
26770a4e555SLoGin /// 发送 EOI(End Of Interrupt)
send_eoi(&self)26870a4e555SLoGin fn send_eoi(&self) {
26970a4e555SLoGin unsafe {
27070a4e555SLoGin let s = self as *const Self as *mut Self;
271b5b571e0SLoGin (*s).write(XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_EOI, 0);
27270a4e555SLoGin }
27370a4e555SLoGin }
27470a4e555SLoGin
27570a4e555SLoGin /// 获取版本号
version(&self) -> u827670a4e555SLoGin fn version(&self) -> u8 {
277b5b571e0SLoGin unsafe { (self.read(XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_Version) & 0xff) as u8 }
27870a4e555SLoGin }
27970a4e555SLoGin
support_eoi_broadcast_suppression(&self) -> bool28070a4e555SLoGin fn support_eoi_broadcast_suppression(&self) -> bool {
281b5b571e0SLoGin unsafe { ((self.read(XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_Version) >> 24) & 1) == 1 }
28270a4e555SLoGin }
28370a4e555SLoGin
max_lvt_entry(&self) -> u828470a4e555SLoGin fn max_lvt_entry(&self) -> u8 {
28570a4e555SLoGin unsafe {
286b5b571e0SLoGin ((self.read(XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_Version) >> 16) & 0xff) as u8 + 1
28770a4e555SLoGin }
28870a4e555SLoGin }
28970a4e555SLoGin
29070a4e555SLoGin /// 获取ID
id(&self) -> ApicId291e2841179SLoGin fn id(&self) -> ApicId {
292b5b571e0SLoGin unsafe { ApicId::new(self.read(XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_ID) >> 24) }
29370a4e555SLoGin }
29470a4e555SLoGin
29570a4e555SLoGin /// 设置LVT寄存器的值
set_lvt(&mut self, lvt: LVT)29670a4e555SLoGin fn set_lvt(&mut self, lvt: LVT) {
29770a4e555SLoGin unsafe {
29870a4e555SLoGin self.write(lvt.register().into(), lvt.data);
29970a4e555SLoGin }
30070a4e555SLoGin }
30170a4e555SLoGin
read_lvt(&self, reg: LVTRegister) -> LVT30270a4e555SLoGin fn read_lvt(&self, reg: LVTRegister) -> LVT {
30370a4e555SLoGin unsafe {
30470a4e555SLoGin LVT::new(
30570a4e555SLoGin reg,
306b5b571e0SLoGin self.read(XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_LVT_TIMER),
30770a4e555SLoGin )
30870a4e555SLoGin .unwrap()
30970a4e555SLoGin }
31070a4e555SLoGin }
31170a4e555SLoGin
mask_all_lvt(&mut self)31270a4e555SLoGin fn mask_all_lvt(&mut self) {
31370a4e555SLoGin // self.set_lvt(LVT::new(LVTRegister::CMCI, LVT::MASKED).unwrap());
31470a4e555SLoGin self.set_lvt(LVT::new(LVTRegister::Timer, LVT::MASKED).unwrap());
31570a4e555SLoGin self.set_lvt(LVT::new(LVTRegister::Thermal, LVT::MASKED).unwrap());
31670a4e555SLoGin self.set_lvt(LVT::new(LVTRegister::PerformanceMonitor, LVT::MASKED).unwrap());
31770a4e555SLoGin self.set_lvt(LVT::new(LVTRegister::LINT0, LVT::MASKED).unwrap());
31870a4e555SLoGin self.set_lvt(LVT::new(LVTRegister::LINT1, LVT::MASKED).unwrap());
31970a4e555SLoGin self.set_lvt(LVT::new(LVTRegister::ErrorReg, LVT::MASKED).unwrap());
32070a4e555SLoGin }
32170a4e555SLoGin
write_icr(&self, icr: x86::apic::Icr)32270a4e555SLoGin fn write_icr(&self, icr: x86::apic::Icr) {
32370a4e555SLoGin unsafe {
32470a4e555SLoGin // Wait for any previous send to finish
325b5b571e0SLoGin while self.read(XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_ICR_31_0) & DELIVS != 0 {
32670a4e555SLoGin spin_loop();
32770a4e555SLoGin }
32870a4e555SLoGin
32970a4e555SLoGin self.write(
330b5b571e0SLoGin XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_ICR_63_32,
33170a4e555SLoGin icr.upper(),
33270a4e555SLoGin );
33370a4e555SLoGin self.write(
334b5b571e0SLoGin XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_ICR_31_0,
33570a4e555SLoGin icr.lower(),
33670a4e555SLoGin );
33770a4e555SLoGin
33870a4e555SLoGin // Wait for send to finish
339b5b571e0SLoGin while self.read(XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_ICR_31_0) & DELIVS != 0 {
34070a4e555SLoGin spin_loop();
34170a4e555SLoGin }
34270a4e555SLoGin }
34370a4e555SLoGin }
34470a4e555SLoGin }
345