xref: /cloud-hypervisor/arch/src/x86_64/interrupts.rs (revision 7d7bfb2034001d4cb15df2ddc56d2d350c8da30f)
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