// 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::convert::TryInto; use std::fmt::{self, Display}; use std::mem; use std::result; use kvm; use kvm_sys::kvm_lapic_state; use sys_util; #[derive(Debug)] pub enum Error { GetLapic(sys_util::Error), SetLapic(sys_util::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. const APIC_LVT0: usize = 0x350; const APIC_LVT1: usize = 0x360; const APIC_MODE_NMI: u32 = 0x4; const APIC_MODE_EXTINT: u32 = 0x7; fn get_klapic_reg(klapic: &kvm_lapic_state, reg_offset: usize) -> u32 { let sliceu8 = unsafe { // This array is only accessed as parts of a u32 word, so interpret it as a u8 array. // from_le_bytes() only works on arrays of u8, not i8(c_char). mem::transmute::<&[i8], &[u8]>(&klapic.regs[reg_offset..reg_offset + 4]) }; // Slice conversion to array can't fail if the offsets defined above are correct. u32::from_le_bytes(sliceu8.try_into().unwrap()) } fn set_klapic_reg(klapic: &mut kvm_lapic_state, reg_offset: usize, value: u32) { let sliceu8 = unsafe { // This array is only accessed as parts of a u32 word, so interpret it as a u8 array. // to_le_bytes() produces an array of u8, not i8(c_char). mem::transmute::<&mut [i8], &mut [u8]>(&mut klapic.regs[reg_offset..reg_offset + 4]) }; sliceu8.copy_from_slice(&value.to_le_bytes()); } 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` - The VCPU object to configure. pub fn set_lint(vcpu: &kvm::Vcpu) -> Result<()> { let mut klapic = vcpu.get_lapic().map_err(Error::GetLapic)?; let lvt_lint0 = get_klapic_reg(&klapic, APIC_LVT0); set_klapic_reg( &mut klapic, APIC_LVT0, set_apic_delivery_mode(lvt_lint0, APIC_MODE_EXTINT), ); let lvt_lint1 = get_klapic_reg(&klapic, APIC_LVT1); set_klapic_reg( &mut klapic, APIC_LVT1, set_apic_delivery_mode(lvt_lint1, APIC_MODE_NMI), ); vcpu.set_lapic(&klapic).map_err(Error::SetLapic) }