summary refs log tree commit diff
path: root/x86_64/src/interrupts.rs
blob: a2b0f1c0f978372a04f2b683ba97a924dcebd464 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// 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)
}