Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 11 additions & 2 deletions src/arch/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,26 @@ tdx = [ "tee", "dep:tdx" ]

[dependencies]
libc = ">=0.2.39"
vm-memory = { version = "0.17", features = ["backend-mmap"] }
vm-memory = { version = "0.17", default-features = false, features = ["backend-mmap"] }
vmm-sys-util = "0.14"

arch_gen = { package = "krun-arch-gen", version = "=0.1.0-1.18.0", path = "../arch_gen" }
smbios = { package = "krun-smbios", version = "=0.1.0-1.18.0", path = "../smbios" }
utils = { package = "krun-utils", version = "=0.1.0-1.18.0", path = "../utils" }

[target.'cfg(not(target_os = "windows"))'.dependencies]
smbios = { package = "krun-smbios", version = "=0.1.0-1.18.0", path = "../smbios" }

[target.'cfg(target_os = "linux")'.dependencies]
kvm-bindings = { version = "0.12", features = ["fam-wrappers"] }
kvm-ioctls = "0.22"
tdx = { version = "0.1.0", optional = true }

[target.'cfg(target_os = "windows")'.dependencies]
whp = { package = "krun-whp", version = "=0.1.0-1.18.0", path = "../whp" }
windows-sys = { version = "0.61.2", features = [
"Win32_Foundation",
"Win32_System_Hypervisor",
] }

[dev-dependencies]
utils = { package = "krun-utils", version = "=0.1.0-1.18.0", path = "../utils" }
28 changes: 22 additions & 6 deletions src/arch/src/x86_64/gdt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,24 @@

// For GDT details see arch/x86/include/asm/segment.h

use kvm_bindings::kvm_segment;
/// Hypervisor-agnostic segment descriptor with the same field semantics as
/// `kvm_segment` -> https://github.com/rust-vmm/kvm/blob/main/kvm-bindings/src/x86_64/bindings.rs#L1190
/// Platform-specific code converts this to the native type.
#[derive(Debug, Clone, Copy, Default)]
pub struct SegmentDescriptor {
pub base: u64,
pub limit: u32,
pub selector: u16,
pub type_: u8,
pub present: u8,
pub dpl: u8,
pub db: u8,
pub s: u8,
pub l: u8,
pub g: u8,
pub avl: u8,
pub unusable: u8,
}

/// Constructor for a conventional segment GDT (or LDT) entry. Derived from the kernel's segment.h.
pub fn gdt_entry(flags: u16, base: u32, limit: u32) -> u64 {
Expand Down Expand Up @@ -60,14 +77,14 @@ fn get_type(entry: u64) -> u8 {
((entry & 0x0000_0F00_0000_0000) >> 40) as u8
}

/// Automatically build the kvm struct for SET_SREGS from the kernel bit fields.
/// Automatically builds a hypervisor-agnostic `SegmentDescriptor` from a raw GDT entry.
///
/// # Arguments
///
/// * `entry` - The gdt entry.
/// * `table_index` - Index of the entry in the gdt table.
pub fn kvm_segment_from_gdt(entry: u64, table_index: u8) -> kvm_segment {
kvm_segment {
pub fn segment_from_gdt(entry: u64, table_index: u8) -> SegmentDescriptor {
SegmentDescriptor {
base: get_base(entry),
limit: get_limit(entry),
selector: u16::from(table_index * 8),
Expand All @@ -79,7 +96,6 @@ pub fn kvm_segment_from_gdt(entry: u64, table_index: u8) -> kvm_segment {
l: get_l(entry),
g: get_g(entry),
avl: get_avl(entry),
padding: 0,
unusable: match get_p(entry) {
0 => 1,
_ => 0,
Expand All @@ -94,7 +110,7 @@ mod tests {
#[test]
fn field_parse() {
let gdt = gdt_entry(0xA09B, 0x10_0000, 0xfffff);
let seg = kvm_segment_from_gdt(gdt, 0);
let seg = segment_from_gdt(gdt, 0);
// 0xA09B
// 'A'
assert_eq!(0x1, seg.g);
Expand Down
139 changes: 2 additions & 137 deletions src/arch/src/x86_64/interrupts.rs
Original file line number Diff line number Diff line change
@@ -1,137 +1,2 @@
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Portions 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 THIRD-PARTY file.

use kvm_bindings::kvm_lapic_state;
use kvm_ioctls::VcpuFd;
use utils::byte_order;
/// Errors thrown while configuring the LAPIC.
#[derive(Debug)]
pub enum Error {
/// Failure in retrieving the LAPIC configuration.
GetLapic(kvm_ioctls::Error),
/// Failure in modifying the LAPIC configuration.
SetLapic(kvm_ioctls::Error),
}
type Result<T> = std::result::Result<T, Error>;

// 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 range = reg_offset..reg_offset + 4;
let reg = klapic.regs.get(range).expect("get_klapic_reg range");
byte_order::read_le_i32(reg) as u32
}

fn set_klapic_reg(klapic: &mut kvm_lapic_state, reg_offset: usize, value: u32) {
let range = reg_offset..reg_offset + 4;
let reg = klapic.regs.get_mut(range).expect("set_klapic_reg range");
byte_order::write_le_i32(&mut *reg, value as i32)
}

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: &VcpuFd) -> 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)
}

#[cfg(test)]
mod tests {
extern crate utils;

use super::*;
use kvm_ioctls::Kvm;

const KVM_APIC_REG_SIZE: usize = 0x400;

#[test]
fn test_set_and_get_klapic_reg() {
let reg_offset = 0x340;
let mut klapic = kvm_lapic_state::default();
set_klapic_reg(&mut klapic, reg_offset, 3);
let value = get_klapic_reg(&klapic, reg_offset);
assert_eq!(value, 3);
}

#[test]
#[should_panic]
fn test_set_and_get_klapic_out_of_bounds() {
let reg_offset = KVM_APIC_REG_SIZE + 10;
let mut klapic = kvm_lapic_state::default();
set_klapic_reg(&mut klapic, reg_offset, 3);
}

#[test]
fn test_apic_delivery_mode() {
let mut v: Vec<u32> = (0..20).map(|_| utils::rand::xor_rng_u32()).collect();

v.iter_mut()
.for_each(|x| *x = set_apic_delivery_mode(*x, 2));
let after: Vec<u32> = v.iter().map(|x| ((*x & !0x700) | ((2) << 8))).collect();
assert_eq!(v, after);
}

#[test]
fn test_setlint() {
let kvm = Kvm::new().unwrap();
assert!(kvm.check_extension(kvm_ioctls::Cap::Irqchip));
let vm = kvm.create_vm().unwrap();
//the get_lapic ioctl will fail if there is no irqchip created beforehand.
assert!(vm.create_irq_chip().is_ok());
let vcpu = vm.create_vcpu(0).unwrap();
let klapic_before: kvm_lapic_state = vcpu.get_lapic().unwrap();

// Compute the value that is expected to represent LVT0 and LVT1.
let lint0 = get_klapic_reg(&klapic_before, APIC_LVT0);
let lint1 = get_klapic_reg(&klapic_before, APIC_LVT1);
let lint0_mode_expected = set_apic_delivery_mode(lint0, APIC_MODE_EXTINT);
let lint1_mode_expected = set_apic_delivery_mode(lint1, APIC_MODE_NMI);

set_lint(&vcpu).unwrap();

// Compute the value that represents LVT0 and LVT1 after set_lint.
let klapic_actual: kvm_lapic_state = vcpu.get_lapic().unwrap();
let lint0_mode_actual = get_klapic_reg(&klapic_actual, APIC_LVT0);
let lint1_mode_actual = get_klapic_reg(&klapic_actual, APIC_LVT1);
assert_eq!(lint0_mode_expected, lint0_mode_actual);
assert_eq!(lint1_mode_expected, lint1_mode_actual);
}

#[test]
fn test_setlint_fails() {
let kvm = Kvm::new().unwrap();
let vm = kvm.create_vm().unwrap();
let vcpu = vm.create_vcpu(0).unwrap();
// 'get_lapic' ioctl triggered by the 'set_lint' function will fail if there is no
// irqchip created beforehand.
assert!(set_lint(&vcpu).is_err());
}
}
#[cfg(target_os = "linux")]
pub use super::linux::interrupts::*;
6 changes: 6 additions & 0 deletions src/arch/src/x86_64/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ pub const RESET_VECTOR: u64 = 0xfff0;
pub const RESET_VECTOR: u64 = 0xffff_fff0;
pub const RESET_VECTOR_SEV_AP: u64 = 0xfff3;

/// Address of the AP idle trampoline (`cli; hlt; jmp` loop) used during
/// SMP boot on WHP. APs park here in real mode until the BSP sends
/// INIT + SIPI through the emulated local APIC.
#[cfg(target_os = "windows")]
pub const AP_TRAMPOLINE_START: u64 = 0x1000;

/// The address to load the firmware, if present.
pub const FIRMWARE_START: u64 = 0xffff_0000;

Expand Down
137 changes: 137 additions & 0 deletions src/arch/src/x86_64/linux/interrupts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Portions 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 THIRD-PARTY file.

use kvm_bindings::kvm_lapic_state;
use kvm_ioctls::VcpuFd;
use utils::byte_order;
/// Errors thrown while configuring the LAPIC.
#[derive(Debug)]
pub enum Error {
/// Failure in retrieving the LAPIC configuration.
GetLapic(kvm_ioctls::Error),
/// Failure in modifying the LAPIC configuration.
SetLapic(kvm_ioctls::Error),
}
type Result<T> = std::result::Result<T, Error>;

// 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 range = reg_offset..reg_offset + 4;
let reg = klapic.regs.get(range).expect("get_klapic_reg range");
byte_order::read_le_i32(reg) as u32
}

fn set_klapic_reg(klapic: &mut kvm_lapic_state, reg_offset: usize, value: u32) {
let range = reg_offset..reg_offset + 4;
let reg = klapic.regs.get_mut(range).expect("set_klapic_reg range");
byte_order::write_le_i32(&mut *reg, value as i32)
}

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: &VcpuFd) -> 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)
}

#[cfg(test)]
mod tests {
extern crate utils;

use super::*;
use kvm_ioctls::Kvm;

const KVM_APIC_REG_SIZE: usize = 0x400;

#[test]
fn test_set_and_get_klapic_reg() {
let reg_offset = 0x340;
let mut klapic = kvm_lapic_state::default();
set_klapic_reg(&mut klapic, reg_offset, 3);
let value = get_klapic_reg(&klapic, reg_offset);
assert_eq!(value, 3);
}

#[test]
#[should_panic]
fn test_set_and_get_klapic_out_of_bounds() {
let reg_offset = KVM_APIC_REG_SIZE + 10;
let mut klapic = kvm_lapic_state::default();
set_klapic_reg(&mut klapic, reg_offset, 3);
}

#[test]
fn test_apic_delivery_mode() {
let mut v: Vec<u32> = (0..20).map(|_| utils::rand::xor_rng_u32()).collect();

v.iter_mut()
.for_each(|x| *x = set_apic_delivery_mode(*x, 2));
let after: Vec<u32> = v.iter().map(|x| ((*x & !0x700) | ((2) << 8))).collect();
assert_eq!(v, after);
}

#[test]
fn test_setlint() {
let kvm = Kvm::new().unwrap();
assert!(kvm.check_extension(kvm_ioctls::Cap::Irqchip));
let vm = kvm.create_vm().unwrap();
//the get_lapic ioctl will fail if there is no irqchip created beforehand.
assert!(vm.create_irq_chip().is_ok());
let vcpu = vm.create_vcpu(0).unwrap();
let klapic_before: kvm_lapic_state = vcpu.get_lapic().unwrap();

// Compute the value that is expected to represent LVT0 and LVT1.
let lint0 = get_klapic_reg(&klapic_before, APIC_LVT0);
let lint1 = get_klapic_reg(&klapic_before, APIC_LVT1);
let lint0_mode_expected = set_apic_delivery_mode(lint0, APIC_MODE_EXTINT);
let lint1_mode_expected = set_apic_delivery_mode(lint1, APIC_MODE_NMI);

set_lint(&vcpu).unwrap();

// Compute the value that represents LVT0 and LVT1 after set_lint.
let klapic_actual: kvm_lapic_state = vcpu.get_lapic().unwrap();
let lint0_mode_actual = get_klapic_reg(&klapic_actual, APIC_LVT0);
let lint1_mode_actual = get_klapic_reg(&klapic_actual, APIC_LVT1);
assert_eq!(lint0_mode_expected, lint0_mode_actual);
assert_eq!(lint1_mode_expected, lint1_mode_actual);
}

#[test]
fn test_setlint_fails() {
let kvm = Kvm::new().unwrap();
let vm = kvm.create_vm().unwrap();
let vcpu = vm.create_vcpu(0).unwrap();
// 'get_lapic' ioctl triggered by the 'set_lint' function will fail if there is no
// irqchip created beforehand.
assert!(set_lint(&vcpu).is_err());
}
}
3 changes: 3 additions & 0 deletions src/arch/src/x86_64/linux/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod interrupts;
pub mod msr;
pub mod regs;
Loading
Loading