// Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use std::fmt::{self, Display}; use std::result; use devices::IrqChipX86_64; #[derive(Debug)] pub enum Error { GetLapic(base::Error), SetLapic(base::Error), } pub type Result = result::Result; impl std::error::Error for Error {} impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use self::Error::*; match self { GetLapic(e) => write!(f, "GetLapic ioctl failed: {}", e), SetLapic(e) => write!(f, "SetLapic ioctl failed: {}", e), } } } // Defines poached from apicdef.h kernel header. // Offset, in bytes, of LAPIC local vector table LINT0/LINT1 registers. const APIC_LVT0_OFFSET: usize = 0x350; const APIC_LVT1_OFFSET: usize = 0x360; // Register num of LINT0/LINT1 register. const APIC_LVT0_REGISTER: usize = lapic_byte_offset_to_register(APIC_LVT0_OFFSET); const APIC_LVT1_REGISTER: usize = lapic_byte_offset_to_register(APIC_LVT1_OFFSET); const APIC_MODE_NMI: u32 = 0x4; const APIC_MODE_EXTINT: u32 = 0x7; // Converts a LAPIC register byte offset to a register number. const fn lapic_byte_offset_to_register(offset_bytes: usize) -> usize { // Registers are 16 byte aligned offset_bytes / 16 } fn set_apic_delivery_mode(reg: u32, mode: u32) -> u32 { ((reg) & !0x700) | ((mode) << 8) } /// Configures LAPICs. LAPIC0 is set for external interrupts, LAPIC1 is set for NMI. /// /// # Arguments /// * `vcpu_id` - The number of the VCPU to configure. /// * `irqchip` - The IrqChip for getting/setting LAPIC state. pub fn set_lint(vcpu_id: usize, irqchip: &mut dyn IrqChipX86_64) -> Result<()> { let mut lapic = irqchip.get_lapic_state(vcpu_id).map_err(Error::GetLapic)?; for (reg, mode) in &[ (APIC_LVT0_REGISTER, APIC_MODE_EXTINT), (APIC_LVT1_REGISTER, APIC_MODE_NMI), ] { lapic.regs[*reg] = set_apic_delivery_mode(lapic.regs[*reg], *mode); } irqchip .set_lapic_state(vcpu_id, &lapic) .map_err(Error::SetLapic) }