use std::convert::TryFrom;
use std::convert::TryInto;
use std::time::Duration;
use std::time::Instant;
use base::error;
use base::warn;
use base::TimerTrait;
use bit_field::*;
use hypervisor::DeliveryMode;
use hypervisor::DeliveryStatus;
use hypervisor::DestinationMode;
use hypervisor::LapicState;
use hypervisor::Level;
use hypervisor::MPState;
use hypervisor::MsiAddressMessage;
use hypervisor::MsiDataMessage;
use hypervisor::TriggerMode;
pub type Vector = u8;
pub const APIC_BASE_ADDRESS: u64 = 0xFEE00000;
pub const APIC_MEM_LENGTH_BYTES: u64 = 0x1000;
const CYCLE_LENGTH_FALLBACK: Duration = Duration::from_nanos(10);
const REG_ALIGN_BYTES: usize = 16;
const BOOTSTRAP_PROCESSOR: u8 = 0;
const VERSION: u8 = 0x14;
const MAX_LVT: u8 = 5;
const LOCAL_VECTOR_MASKED: u32 = 1 << 16;
const DESTINATION_FORMAT_FLAT: u8 = 0xF;
const DESTINATION_FORMAT_CLUSTER: u8 = 0x0;
const PHYSICAL_BROADCAST_ADDRESS: u8 = 0xFF;
const SOFTWARE_ENABLE: u32 = 1 << 8;
const TIMER_MODE_MASK: u32 = 3 << 17;
const TIMER_MODE_ONE_SHOT: u32 = 0 << 17;
const TIMER_MODE_PERIODIC: u32 = 1 << 17;
const TIMER_MODE_TSC_DEADLINE: u32 = 2 << 17;
const TIMER_DIVIDE_TABLE: [u32; 16] = [
    2, 4, 8, 16, 1, 1, 1, 1, 32, 64, 128, 1, 1, 1, 1, 1, ];
const ZERO_DURATION: Duration = Duration::from_nanos(0);
pub struct Apic {
    id: u8,
    cycle_length: Duration,
    regs: [u8; APIC_MEM_LENGTH_BYTES as usize],
    mp_state: MPState,
    timer: Box<dyn TimerTrait>,
    timer_length: Option<Duration>,
    last_tick: Instant,
    sipi: Option<Vector>,
    init: bool,
    nmis: u32,
}
impl Apic {
    pub fn new(id: u8, timer: Box<dyn TimerTrait>) -> Self {
        let cycle_length = Duration::from_nanos(1_000_000_000 / Self::frequency() as u64);
        let mp_state = if id == BOOTSTRAP_PROCESSOR {
            MPState::Runnable
        } else {
            MPState::Uninitialized
        };
        let mut apic = Apic {
            id,
            cycle_length,
            regs: [0; APIC_MEM_LENGTH_BYTES as usize],
            mp_state,
            timer,
            timer_length: None,
            last_tick: Instant::now(),
            sipi: None,
            init: false,
            nmis: 0,
        };
        apic.load_reset_state();
        apic
    }
    pub fn frequency() -> u32 {
        match crate::tsc::bus_freq_hz(std::arch::x86_64::__cpuid_count) {
            Some(hz) => hz,
            None => (1_000_000_000u128 / CYCLE_LENGTH_FALLBACK.as_nanos()) as u32,
        }
    }
    pub fn id(&self) -> u8 {
        self.id
    }
    pub fn get_cycle_length(&self) -> Duration {
        self.cycle_length
    }
    pub fn get_state(&self) -> LapicState {
        let mut state = LapicState { regs: [0; 64] };
        for reg in 0..state.regs.len() {
            state.regs[reg] = self.get_reg(reg * REG_ALIGN_BYTES);
        }
        state
    }
    pub fn set_state(&mut self, state: &LapicState) {
        for (reg, val) in state.regs.iter().enumerate() {
            self.set_reg(reg * REG_ALIGN_BYTES, *val);
        }
        self.start_timer();
    }
    pub fn get_mp_state(&self) -> MPState {
        self.mp_state
    }
    pub fn set_mp_state(&mut self, state: &MPState) {
        self.mp_state = *state;
    }
    fn valid_mmio(offset: u64, data: &[u8]) -> bool {
        if offset.trailing_zeros() >= 4 && data.len() == 4 {
            true
        } else {
            error!(
                "Invalid offset {} or size {} for apic mmio",
                offset,
                data.len()
            );
            false
        }
    }
    pub fn read(&self, offset: u64, data: &mut [u8]) {
        if !Self::valid_mmio(offset, data) {
            return;
        }
        let offset = offset as usize;
        let val = match offset {
            Reg::PPR => self.get_processor_priority() as u32,
            Reg::TIMER_CURRENT_COUNT => {
                let count_remaining = self.next_timer_expiration().as_nanos()
                    / self.cycle_length.as_nanos()
                    / self.get_timer_divide_control() as u128;
                count_remaining.try_into().unwrap_or_else(|_| {
                    warn!("APIC time remaining overflow");
                    u32::MAX
                })
            }
            _ => self.get_reg(offset),
        };
        data.copy_from_slice(&val.to_le_bytes());
    }
    pub fn write(&mut self, offset: u64, data: &[u8]) -> Option<ApicBusMsg> {
        if !Self::valid_mmio(offset, data) {
            return None;
        }
        let offset = offset as usize;
        let data = u32::from_le_bytes(data.try_into().unwrap());
        let mut msg: Option<ApicBusMsg> = None;
        match offset {
            Reg::ID => {}
            Reg::TPR => self.set_reg(Reg::TPR, data & 0xFF), Reg::EOI => {
                if let Some(vector) = self.highest_bit_in_vector(VectorReg::Isr) {
                    self.clear_vector_bit(VectorReg::Isr, vector);
                    msg = Some(ApicBusMsg::Eoi(vector));
                    }
            }
            Reg::INTERRUPT_COMMAND_LO => {
                self.set_reg(Reg::INTERRUPT_COMMAND_LO, data & !(1 << 12));
                let interrupt = self.decode_icr();
                msg = Some(ApicBusMsg::Ipi(interrupt));
            }
            Reg::TIMER_DIVIDE_CONTROL
            | Reg::LOCAL_CMCI
            | Reg::INTERRUPT_COMMAND_HI
            | Reg::SPURIOUS_INT
            | Reg::LOGICAL_DESTINATION
            | Reg::DESTINATION_FORMAT => self.set_reg(offset, data),
            Reg::LOCAL_INT_0
            | Reg::LOCAL_INT_1
            | Reg::LOCAL_THERMAL
            | Reg::LOCAL_PERF
            | Reg::LOCAL_ERROR => {
                if self.enabled() {
                    self.set_reg(offset, data);
                } else {
                    self.set_reg(offset, data | LOCAL_VECTOR_MASKED);
                }
            }
            Reg::TIMER_INITIAL_COUNT => {
                self.set_reg(Reg::TIMER_INITIAL_COUNT, data);
                self.start_timer();
            }
            Reg::LOCAL_TIMER => {
                let old_mode = self.get_reg(Reg::LOCAL_TIMER) & TIMER_MODE_MASK;
                let new_mode = data & TIMER_MODE_MASK;
                if old_mode != new_mode {
                    self.clear_timer();
                }
                self.set_reg(Reg::LOCAL_TIMER, data);
            }
            _ => {
                }
        }
        msg
    }
    pub fn single_dest_fast(dest: &InterruptDestination) -> Option<u8> {
        if dest.shorthand == DestinationShorthand::Self_ {
            Some(dest.source_id)
        } else if dest.shorthand == DestinationShorthand::None
            && dest.mode == DestinationMode::Physical
            && dest.dest_id != PHYSICAL_BROADCAST_ADDRESS
        {
            Some(dest.dest_id)
        } else {
            None
        }
    }
    pub fn match_dest(&self, dest: &InterruptDestination) -> bool {
        match dest.shorthand {
            DestinationShorthand::All => true,
            DestinationShorthand::AllExcludingSelf => dest.source_id != self.id,
            DestinationShorthand::Self_ => dest.source_id == self.id,
            DestinationShorthand::None => match dest.mode {
                DestinationMode::Physical => {
                    dest.dest_id == PHYSICAL_BROADCAST_ADDRESS || dest.dest_id == self.id
                }
                DestinationMode::Logical => self.matches_logical_address(dest.dest_id),
            },
        }
    }
    pub fn get_processor_priority(&self) -> u8 {
        let tpr = self.regs[Reg::TPR];
        let isrv = self.highest_bit_in_vector(VectorReg::Isr).unwrap_or(0);
        if tpr >> 4 >= isrv >> 4 {
            tpr
        } else {
            isrv & !0xF
        }
    }
    pub fn accept_irq(&mut self, i: &InterruptData) {
        match i.delivery {
            DeliveryMode::Fixed | DeliveryMode::Lowest => {
                self.set_vector_bit(VectorReg::Irr, i.vector);
                if i.trigger == TriggerMode::Level {
                    self.set_vector_bit(VectorReg::Tmr, i.vector);
                } else {
                    self.clear_vector_bit(VectorReg::Tmr, i.vector);
                }
                self.mp_state = MPState::Runnable;
            }
            DeliveryMode::Startup => self.sipi = Some(i.vector),
            DeliveryMode::Init => {
                if i.level == Level::Assert {
                    self.init = true;
                }
            }
            DeliveryMode::NMI => self.nmis += 1,
            DeliveryMode::External => warn!("APIC doesn't handle external interrupts, dropping"),
            DeliveryMode::RemoteRead => {
                }
            DeliveryMode::SMI => warn!("APIC doesn't handle SMIs, dropping interrupt"),
        }
    }
    fn inject_interrupt(&mut self, clear: bool) -> Option<Vector> {
        let irrv = self.highest_bit_in_vector(VectorReg::Irr).unwrap_or(0);
        if irrv >> 4 > self.get_processor_priority() >> 4 {
            if clear {
                self.clear_vector_bit(VectorReg::Irr, irrv);
                self.set_vector_bit(VectorReg::Isr, irrv);
            }
            Some(irrv)
        } else {
            None
        }
    }
    fn decode_icr(&mut self) -> Interrupt {
        let hi = self.get_reg(Reg::INTERRUPT_COMMAND_HI) as u64;
        let lo = self.get_reg(Reg::INTERRUPT_COMMAND_LO) as u64;
        let icr = hi << 32 | lo;
        let mut command = InterruptCommand::new();
        command.set(0, 64, icr);
        Interrupt {
            dest: InterruptDestination {
                source_id: self.id,
                dest_id: command.get_destination(),
                shorthand: command.get_shorthand(),
                mode: command.get_destination_mode(),
            },
            data: InterruptData {
                vector: command.get_vector(),
                delivery: command.get_delivery(),
                trigger: command.get_trigger(),
                level: command.get_level(),
            },
        }
    }
    fn enabled(&self) -> bool {
        self.get_reg(Reg::SPURIOUS_INT) & SOFTWARE_ENABLE != 0
    }
    pub fn set_enabled(&mut self, enable: bool) {
        let mut val = self.get_reg(Reg::SPURIOUS_INT);
        if enable {
            val |= SOFTWARE_ENABLE;
        } else {
            val &= !SOFTWARE_ENABLE;
        }
        self.set_reg(Reg::SPURIOUS_INT, val);
    }
    pub fn get_pending_irqs(&mut self, vcpu_ready: bool) -> PendingInterrupts {
        let (fixed, needs_window) = if !self.enabled() {
            (None, false)
        } else {
            match self.inject_interrupt(vcpu_ready) {
                Some(vector) if vcpu_ready => {
                    let has_second_interrupt = self.inject_interrupt(false).is_some();
                    (Some(vector), has_second_interrupt)
                }
                Some(_) if !vcpu_ready => (None, true),
                None => (None, false),
                _ => unreachable!(),
            }
        };
        let nmis = self.nmis;
        self.nmis = 0;
        let init = self.init;
        self.init = false;
        let startup = self.sipi;
        self.sipi = None;
        PendingInterrupts {
            fixed,
            nmis,
            init,
            startup,
            needs_window,
        }
    }
    pub fn load_reset_state(&mut self) {
        for reg in self.regs.iter_mut() {
            *reg = 0;
        }
        self.set_reg(Reg::DESTINATION_FORMAT, 0xFFFFFFFF);
        self.set_reg(Reg::LOCAL_INT_0, LOCAL_VECTOR_MASKED);
        self.set_reg(Reg::LOCAL_INT_1, LOCAL_VECTOR_MASKED);
        self.set_reg(Reg::LOCAL_THERMAL, LOCAL_VECTOR_MASKED);
        self.set_reg(Reg::LOCAL_PERF, LOCAL_VECTOR_MASKED);
        self.set_reg(Reg::LOCAL_ERROR, LOCAL_VECTOR_MASKED);
        self.set_reg(Reg::LOCAL_TIMER, LOCAL_VECTOR_MASKED);
        self.clear_timer();
        let mut version = VersionRegister::new();
        version.set_version(VERSION);
        version.set_max_lvt(MAX_LVT);
        version.set_eoi_broadcast_suppression(1);
        let bits = version.get(0, 32) as u32;
        self.set_reg(Reg::VERSION, bits);
        self.set_reg(Reg::ID, (self.id as u32) << 24);
        self.set_reg(Reg::SPURIOUS_INT, 0xFF);
    }
    pub fn debug_status(&self) -> String {
        let mut irr = [0u32; 8];
        let mut isr = [0u32; 8];
        for i in 0..8 {
            irr[i] = self.get_reg(Reg::IRR + i * REG_ALIGN_BYTES);
            isr[i] = self.get_reg(Reg::ISR + i * REG_ALIGN_BYTES);
        }
        let irrv = self.highest_bit_in_vector(VectorReg::Irr).unwrap_or(0);
        let isrv = self.highest_bit_in_vector(VectorReg::Isr).unwrap_or(0);
        let timer = self
            .timer_length
            .map(|d| format!("{}ns", d.as_nanos()))
            .unwrap_or("None".to_string());
        format!(
            "enabled={} irr={:?} irrv={} isr={:?} isrv={} irrv_prio={} proc_prio={}, timer={}",
            self.enabled(),
            irr,
            irrv,
            isr,
            isrv,
            irrv >> 4,
            self.get_processor_priority() >> 4,
            timer,
        )
    }
    pub fn handle_timer_expiration(&mut self) {
        if let Err(e) = self.timer.mark_waited() {
            error!("APIC timer wait unexpectedly failed: {}", e);
            return;
        }
        self.last_tick = Instant::now();
        let local_timer = self.get_reg(Reg::LOCAL_TIMER);
        let is_masked = local_timer & LOCAL_VECTOR_MASKED != 0;
        if is_masked || self.timer_length.is_none() {
            return;
        }
        let vector = local_timer as u8;
        self.accept_irq(&InterruptData {
            vector,
            delivery: DeliveryMode::Fixed,
            trigger: TriggerMode::Edge,
            level: Level::Deassert,
        });
    }
    fn get_reg(&self, offset: usize) -> u32 {
        let bytes = &self.regs[offset..offset + 4];
        u32::from_le_bytes(bytes.try_into().unwrap())
    }
    fn set_reg(&mut self, offset: usize, val: u32) {
        self.regs[offset..offset + 4].copy_from_slice(&val.to_le_bytes());
    }
    fn reg_bit_for_vector(reg: VectorReg, vector: Vector) -> (usize, u8) {
        let vector = vector as usize;
        let index = (reg as usize) + 0x10 * (vector >> 5) + ((vector >> 3) & 0x3);
        let bitmask = 1 << (vector & 0x7);
        (index, bitmask)
    }
    fn set_vector_bit(&mut self, reg: VectorReg, vector: Vector) {
        let (reg, bitmask) = Self::reg_bit_for_vector(reg, vector);
        self.regs[reg] |= bitmask;
    }
    fn clear_vector_bit(&mut self, reg: VectorReg, vector: Vector) {
        let (reg, bitmask) = Self::reg_bit_for_vector(reg, vector);
        self.regs[reg] &= !bitmask;
    }
    fn highest_bit_in_vector(&self, reg: VectorReg) -> Option<Vector> {
        let reg = reg as usize;
        for i in (0..8).rev() {
            let val = self.get_reg(reg + i * REG_ALIGN_BYTES);
            if val != 0 {
                let msb_set = 31 - val.leading_zeros() as u8;
                return Some(msb_set + 32 * i as u8);
            }
        }
        None
    }
    fn matches_logical_address(&self, dest: u8) -> bool {
        let bits = self.get_reg(Reg::DESTINATION_FORMAT) as u64;
        let mut format = DestinationFormat::new();
        format.set(0, 32, bits);
        let model = format.get_model();
        let bits = self.get_reg(Reg::LOGICAL_DESTINATION) as u64;
        let mut logical_dest = LogicalDestination::new();
        logical_dest.set(0, 32, bits);
        let local_logical_id = logical_dest.get_logical_id();
        match model {
            DESTINATION_FORMAT_FLAT => dest & local_logical_id != 0,
            DESTINATION_FORMAT_CLUSTER => {
                error!("Cluster-mode APIC logical destinations unsupported");
                false
            }
            _ => {
                error!("Invalid APIC logical destination format {}", model);
                false
            }
        }
    }
    fn get_timer_divide_control(&self) -> u32 {
        let div_control = self.get_reg(Reg::TIMER_DIVIDE_CONTROL) as usize & 0xF;
        TIMER_DIVIDE_TABLE[div_control]
    }
    fn start_timer(&mut self) {
        self.clear_timer();
        let initial_count = self.get_reg(Reg::TIMER_INITIAL_COUNT);
        if initial_count == 0 {
            return;
        }
        let length = self.cycle_length * initial_count * self.get_timer_divide_control();
        let mode = self.get_reg(Reg::LOCAL_TIMER) & TIMER_MODE_MASK;
        match mode {
            TIMER_MODE_ONE_SHOT => {
                if let Err(e) = self.timer.reset_oneshot(length) {
                    error!("Failed to reset APIC timer to one-shot({:?}) {}", length, e);
                    return;
                }
            }
            TIMER_MODE_PERIODIC => {
                if let Err(e) = self.timer.reset_repeating(length) {
                    error!(
                        "Failed to reset APIC timer to repeating({:?}) {}",
                        length, e
                    );
                    return;
                }
            }
            TIMER_MODE_TSC_DEADLINE => {
                warn!("APIC TSC-deadline timer not supported");
                return;
            }
            _ => {
                error!("Invalid APIC timer mode 0x{:X}", mode);
                return;
            }
        };
        self.last_tick = Instant::now();
        self.timer_length = Some(length);
    }
    fn clear_timer(&mut self) {
        if self.timer_length.is_some() {
            if let Err(e) = self.timer.clear() {
                error!("Failed to clear APIC timer: {}", e);
            }
            self.timer_length = None;
        }
    }
    fn next_timer_expiration(&self) -> Duration {
        if let Some(length) = self.timer_length {
            let elapsed = self.last_tick.elapsed();
            length.checked_sub(elapsed).unwrap_or(ZERO_DURATION)
        } else {
            ZERO_DURATION
        }
    }
}
impl Drop for Apic {
    fn drop(&mut self) {
        self.clear_timer();
    }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ApicBusMsg {
    Eoi(Vector),
    Ipi(Interrupt),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct PendingInterrupts {
    pub fixed: Option<Vector>,
    pub nmis: u32,
    pub init: bool,
    pub startup: Option<Vector>,
    pub needs_window: bool,
}
#[bitfield]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DestinationShorthand {
    None = 0b00,
    Self_ = 0b01,
    All = 0b10,
    AllExcludingSelf = 0b11,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Interrupt {
    pub dest: InterruptDestination,
    pub data: InterruptData,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InterruptDestination {
    pub source_id: u8,
    pub dest_id: u8,
    pub shorthand: DestinationShorthand,
    pub mode: DestinationMode,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InterruptData {
    pub vector: Vector,
    pub delivery: DeliveryMode,
    pub trigger: TriggerMode,
    pub level: Level,
}
impl TryFrom<&MsiAddressMessage> for InterruptDestination {
    type Error = String;
    fn try_from(msi: &MsiAddressMessage) -> std::result::Result<Self, Self::Error> {
        if msi.get_always_0xfee() != 0xFEE {
            return Err(format!(
                "top 12 bits must be 0xFEE but are 0x{:X}",
                msi.get_always_0xfee()
            ));
        }
        Ok(InterruptDestination {
            source_id: 0,
            dest_id: msi.get_destination_id(),
            shorthand: DestinationShorthand::None,
            mode: msi.get_destination_mode(),
        })
    }
}
impl From<&MsiDataMessage> for InterruptData {
    fn from(msi: &MsiDataMessage) -> Self {
        InterruptData {
            vector: msi.get_vector(),
            delivery: msi.get_delivery_mode(),
            trigger: msi.get_trigger(),
            level: msi.get_level(),
        }
    }
}
#[bitfield]
#[derive(Clone, Copy)]
struct LocalInterrupt {
    vector: BitField8,
    #[bits = 3]
    delivery_mode: DeliveryMode,
    reserved1: BitField1,
    #[bits = 1]
    delivery_status: DeliveryStatus,
    polarity: BitField1,
    remote_irr: BitField1,
    #[bits = 1]
    trigger: TriggerMode,
    masked: BitField1,
    reserved2: BitField7,
    reserved3: BitField8,
}
#[bitfield]
#[derive(Clone, Copy)]
struct VersionRegister {
    version: BitField8,
    reserved1: BitField8,
    max_lvt: BitField8,
    eoi_broadcast_suppression: BitField1,
    reserved2: BitField7,
}
#[bitfield]
#[derive(Clone, Copy)]
struct DestinationFormat {
    reserved: BitField28,
    model: BitField4,
}
#[bitfield]
#[derive(Clone, Copy)]
struct LogicalDestination {
    reserved: BitField24,
    logical_id: BitField8,
}
#[bitfield]
#[derive(Clone, Copy)]
struct InterruptCommand {
    vector: BitField8,
    #[bits = 3]
    delivery: DeliveryMode,
    #[bits = 1]
    destination_mode: DestinationMode,
    #[bits = 1]
    delivery_status: DeliveryStatus,
    reserved1: BitField1,
    #[bits = 1]
    level: Level,
    #[bits = 1]
    trigger: TriggerMode,
    reserved2: BitField2,
    #[bits = 2]
    shorthand: DestinationShorthand,
    reserved3: BitField36,
    destination: BitField8,
}
struct Reg;
impl Reg {
    const ID: usize = 0x20;
    const VERSION: usize = 0x30;
    const TPR: usize = 0x80;
    const PPR: usize = 0xA0;
    const EOI: usize = 0xB0;
    const LOGICAL_DESTINATION: usize = 0xD0;
    const DESTINATION_FORMAT: usize = 0xE0;
    const SPURIOUS_INT: usize = 0xF0;
    const ISR: usize = 0x100;
    const TMR: usize = 0x180;
    const IRR: usize = 0x200;
    const LOCAL_CMCI: usize = 0x2F0;
    const INTERRUPT_COMMAND_LO: usize = 0x300;
    const INTERRUPT_COMMAND_HI: usize = 0x310;
    const LOCAL_TIMER: usize = 0x320;
    const LOCAL_THERMAL: usize = 0x330;
    const LOCAL_PERF: usize = 0x340;
    const LOCAL_INT_0: usize = 0x350;
    const LOCAL_INT_1: usize = 0x360;
    const LOCAL_ERROR: usize = 0x370;
    const TIMER_INITIAL_COUNT: usize = 0x380;
    const TIMER_CURRENT_COUNT: usize = 0x390;
    const TIMER_DIVIDE_CONTROL: usize = 0x3E0;
}
#[repr(usize)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum VectorReg {
    Isr = Reg::ISR,
    Tmr = Reg::TMR,
    Irr = Reg::IRR,
}
#[cfg(test)]
mod tests {
    use std::mem;
    use std::sync::Arc;
    use base::FakeClock;
    use base::FakeTimer;
    use sync::Mutex;
    use super::*;
    #[test]
    fn struct_size() {
        assert_eq!(4, mem::size_of::<LocalInterrupt>());
        assert_eq!(4, mem::size_of::<VersionRegister>());
        assert_eq!(4, mem::size_of::<DestinationFormat>());
        assert_eq!(4, mem::size_of::<LogicalDestination>());
        assert_eq!(8, mem::size_of::<InterruptCommand>());
    }
    #[test]
    fn get_reg() {
        let timer = Box::new(FakeTimer::new(Arc::new(Mutex::new(FakeClock::new()))));
        let mut a = Apic::new(0, timer);
        a.regs[0..4].copy_from_slice(&[0xFE, 0xCA, 0xAD, 0xAB]);
        assert_eq!(a.get_reg(0), 0xABADCAFE);
        a.regs[4092..4096].copy_from_slice(&[0x0D, 0xF0, 0x1D, 0xC0]);
        assert_eq!(a.get_reg(4092), 0xC01DF00D);
    }
    #[test]
    fn set_reg() {
        let timer = Box::new(FakeTimer::new(Arc::new(Mutex::new(FakeClock::new()))));
        let mut a = Apic::new(0, timer);
        a.set_reg(0, 0xABADCAFE);
        assert_eq!(a.regs[0..4], [0xFE, 0xCA, 0xAD, 0xAB]);
        a.set_reg(4092, 0xC01DF00D);
        assert_eq!(a.regs[4092..4096], [0x0D, 0xF0, 0x1D, 0xC0]);
    }
    #[test]
    fn lapic_state() {
        let timer = Box::new(FakeTimer::new(Arc::new(Mutex::new(FakeClock::new()))));
        let mut a = Apic::new(0, timer);
        a.set_reg(0, 0xABADCAFE);
        assert_eq!(a.get_state().regs[0], 0xABADCAFE);
        let mut state = LapicState { regs: [0; 64] };
        state.regs[63] = 0xC01DF00D;
        a.set_state(&state);
        assert_eq!(a.regs[1008..1012], [0x0D, 0xF0, 0x1D, 0xC0]);
    }
    #[test]
    fn valid_mmio() {
        let timer = Box::new(FakeTimer::new(Arc::new(Mutex::new(FakeClock::new()))));
        let mut a = Apic::new(42, timer);
        let mut data = [0u8; 4];
        a.read(Reg::ID as u64, &mut data);
        assert_eq!(data, [0, 0, 0, 42]);
        a.write(Reg::INTERRUPT_COMMAND_HI as u64, &[0xFE, 0xCA, 0xAD, 0xAB]);
        assert_eq!(a.get_reg(Reg::INTERRUPT_COMMAND_HI), 0xABADCAFE);
        let mut data = [0u8; 4];
        a.read(Reg::INTERRUPT_COMMAND_HI as u64, &mut data);
        assert_eq!(data, [0xFE, 0xCA, 0xAD, 0xAB]);
    }
    #[test]
    fn invalid_mmio() {
        let timer = Box::new(FakeTimer::new(Arc::new(Mutex::new(FakeClock::new()))));
        let mut a = Apic::new(0, timer);
        a.set_reg(Reg::INTERRUPT_COMMAND_HI, 0xABADCAFE);
        let mut data = [0u8; 5];
        a.read(Reg::INTERRUPT_COMMAND_HI as u64, &mut data);
        assert_eq!(data, [0; 5]);
        let mut data = [0u8; 4];
        a.read(Reg::INTERRUPT_COMMAND_HI as u64 + 1, &mut data);
        assert_eq!(data, [0; 4]);
        a.write(Reg::INTERRUPT_COMMAND_HI as u64, &[0; 3]);
        assert_eq!(a.get_reg(Reg::INTERRUPT_COMMAND_HI), 0xABADCAFE);
        a.write(Reg::INTERRUPT_COMMAND_HI as u64 + 1, &[0; 4]);
        assert_eq!(a.get_reg(Reg::INTERRUPT_COMMAND_HI), 0xABADCAFE);
    }
    #[test]
    fn vector_reg() {
        let timer = Box::new(FakeTimer::new(Arc::new(Mutex::new(FakeClock::new()))));
        let mut a = Apic::new(0, timer);
        assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), None);
        a.set_vector_bit(VectorReg::Irr, 0);
        assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), Some(0));
        a.set_vector_bit(VectorReg::Irr, 7);
        assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), Some(7));
        a.set_vector_bit(VectorReg::Irr, 8);
        assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), Some(8));
        a.set_vector_bit(VectorReg::Irr, 31);
        assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), Some(31));
        a.set_vector_bit(VectorReg::Irr, 32);
        assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), Some(32));
        a.set_vector_bit(VectorReg::Irr, 74);
        assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), Some(74));
        a.set_vector_bit(VectorReg::Irr, 66);
        assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), Some(74));
        a.set_vector_bit(VectorReg::Irr, 255);
        assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), Some(255));
        assert_eq!(
            a.get_reg(Reg::IRR),
            0b1000_0000_0000_0000_0000_0001_1000_0001
        );
        assert_eq!(
            a.get_reg(Reg::IRR + 1 * REG_ALIGN_BYTES),
            0b0000_0000_0000_0000_0000_0000_0000_0001
        );
        assert_eq!(
            a.get_reg(Reg::IRR + 2 * REG_ALIGN_BYTES),
            0b0000_0000_0000_0000_0000_0100_0000_0100
        );
        assert_eq!(a.get_reg(Reg::IRR + 3 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::IRR + 4 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::IRR + 5 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::IRR + 6 * REG_ALIGN_BYTES), 0);
        assert_eq!(
            a.get_reg(Reg::IRR + 7 * REG_ALIGN_BYTES),
            0b1000_0000_0000_0000_0000_0000_0000_0000
        );
        a.clear_vector_bit(VectorReg::Irr, 255);
        assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), Some(74));
        a.clear_vector_bit(VectorReg::Irr, 74);
        assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), Some(66));
        a.clear_vector_bit(VectorReg::Irr, 32);
        a.clear_vector_bit(VectorReg::Irr, 66);
        a.clear_vector_bit(VectorReg::Irr, 31);
        a.clear_vector_bit(VectorReg::Irr, 200);
        assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), Some(8));
        assert_eq!(
            a.get_reg(Reg::IRR),
            0b0000_0000_0000_0000_0000_0001_1000_0001
        );
        assert_eq!(a.get_reg(Reg::IRR + 1 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::IRR + 2 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::IRR + 3 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::IRR + 4 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::IRR + 5 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::IRR + 6 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::IRR + 7 * REG_ALIGN_BYTES), 0);
    }
    #[test]
    fn single_dest() {
        assert_eq!(
            Apic::single_dest_fast(&InterruptDestination {
                source_id: 0,
                dest_id: 254,
                shorthand: DestinationShorthand::None,
                mode: DestinationMode::Physical,
            }),
            Some(254)
        );
        assert_eq!(
            Apic::single_dest_fast(&InterruptDestination {
                source_id: 0,
                dest_id: 254,
                shorthand: DestinationShorthand::Self_,
                mode: DestinationMode::Physical,
            }),
            Some(0)
        );
        assert_eq!(
            Apic::single_dest_fast(&InterruptDestination {
                source_id: 0,
                dest_id: PHYSICAL_BROADCAST_ADDRESS,
                shorthand: DestinationShorthand::None,
                mode: DestinationMode::Physical,
            }),
            None
        );
        assert_eq!(
            Apic::single_dest_fast(&InterruptDestination {
                source_id: 0,
                dest_id: 254,
                shorthand: DestinationShorthand::All,
                mode: DestinationMode::Physical,
            }),
            None
        );
        assert_eq!(
            Apic::single_dest_fast(&InterruptDestination {
                source_id: 0,
                dest_id: 254,
                shorthand: DestinationShorthand::AllExcludingSelf,
                mode: DestinationMode::Physical,
            }),
            None
        );
        assert_eq!(
            Apic::single_dest_fast(&InterruptDestination {
                source_id: 0,
                dest_id: 254,
                shorthand: DestinationShorthand::None,
                mode: DestinationMode::Logical,
            }),
            None
        );
    }
    #[test]
    fn match_dest() {
        let timer = Box::new(FakeTimer::new(Arc::new(Mutex::new(FakeClock::new()))));
        let mut a = Apic::new(254, timer);
        a.set_reg(Reg::LOGICAL_DESTINATION, 0b11001001 << 24);
        assert!(a.match_dest(&InterruptDestination {
            source_id: 0,
            dest_id: 254,
            shorthand: DestinationShorthand::None,
            mode: DestinationMode::Physical,
        }));
        assert!(a.match_dest(&InterruptDestination {
            source_id: 0,
            dest_id: PHYSICAL_BROADCAST_ADDRESS,
            shorthand: DestinationShorthand::None,
            mode: DestinationMode::Physical,
        }));
        assert!(!a.match_dest(&InterruptDestination {
            source_id: 0,
            dest_id: 77,
            shorthand: DestinationShorthand::None,
            mode: DestinationMode::Physical,
        }));
        assert!(a.match_dest(&InterruptDestination {
            source_id: 0,
            dest_id: 0b01001000,
            shorthand: DestinationShorthand::None,
            mode: DestinationMode::Logical,
        }));
        assert!(!a.match_dest(&InterruptDestination {
            source_id: 0,
            dest_id: 0b00010010,
            shorthand: DestinationShorthand::None,
            mode: DestinationMode::Logical,
        }));
        assert!(a.match_dest(&InterruptDestination {
            source_id: 0,
            dest_id: 0,
            shorthand: DestinationShorthand::All,
            mode: DestinationMode::Physical,
        }));
        assert!(a.match_dest(&InterruptDestination {
            source_id: 254,
            dest_id: 0,
            shorthand: DestinationShorthand::Self_,
            mode: DestinationMode::Physical,
        }));
        assert!(!a.match_dest(&InterruptDestination {
            source_id: 0,
            dest_id: 0,
            shorthand: DestinationShorthand::Self_,
            mode: DestinationMode::Physical,
        }));
        assert!(a.match_dest(&InterruptDestination {
            source_id: 0,
            dest_id: 0,
            shorthand: DestinationShorthand::AllExcludingSelf,
            mode: DestinationMode::Physical,
        }));
        assert!(!a.match_dest(&InterruptDestination {
            source_id: 254,
            dest_id: 0,
            shorthand: DestinationShorthand::AllExcludingSelf,
            mode: DestinationMode::Physical,
        }));
    }
    #[test]
    fn processor_priority() {
        let timer = Box::new(FakeTimer::new(Arc::new(Mutex::new(FakeClock::new()))));
        let mut a = Apic::new(0, timer);
        assert_eq!(a.get_processor_priority(), 0);
        a.set_reg(Reg::TPR, 0xF);
        let prio = a.get_processor_priority();
        assert!(
            prio == 0 || prio == 0xF,
            "Expected priority 0 or 0xF, got {}",
            prio
        );
        a.set_reg(Reg::TPR, 0x10);
        assert_eq!(a.get_processor_priority(), 0x10);
        a.set_reg(Reg::TPR, 0);
        assert_eq!(a.get_processor_priority(), 0);
        let timer = Box::new(FakeTimer::new(Arc::new(Mutex::new(FakeClock::new()))));
        let mut a = Apic::new(0, timer);
        a.set_vector_bit(VectorReg::Isr, 0xF);
        assert_eq!(a.get_processor_priority(), 0);
        a.set_vector_bit(VectorReg::Isr, 0x11);
        assert_eq!(a.get_processor_priority(), 0x10);
        a.clear_vector_bit(VectorReg::Isr, 0x11);
        assert_eq!(a.get_processor_priority(), 0);
        let timer = Box::new(FakeTimer::new(Arc::new(Mutex::new(FakeClock::new()))));
        let mut a = Apic::new(0, timer);
        a.set_vector_bit(VectorReg::Isr, 0x25);
        a.set_vector_bit(VectorReg::Isr, 0x11);
        a.set_reg(Reg::TPR, 0x31);
        assert_eq!(a.get_processor_priority(), 0x31);
        a.set_reg(Reg::TPR, 0x19);
        assert_eq!(a.get_processor_priority(), 0x20);
        a.clear_vector_bit(VectorReg::Isr, 0x25);
        let prio = a.get_processor_priority();
        assert!(
            prio == 0x10 || prio == 0x19,
            "Expected priority 0x10 or 0x19, got {}",
            prio
        );
    }
    #[test]
    fn accept_irq() {
        let timer = Box::new(FakeTimer::new(Arc::new(Mutex::new(FakeClock::new()))));
        let mut a = Apic::new(0, timer);
        assert_eq!(a.init, false);
        assert_eq!(a.sipi, None);
        assert_eq!(a.nmis, 0);
        a.accept_irq(&InterruptData {
            vector: 20,
            delivery: DeliveryMode::Fixed,
            trigger: TriggerMode::Level,
            level: Level::Assert,
        });
        a.accept_irq(&InterruptData {
            vector: 20,
            delivery: DeliveryMode::Fixed,
            trigger: TriggerMode::Level,
            level: Level::Assert,
        });
        a.accept_irq(&InterruptData {
            vector: 21,
            delivery: DeliveryMode::Fixed,
            trigger: TriggerMode::Edge,
            level: Level::Assert,
        });
        a.accept_irq(&InterruptData {
            vector: 255,
            delivery: DeliveryMode::Lowest,
            trigger: TriggerMode::Level,
            level: Level::Assert,
        });
        a.accept_irq(&InterruptData {
            vector: 0,
            delivery: DeliveryMode::Init,
            trigger: TriggerMode::Level,
            level: Level::Assert,
        });
        a.accept_irq(&InterruptData {
            vector: 7,
            delivery: DeliveryMode::Startup,
            trigger: TriggerMode::Edge,
            level: Level::Assert,
        });
        a.accept_irq(&InterruptData {
            vector: 8,
            delivery: DeliveryMode::Startup,
            trigger: TriggerMode::Edge,
            level: Level::Assert,
        });
        a.accept_irq(&InterruptData {
            vector: 0,
            delivery: DeliveryMode::NMI,
            trigger: TriggerMode::Edge,
            level: Level::Assert,
        });
        a.accept_irq(&InterruptData {
            vector: 0,
            delivery: DeliveryMode::NMI,
            trigger: TriggerMode::Edge,
            level: Level::Assert,
        });
        assert_eq!(a.init, true);
        assert_eq!(a.sipi, Some(8));
        assert_eq!(a.nmis, 2);
        assert_eq!(
            a.get_reg(Reg::IRR),
            0b0000_0000_0011_0000_0000_0000_0000_0000
        );
        assert_eq!(a.get_reg(Reg::IRR + 1 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::IRR + 2 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::IRR + 3 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::IRR + 4 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::IRR + 5 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::IRR + 6 * REG_ALIGN_BYTES), 0);
        assert_eq!(
            a.get_reg(Reg::IRR + 7 * REG_ALIGN_BYTES),
            0b1000_0000_0000_0000_0000_0000_0000_0000
        );
        assert_eq!(a.get_reg(Reg::ISR), 0);
        assert_eq!(a.get_reg(Reg::ISR + 1 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::ISR + 2 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::ISR + 3 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::ISR + 4 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::ISR + 5 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::ISR + 6 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::ISR + 7 * REG_ALIGN_BYTES), 0);
        assert_eq!(
            a.get_reg(Reg::TMR),
            0b0000_0000_0001_0000_0000_0000_0000_0000
        );
        assert_eq!(a.get_reg(Reg::TMR + 1 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::TMR + 2 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::TMR + 3 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::TMR + 4 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::TMR + 5 * REG_ALIGN_BYTES), 0);
        assert_eq!(a.get_reg(Reg::TMR + 6 * REG_ALIGN_BYTES), 0);
        assert_eq!(
            a.get_reg(Reg::TMR + 7 * REG_ALIGN_BYTES),
            0b1000_0000_0000_0000_0000_0000_0000_0000
        );
    }
    #[test]
    fn icr_write_sends_ipi() {
        let timer = Box::new(FakeTimer::new(Arc::new(Mutex::new(FakeClock::new()))));
        let mut a = Apic::new(229, timer);
        a.write(Reg::INTERRUPT_COMMAND_HI as u64, &[0, 0, 0, 42]);
        #[rustfmt::skip]
        let msg = a.write(
            Reg::INTERRUPT_COMMAND_LO as u64,
            &[
                123,        0b11001001, 0b00001100, 0,          ],
        );
        let msg = msg.unwrap();
        assert_eq!(
            msg,
            ApicBusMsg::Ipi(Interrupt {
                dest: InterruptDestination {
                    source_id: 229,
                    dest_id: 42,
                    shorthand: DestinationShorthand::AllExcludingSelf,
                    mode: DestinationMode::Logical,
                },
                data: InterruptData {
                    vector: 123,
                    delivery: DeliveryMode::Lowest,
                    trigger: TriggerMode::Level,
                    level: Level::Assert,
                },
            })
        );
        a.write(Reg::INTERRUPT_COMMAND_HI as u64, &[0, 0, 0, 161]);
        let msg = a.write(
            Reg::INTERRUPT_COMMAND_LO as u64,
            &[
                255,        0b00010110, 0b00000000, 0,          ],
        );
        let msg = msg.unwrap();
        assert_eq!(
            msg,
            ApicBusMsg::Ipi(Interrupt {
                dest: InterruptDestination {
                    source_id: 229,
                    dest_id: 161,
                    shorthand: DestinationShorthand::None,
                    mode: DestinationMode::Physical,
                },
                data: InterruptData {
                    vector: 255,
                    delivery: DeliveryMode::Startup,
                    trigger: TriggerMode::Edge,
                    level: Level::Deassert,
                },
            })
        );
    }
    #[test]
    fn end_of_interrupt() {
        let timer = Box::new(FakeTimer::new(Arc::new(Mutex::new(FakeClock::new()))));
        let mut a = Apic::new(0, timer);
        let msg = a.write(Reg::EOI as u64, &[0; 4]);
        assert_eq!(msg, None); a.set_vector_bit(VectorReg::Isr, 39);
        a.set_vector_bit(VectorReg::Isr, 255);
        let msg = a.write(Reg::EOI as u64, &[0; 4]).unwrap();
        assert_eq!(msg, ApicBusMsg::Eoi(255));
        assert_eq!(a.highest_bit_in_vector(VectorReg::Isr), Some(39));
        a.set_vector_bit(VectorReg::Isr, 40);
        let msg = a.write(Reg::EOI as u64, &[0; 4]).unwrap();
        assert_eq!(msg, ApicBusMsg::Eoi(40));
        assert_eq!(a.highest_bit_in_vector(VectorReg::Isr), Some(39));
        let msg = a.write(Reg::EOI as u64, &[0; 4]).unwrap();
        assert_eq!(msg, ApicBusMsg::Eoi(39));
        assert_eq!(a.highest_bit_in_vector(VectorReg::Isr), None);
        let msg = a.write(Reg::EOI as u64, &[0; 4]);
        assert_eq!(msg, None);
    }
    #[test]
    fn non_fixed_irqs_injected() {
        let timer = Box::new(FakeTimer::new(Arc::new(Mutex::new(FakeClock::new()))));
        let mut a = Apic::new(0, timer);
        a.set_enabled(true);
        a.accept_irq(&InterruptData {
            vector: 0,
            delivery: DeliveryMode::Init,
            trigger: TriggerMode::Level,
            level: Level::Assert,
        });
        a.accept_irq(&InterruptData {
            vector: 7,
            delivery: DeliveryMode::Startup,
            trigger: TriggerMode::Edge,
            level: Level::Assert,
        });
        a.accept_irq(&InterruptData {
            vector: 0,
            delivery: DeliveryMode::NMI,
            trigger: TriggerMode::Edge,
            level: Level::Assert,
        });
        a.accept_irq(&InterruptData {
            vector: 0,
            delivery: DeliveryMode::NMI,
            trigger: TriggerMode::Edge,
            level: Level::Assert,
        });
        let irqs = a.get_pending_irqs(false);
        assert_eq!(
            irqs,
            PendingInterrupts {
                fixed: None,
                nmis: 2,
                init: true,
                startup: Some(7),
                needs_window: false,
            }
        );
        assert_eq!(a.nmis, 0);
        assert_eq!(a.init, false);
        assert_eq!(a.sipi, None);
        a.accept_irq(&InterruptData {
            vector: 0,
            delivery: DeliveryMode::NMI,
            trigger: TriggerMode::Edge,
            level: Level::Assert,
        });
        let irqs = a.get_pending_irqs(true);
        assert_eq!(
            irqs,
            PendingInterrupts {
                nmis: 1,
                ..Default::default()
            }
        );
        assert_eq!(a.nmis, 0);
        let irqs = a.get_pending_irqs(true);
        assert_eq!(
            irqs,
            PendingInterrupts {
                ..Default::default()
            }
        );
    }
    #[test]
    fn fixed_irq_injected() {
        let timer = Box::new(FakeTimer::new(Arc::new(Mutex::new(FakeClock::new()))));
        let mut a = Apic::new(0, timer);
        a.set_enabled(true);
        a.accept_irq(&InterruptData {
            vector: 0x10,
            delivery: DeliveryMode::Fixed,
            trigger: TriggerMode::Level,
            level: Level::Assert,
        });
        let irqs = a.get_pending_irqs(false);
        assert_eq!(
            irqs,
            PendingInterrupts {
                fixed: None,
                needs_window: true,
                ..Default::default()
            }
        );
        assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), Some(0x10));
        assert_eq!(a.highest_bit_in_vector(VectorReg::Isr), None);
        let irqs = a.get_pending_irqs(true);
        assert_eq!(
            irqs,
            PendingInterrupts {
                fixed: Some(0x10),
                needs_window: false,
                ..Default::default()
            }
        );
        assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), None);
        assert_eq!(a.highest_bit_in_vector(VectorReg::Isr), Some(0x10));
    }
    #[test]
    fn high_priority_irq_injected() {
        let timer = Box::new(FakeTimer::new(Arc::new(Mutex::new(FakeClock::new()))));
        let mut a = Apic::new(0, timer);
        a.set_enabled(true);
        a.accept_irq(&InterruptData {
            vector: 0x10,
            delivery: DeliveryMode::Fixed,
            trigger: TriggerMode::Level,
            level: Level::Assert,
        });
        let _ = a.get_pending_irqs(true);
        a.accept_irq(&InterruptData {
            vector: 0x20,
            delivery: DeliveryMode::Fixed,
            trigger: TriggerMode::Level,
            level: Level::Assert,
        });
        let irqs = a.get_pending_irqs(false);
        assert_eq!(
            irqs,
            PendingInterrupts {
                fixed: None,
                needs_window: true,
                ..Default::default()
            }
        );
        assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), Some(0x20));
        assert_eq!(a.highest_bit_in_vector(VectorReg::Isr), Some(0x10));
        let irqs = a.get_pending_irqs(true);
        assert_eq!(
            irqs,
            PendingInterrupts {
                fixed: Some(0x20),
                needs_window: false,
                ..Default::default()
            }
        );
        assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), None);
        assert_eq!(a.highest_bit_in_vector(VectorReg::Isr), Some(0x20));
    }
    #[test]
    fn low_priority_irq_deferred() {
        let timer = Box::new(FakeTimer::new(Arc::new(Mutex::new(FakeClock::new()))));
        let mut a = Apic::new(0, timer);
        a.set_enabled(true);
        a.accept_irq(&InterruptData {
            vector: 0x10,
            delivery: DeliveryMode::Fixed,
            trigger: TriggerMode::Level,
            level: Level::Assert,
        });
        let _ = a.get_pending_irqs(true);
        a.accept_irq(&InterruptData {
            vector: 0x15,
            delivery: DeliveryMode::Fixed,
            trigger: TriggerMode::Level,
            level: Level::Assert,
        });
        let irqs = a.get_pending_irqs(true);
        assert_eq!(
            irqs,
            PendingInterrupts {
                fixed: None,
                needs_window: false,
                ..Default::default()
            }
        );
        assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), Some(0x15));
        assert_eq!(a.highest_bit_in_vector(VectorReg::Isr), Some(0x10));
        let msg = a.write(Reg::EOI as u64, &[0; 4]).unwrap();
        assert_eq!(msg, ApicBusMsg::Eoi(0x10));
        let irqs = a.get_pending_irqs(true);
        assert_eq!(
            irqs,
            PendingInterrupts {
                fixed: Some(0x15),
                needs_window: false,
                ..Default::default()
            }
        );
    }
    #[test]
    fn tpr_defers_injection() {
        let timer = Box::new(FakeTimer::new(Arc::new(Mutex::new(FakeClock::new()))));
        let mut a = Apic::new(0, timer);
        a.set_enabled(true);
        a.accept_irq(&InterruptData {
            vector: 0x25,
            delivery: DeliveryMode::Fixed,
            trigger: TriggerMode::Level,
            level: Level::Assert,
        });
        a.set_reg(Reg::TPR, 0x20);
        let irqs = a.get_pending_irqs(true);
        assert_eq!(
            irqs,
            PendingInterrupts {
                fixed: None,
                needs_window: false,
                ..Default::default()
            }
        );
        a.set_reg(Reg::TPR, 0x19);
        let irqs = a.get_pending_irqs(true);
        assert_eq!(
            irqs,
            PendingInterrupts {
                fixed: Some(0x25),
                needs_window: false,
                ..Default::default()
            }
        );
    }
    #[test]
    fn timer_starts() {
        let clock = Arc::new(Mutex::new(FakeClock::new()));
        let mut a = Apic::new(0, Box::new(FakeTimer::new(clock.clone())));
        a.set_enabled(true);
        a.write(Reg::LOCAL_TIMER as u64, &TIMER_MODE_ONE_SHOT.to_le_bytes());
        a.write(Reg::TIMER_DIVIDE_CONTROL as u64, &[1, 0, 0, 0]); a.write(Reg::TIMER_INITIAL_COUNT as u64, &500_000_u32.to_le_bytes());
        let timer_ns = u64::try_from(4 * 500_000 * a.get_cycle_length().as_nanos()).unwrap();
        clock.lock().add_ns(timer_ns - 1);
        assert_eq!(a.timer.mark_waited(), Ok(true));
        clock.lock().add_ns(1);
        assert_eq!(a.timer.mark_waited(), Ok(false));
        clock.lock().add_ns(timer_ns);
        assert_eq!(a.timer.mark_waited(), Ok(true));
        a.write(Reg::TIMER_DIVIDE_CONTROL as u64, &[0b1011, 0, 0, 0]); a.write(Reg::LOCAL_TIMER as u64, &TIMER_MODE_PERIODIC.to_le_bytes());
        a.write(
            Reg::TIMER_INITIAL_COUNT as u64,
            &1_000_000_u32.to_le_bytes(),
        );
        let timer_ns = u64::try_from(1 * 1_000_000 * a.get_cycle_length().as_nanos()).unwrap();
        clock.lock().add_ns(timer_ns - 1);
        assert_eq!(a.timer.mark_waited(), Ok(true));
        clock.lock().add_ns(1);
        assert_eq!(a.timer.mark_waited(), Ok(false));
        clock.lock().add_ns(timer_ns - 1);
        assert_eq!(a.timer.mark_waited(), Ok(true));
        clock.lock().add_ns(1);
        assert_eq!(a.timer.mark_waited(), Ok(false));
    }
    #[test]
    fn timer_interrupts() {
        let clock = Arc::new(Mutex::new(FakeClock::new()));
        let mut a = Apic::new(0, Box::new(FakeTimer::new(clock.clone())));
        a.set_enabled(true);
        let val = TIMER_MODE_PERIODIC | LOCAL_VECTOR_MASKED | 123;
        a.write(Reg::LOCAL_TIMER as u64, &val.to_le_bytes());
        a.write(Reg::TIMER_DIVIDE_CONTROL as u64, &[0b1011, 0, 0, 0]); a.write(Reg::TIMER_INITIAL_COUNT as u64, &500_000_u32.to_le_bytes());
        clock
            .lock()
            .add_ns(500_000 * a.get_cycle_length().as_nanos() as u64);
        a.handle_timer_expiration();
        assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), None);
        let val = TIMER_MODE_PERIODIC | 123;
        a.write(Reg::LOCAL_TIMER as u64, &val.to_le_bytes());
        clock
            .lock()
            .add_ns(500_000 * a.get_cycle_length().as_nanos() as u64);
        a.handle_timer_expiration();
        assert_eq!(a.highest_bit_in_vector(VectorReg::Irr), Some(123));
    }
}