| // 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<T> = result::Result<T, Error>; |
| |
| 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) |
| } |