Skip to content

Implement CPUID handler #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jul 20, 2021
10 changes: 7 additions & 3 deletions .cargo/config
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
[build]
target = "x86_64-unknown-uefi"

[target.x86_64-unknown-uefi]
rustflags = ["-Clink-args= /subsystem:EFI_RUNTIME_DRIVER"]

[unstable]
build-std = ["core", "compiler_builtins"]
build-std-features = ["compiler-builtins-mem"]

[target.x86_64-unknown-uefi.uefi]
rustflags = "-Clink-args=' /subsystem:EFI_RUNTIME_DRIVER'"

#[target.x86_64-unknown-uefi.rustyvctl]
#rustflags = ["-Clink-args= /subsystem:EFI_APPLICATION"]

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

members = [
"hypervisor",
"hypervisor_abi",
"uefi",
"pcuart",
"linux",
"dmesg_logger",
"rustyvctl",
]
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ rustup target install x86_64-unknown-uefi
Once you have the right version of rust installed, building is straightforward:

```
cargo build --target x86_64-unknown-uefi
sh build.sh
```

## Launching from a UEFI shell

First, build the project, and copy the hypervisor from
`target/x86_64-unknown-uefi/debug/uefi.efi` onto a USB stick.
`target/x86_64-unknown-uefi/debug/rustyvisor.efi` onto a USB stick.


Unmount the USB stick from your development device and insert it into your test
Expand Down Expand Up @@ -75,11 +75,11 @@ Press ESC in 1 seconds to skip startup.nsh or any other key to continue.
Shell> fs0:
FS0:\> dir
Directory of: FS0:\
06/03/2021 23:33 342,016 uefi.efi
06/03/2021 23:33 342,016 rustyvisor.efi
06/03/2021 23:42 10,383 NvVars
2 File(s) 433,807 bytes
0 Dir(s)
FS0:\> load .\uefi.efi
FS0:\> load .\rustyvisor.efi
FS0:\>
```

Expand Down
32 changes: 32 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/bin/sh

set -x
set -e

if [ "$1" = "clean" ] ; then
cd uefi || exit 255
cargo clean
cd ..

cd rustyvctl || exit 255
cargo clean
cd ..

cd linux || exit 255
make clean
cd ..

exit 0
fi

cd uefi || exit 255
cargo build
cd ..

cd rustyvctl || exit 255
cargo build
cd ..

cd linux || exit 255
make
cd ..
9 changes: 9 additions & 0 deletions dmesg_logger/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
#![no_std]
#![warn(missing_docs)]

//! A logging facade for writing logging information to the Linux Kernel System
//! Log.

use log;

use core::fmt;
Expand All @@ -8,6 +13,7 @@ extern "C" {
fn printk(fmt: *const u8, ...) -> i32;
}

/// A logger which writes to the Linux Kernel System Log.
pub struct DMesgLogger {}

struct PrintK {}
Expand All @@ -31,6 +37,9 @@ impl fmt::Write for PrintK {
}

impl DMesgLogger {
/// A function similar to core::fmt::Write, except that self is not
/// mutable, so we can use it without doing any locking. We let Linux
/// handle the locking for us.
pub fn write_fmt(&self, args: core::fmt::Arguments) {
let mut printk_obj = PrintK {};
let _ = write!(printk_obj, "{}\r\n", args);
Expand Down
1 change: 1 addition & 0 deletions hypervisor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ edition = "2018"
spin = { version = "0.9" }
log = { default-features = false, version = "0.4" }
pcuart = { path= "../pcuart"}
hypervisor_abi = { path= "../hypervisor_abi"}
dmesg_logger = { path = "../dmesg_logger" }
#x86 = "0.39.0"
x86 = { git = "https://github.com/iankronquist/rust-x86" }
Expand Down
48 changes: 48 additions & 0 deletions hypervisor/src/hypercall_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use crate::register_state::GeneralPurposeRegisterState;

const HYPERVISOR_VERSION: &str = env!("CARGO_PKG_VERSION");

fn parse_version(version_string: &str) -> [u32; 3] {
let mut index = 0;
let mut version: [u32; 3] = [0, 0, 0];
for c in version_string.chars() {
if index > version.len() {
break;
}

if let Some(digit) = c.to_digit(10) {
version[index] *= 10;
version[index] += digit;
} else {
index += 1;
continue;
}
}

version
}

/// Handle a hypercall.
/// Expects gprs.rax to hold hypercall::HYPERCALL_MAGIC and gprs.rcx to hold a
/// valid hypercall reason.
pub fn handle_hypercall(gprs: &mut GeneralPurposeRegisterState) -> Result<(), x86::vmx::VmFail> {
assert_eq!(hypervisor_abi::HYPERCALL_MAGIC, gprs.rax as u32);

let reason = gprs.rcx as u32;
match reason {
hypervisor_abi::HYPERCALL_REASON_VERSION => {
let version = parse_version(&HYPERVISOR_VERSION);
gprs.rax = u64::from(version[0]);
gprs.rbx = u64::from(version[1]);
gprs.rcx = u64::from(version[2]);
gprs.rdx = 0; // Reserved 0
}
_ => {
gprs.rax = 0;
gprs.rbx = 0;
gprs.rcx = 0;
gprs.rdx = 0;
}
}
Ok(())
}
23 changes: 23 additions & 0 deletions hypervisor/src/interrupt_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ use crate::{

const VM_PREEMPTION_TIMER_VALUE: u64 = 0xffff;
const INTERRUPT_COUNT: usize = 256;

/// A Virtualized Interrupt Controller
/// Caches various interrupt information.
/// Should be initialized as all zeroes.
/// Each core should have their own VirtualLocalInterruptController.
pub struct VirtualLocalInterruptController {
total_interrupts_count: usize,
delayed_delivery_interrupts: [usize; INTERRUPT_COUNT],
Expand Down Expand Up @@ -118,6 +123,10 @@ fn vmx_deconfigure_interrupt_window_exiting() -> Result<(), x86::vmx::VmFail> {
vmwrite(VmcsField::CpuBasedVmExecControl, cpu_based_controls)
}

/// Call this function when an external interrupt is received.
/// If the guest is interruptable, inject the interrupt into the guest.
/// Otherwise, cache it for later delivery and configure the guest to wake up
/// later for interrupt delivery.
pub fn received_external_interrupt() -> Result<(), x86::vmx::VmFail> {
let interrupt_info = vmread(VmcsField::VmExitIntrInfo)?;
let interrupt_number = interrupt_info & 0xff;
Expand All @@ -134,6 +143,17 @@ pub fn received_external_interrupt() -> Result<(), x86::vmx::VmFail> {
}
}

/// Call when the guest preemption timer fires.
/// If the guest does not support interrupt window exiting, and there is an
/// outstanding interrupt cached, and the guest is interruptable,
/// an interrupt will be injected into the guest.
///
/// If the interrupt controller requested the timer be enabled and the timer is
/// not being used elsewhere in the hypervisor, and there are no outstanding
/// interrupts, the timer will be disabled to allow the guest to run unimpeded.
///
/// This function only does work if the guest does not support interrupt window
/// exiting.
pub fn received_preemption_timer() -> Result<(), x86::vmx::VmFail> {
let local_interrupt_controller = get_local_interrupt_controller();
if local_interrupt_controller.requested_poll_of_interrupts_on_next_preemption_timer
Expand All @@ -157,6 +177,9 @@ pub fn received_preemption_timer() -> Result<(), x86::vmx::VmFail> {
Ok(())
}

/// If interrupt window exiting is supported by the guest, then this function
/// should be called when an interrupt window exit occurs. This function will
/// then inject any outstanding interrupts into the guest.
pub fn received_interrupt_window_exit() -> Result<(), x86::vmx::VmFail> {
let local_interrupt_controller = get_local_interrupt_controller();
assert!(vmx_is_guest_interruptable());
Expand Down
3 changes: 3 additions & 0 deletions hypervisor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#![feature(asm)]
#![feature(lang_items)]
#![allow(unknown_lints)]
#![warn(missing_docs)]

//! A library implementing a mostly-passthrough hypervisor.
//! A mostly passthrough hypervisor mostly virtualizes the guest and does very
Expand All @@ -25,8 +26,10 @@
//! 2. Once globally, call [rustyvisor_unload](fn.rustyvisor_unload.html)

use ::log::{error, info, trace, LevelFilter};
extern crate hypervisor_abi;

mod debug;
mod hypercall_handler;
pub mod interrupt_controller;
mod interrupts;
mod isr;
Expand Down
30 changes: 15 additions & 15 deletions hypervisor/src/register_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@
#[derive(Debug)]
#[repr(C)]
pub struct GeneralPurposeRegisterState {
r15: u64,
r14: u64,
r13: u64,
r12: u64,
r11: u64,
r10: u64,
r9: u64,
r8: u64,
rdi: u64,
rsi: u64,
rbp: u64,
rdx: u64,
rcx: u64,
rbx: u64,
rax: u64,
pub r15: u64,
pub r14: u64,
pub r13: u64,
pub r12: u64,
pub r11: u64,
pub r10: u64,
pub r9: u64,
pub r8: u64,
pub rdi: u64,
pub rsi: u64,
pub rbp: u64,
pub rdx: u64,
pub rcx: u64,
pub rbx: u64,
pub rax: u64,
}

impl GeneralPurposeRegisterState {
Expand Down
Loading