1 // Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 // 4 // Portions Copyright 2017 The Chromium OS Authors. All rights reserved. 5 // Use of this source code is governed by a BSD-style license that can be 6 // found in the LICENSE-BSD-3-Clause file. 7 8 use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; 9 use hypervisor::x86_64::LapicState; 10 use std::io::Cursor; 11 use std::mem; 12 use std::result; 13 use std::sync::Arc; 14 15 pub type Result<T> = result::Result<T, hypervisor::HypervisorCpuError>; 16 17 // Defines poached from apicdef.h kernel header. 18 pub const APIC_LVT0: usize = 0x350; 19 pub const APIC_LVT1: usize = 0x360; 20 pub const APIC_MODE_NMI: u32 = 0x4; 21 pub const APIC_MODE_EXTINT: u32 = 0x7; 22 23 pub fn get_klapic_reg(klapic: &LapicState, reg_offset: usize) -> u32 { 24 let sliceu8 = unsafe { 25 // This array is only accessed as parts of a u32 word, so interpret it as a u8 array. 26 // Cursors are only readable on arrays of u8, not i8(c_char). 27 mem::transmute::<&[i8], &[u8]>(&klapic.regs[reg_offset..]) 28 }; 29 let mut reader = Cursor::new(sliceu8); 30 // Following call can't fail if the offsets defined above are correct. 31 reader 32 .read_u32::<LittleEndian>() 33 .expect("Failed to read klapic register") 34 } 35 36 pub fn set_klapic_reg(klapic: &mut LapicState, reg_offset: usize, value: u32) { 37 let sliceu8 = unsafe { 38 // This array is only accessed as parts of a u32 word, so interpret it as a u8 array. 39 // Cursors are only readable on arrays of u8, not i8(c_char). 40 mem::transmute::<&mut [i8], &mut [u8]>(&mut klapic.regs[reg_offset..]) 41 }; 42 let mut writer = Cursor::new(sliceu8); 43 // Following call can't fail if the offsets defined above are correct. 44 writer 45 .write_u32::<LittleEndian>(value) 46 .expect("Failed to write klapic register") 47 } 48 49 pub fn set_apic_delivery_mode(reg: u32, mode: u32) -> u32 { 50 ((reg) & !0x700) | ((mode) << 8) 51 } 52 53 /// Configures LAPICs. LAPIC0 is set for external interrupts, LAPIC1 is set for NMI. 54 /// 55 /// # Arguments 56 /// * `vcpu` - The VCPU object to configure. 57 pub fn set_lint(vcpu: &Arc<dyn hypervisor::Vcpu>) -> Result<()> { 58 let mut klapic = vcpu.get_lapic()?; 59 60 let lvt_lint0 = get_klapic_reg(&klapic, APIC_LVT0); 61 set_klapic_reg( 62 &mut klapic, 63 APIC_LVT0, 64 set_apic_delivery_mode(lvt_lint0, APIC_MODE_EXTINT), 65 ); 66 let lvt_lint1 = get_klapic_reg(&klapic, APIC_LVT1); 67 set_klapic_reg( 68 &mut klapic, 69 APIC_LVT1, 70 set_apic_delivery_mode(lvt_lint1, APIC_MODE_NMI), 71 ); 72 73 vcpu.set_lapic(&klapic) 74 } 75 76 #[cfg(test)] 77 mod tests { 78 use super::*; 79 80 const KVM_APIC_REG_SIZE: usize = 0x400; 81 82 #[test] 83 fn test_set_and_get_klapic_reg() { 84 let reg_offset = 0x340; 85 let mut klapic = LapicState::default(); 86 set_klapic_reg(&mut klapic, reg_offset, 3); 87 let value = get_klapic_reg(&klapic, reg_offset); 88 assert_eq!(value, 3); 89 } 90 91 #[test] 92 #[should_panic] 93 fn test_set_and_get_klapic_out_of_bounds() { 94 let reg_offset = KVM_APIC_REG_SIZE + 10; 95 let mut klapic = LapicState::default(); 96 set_klapic_reg(&mut klapic, reg_offset, 3); 97 } 98 } 99