Skip to content

Commit

Permalink
Revises console to use memory-mapped I/O (#932)
Browse files Browse the repository at this point in the history
  • Loading branch information
ultimaweapon committed Aug 17, 2024
1 parent 98f9c38 commit b7838d8
Show file tree
Hide file tree
Showing 23 changed files with 606 additions and 218 deletions.
10 changes: 4 additions & 6 deletions src/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,6 @@
struct Param;
struct Pkg;

#define Ram_ADDR 0

#define Ram_SIZE (((1024 * 1024) * 1024) * 8)

#define Ram_VM_PAGE_SIZE 16384

/**
* Error object managed by Rust side.
*/
Expand Down Expand Up @@ -146,6 +140,10 @@ extern int kvm_get_sregs(int vcpu, kvm_sregs *regs);
extern int kvm_set_sregs(int vcpu, const kvm_sregs *regs);
#endif

#if defined(__linux__)
extern int kvm_translate(int vcpu, kvm_translation *arg);
#endif

#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
4 changes: 4 additions & 0 deletions src/core/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,16 @@ fn main() {
conf.usize_is_size_t = true;
conf.export.exclude.push("KvmRegs".into());
conf.export.exclude.push("KvmSpecialRegs".into());
conf.export.exclude.push("KvmTranslation".into());
conf.export
.rename
.insert("KvmRegs".into(), "kvm_regs".into());
conf.export
.rename
.insert("KvmSpecialRegs".into(), "kvm_sregs".into());
conf.export
.rename
.insert("KvmTranslation".into(), "kvm_translation".into());
conf.defines
.insert("target_os = linux".into(), "__linux__".into());
conf.defines
Expand Down
84 changes: 57 additions & 27 deletions src/core/src/vmm/hv/linux/cpu.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use super::ffi::{kvm_get_regs, kvm_get_sregs, kvm_run, kvm_set_regs, kvm_set_sregs};
use super::ffi::{
kvm_get_regs, kvm_get_sregs, kvm_run, kvm_set_regs, kvm_set_sregs, kvm_translate,
KvmTranslation,
};
use super::regs::{KvmRegs, KvmSpecialRegs};
use super::run::KvmRun;
use crate::vmm::hv::{Cpu, CpuExit, CpuIo, CpuStates};
use crate::vmm::hv::{Cpu, CpuExit, CpuIo, CpuStates, IoBuf};
use libc::munmap;
use std::error::Error;
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::os::fd::{AsRawFd, OwnedFd};
Expand Down Expand Up @@ -43,7 +47,7 @@ impl<'a> Drop for KvmCpu<'a> {
impl<'a> Cpu for KvmCpu<'a> {
type States<'b> = KvmStates<'b> where Self: 'b;
type GetStatesErr = StatesError;
type Exit<'b> = KvmExit<'b> where Self: 'b;
type Exit<'b> = KvmExit<'b, 'a> where Self: 'b;
type RunErr = std::io::Error;

fn states(&mut self) -> Result<Self::States<'_>, Self::GetStatesErr> {
Expand Down Expand Up @@ -74,9 +78,7 @@ impl<'a> Cpu for KvmCpu<'a> {

fn run(&mut self) -> Result<Self::Exit<'_>, Self::RunErr> {
match unsafe { kvm_run(self.fd.as_raw_fd()) } {
0 => Ok(KvmExit {
cx: unsafe { &*self.cx.0 },
}),
0 => Ok(KvmExit(self)),
_ => Err(std::io::Error::last_os_error()),
}
}
Expand Down Expand Up @@ -204,34 +206,62 @@ impl<'a> CpuStates for KvmStates<'a> {
}

/// Implementation of [`Cpu::Exit`] for KVM.
pub struct KvmExit<'a> {
cx: &'a KvmRun,
}
pub struct KvmExit<'a, 'b>(&'a mut KvmCpu<'b>);

impl<'a, 'b> CpuExit for KvmExit<'a, 'b> {
type Io = KvmIo<'a, 'b>;

impl<'a> CpuExit for KvmExit<'a> {
#[cfg(target_arch = "x86_64")]
fn is_hlt(&self) -> bool {
self.cx.exit_reason == 5
fn into_hlt(self) -> Result<(), Self> {
if unsafe { (*self.0.cx.0).exit_reason == 5 } {
Ok(())
} else {
Err(self)
}
}

#[cfg(target_arch = "x86_64")]
fn is_io(&mut self) -> Option<CpuIo> {
// Check if I/O.
if self.cx.exit_reason != 2 {
return None;
fn into_io(self) -> Result<Self::Io, Self> {
if unsafe { (*self.0.cx.0).exit_reason } == 6 {
Ok(KvmIo(self.0))
} else {
Err(self)
}
}
}

// Check direction.
let io = unsafe { &self.cx.exit.io };
let port = io.port;
let data = unsafe { (self.cx as *const KvmRun as *const u8).add(io.data_offset) };
let len: usize = io.size.into();
/// Implementation of [`CpuIo`] for KVM.
pub struct KvmIo<'a, 'b>(&'a mut KvmCpu<'b>);

Some(match io.direction {
0 => todo!(), // KVM_EXIT_IO_IN
1 => CpuIo::Out(port, unsafe { std::slice::from_raw_parts(data, len) }),
_ => unreachable!(),
})
impl<'a, 'b> CpuIo for KvmIo<'a, 'b> {
fn addr(&self) -> usize {
unsafe { (*self.0.cx.0).exit.mmio.phys_addr }
}

fn buffer(&mut self) -> IoBuf {
let io = unsafe { &mut (*self.0.cx.0).exit.mmio };
let len: usize = io.len.try_into().unwrap();
let buf = &mut io.data[..len];

match io.is_write {
0 => IoBuf::Read(buf),
_ => IoBuf::Write(buf),
}
}

fn translate(&self, vaddr: usize) -> Result<usize, Box<dyn Error>> {
let mut data = KvmTranslation {
linear_address: vaddr,
physical_address: 0,
valid: 0,
writeable: 0,
usermode: 0,
pad: [0; 5],
};

match unsafe { kvm_translate(self.0.fd.as_raw_fd(), &mut data) } {
0 => Ok(data.physical_address),
_ => return Err(Box::new(std::io::Error::last_os_error())),
}
}
}

Expand Down
11 changes: 11 additions & 0 deletions src/core/src/vmm/hv/linux/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,15 @@ extern "C" {
pub fn kvm_set_regs(vcpu: c_int, regs: *const KvmRegs) -> c_int;
pub fn kvm_get_sregs(vcpu: c_int, regs: *mut KvmSpecialRegs) -> c_int;
pub fn kvm_set_sregs(vcpu: c_int, regs: *const KvmSpecialRegs) -> c_int;
pub fn kvm_translate(vcpu: c_int, arg: *mut KvmTranslation) -> c_int;
}

#[repr(C)]
pub struct KvmTranslation {
pub linear_address: usize,
pub physical_address: usize,
pub valid: u8,
pub writeable: u8,
pub usermode: u8,
pub pad: [u8; 5],
}
4 changes: 2 additions & 2 deletions src/core/src/vmm/hv/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use self::ffi::{
};
use super::Hypervisor;
use crate::vmm::hw::Ram;
use crate::vmm::{MemoryAddr, VmmError};
use crate::vmm::VmmError;
use libc::{mmap, open, MAP_FAILED, MAP_PRIVATE, O_RDWR, PROT_READ, PROT_WRITE};
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
use std::ptr::null_mut;
Expand Down Expand Up @@ -83,7 +83,7 @@ impl Kvm {
// Set RAM.
let vm = unsafe { OwnedFd::from_raw_fd(vm) };
let slot = 0;
let addr = ram.vm_addr().try_into().unwrap();
let addr = ram.addr().try_into().unwrap();
let len = ram.len().try_into().unwrap();
let mem = ram.host_addr().cast_mut().cast();

Expand Down
13 changes: 7 additions & 6 deletions src/core/src/vmm/hv/linux/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub union Exit {
ex: ManuallyDrop<Ex>,
pub io: Io,
debug: ManuallyDrop<Debug>,
mmio: ManuallyDrop<Mmio>,
pub mmio: Mmio,
iocsr_io: ManuallyDrop<IocsrIo>,
hypercall: ManuallyDrop<Hypercall>,
tpr_access: ManuallyDrop<TprAccess>,
Expand Down Expand Up @@ -92,11 +92,12 @@ struct KvmDebugExitArch {
}

#[repr(C)]
struct Mmio {
phys_addr: u64,
data: [u8; 8],
len: u32,
is_write: u8,
#[derive(Clone, Copy)]
pub struct Mmio {
pub phys_addr: usize,
pub data: [u8; 8],
pub len: u32,
pub is_write: u8,
}

#[repr(C)]
Expand Down
37 changes: 27 additions & 10 deletions src/core/src/vmm/hv/macos/cpu.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::vmm::hv::{Cpu, CpuExit, CpuStates};
use crate::vmm::hv::{Cpu, CpuExit, CpuIo, CpuStates, IoBuf};
use hv_sys::hv_vcpu_destroy;
use std::error::Error;
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::num::NonZero;
Expand Down Expand Up @@ -352,22 +353,38 @@ pub struct HfExit<'a> {
}

impl<'a> CpuExit for HfExit<'a> {
type Io = HfIo;

#[cfg(target_arch = "x86_64")]
fn is_hlt(&self) -> bool {
fn into_hlt(self) -> Result<(), Self> {
match self.exit_reason.try_into() {
Ok(hv_sys::VMX_REASON_HLT) => true,
_ => false,
Ok(hv_sys::VMX_REASON_HLT) => Ok(()),
_ => Err(self),
}
}

#[cfg(target_arch = "x86_64")]
fn is_io(&mut self) -> Option<crate::vmm::hv::CpuIo> {
match self.exit_reason.try_into() {
Ok(hv_sys::VMX_REASON_IO) => todo!(),
_ => None,
}
fn into_io(self) -> Result<Self::Io, Self> {
todo!();
}
}

/// Implementation of [`CpuIo`] for Hypervisor Framework.
pub struct HfIo {}

impl CpuIo for HfIo {
fn addr(&self) -> usize {
todo!();
}

fn buffer(&mut self) -> IoBuf {
todo!();
}

fn translate(&self, vaddr: usize) -> Result<usize, Box<dyn Error>> {
todo!();
}
}

/// Implementation of [`Cpu::RunErr`].
#[derive(Debug, Error)]
pub enum RunError {
Expand Down
4 changes: 2 additions & 2 deletions src/core/src/vmm/hv/macos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use self::cpu::HfCpu;
use self::vm::Vm;
use super::Hypervisor;
use crate::vmm::hw::Ram;
use crate::vmm::{MemoryAddr, VmmError};
use crate::vmm::VmmError;
use hv_sys::hv_vcpu_create;
use std::ffi::c_int;
use std::num::NonZero;
Expand All @@ -28,7 +28,7 @@ impl Hf {
// Map memory.
vm.vm_map(
ram.host_addr().cast_mut().cast(),
ram.vm_addr().try_into().unwrap(),
ram.addr().try_into().unwrap(),
ram.len(),
)
.map_err(VmmError::MapRamFailed)?;
Expand Down
25 changes: 17 additions & 8 deletions src/core/src/vmm/hv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,25 @@ pub trait CpuStates {
}

/// Contains information when VM exited.
pub trait CpuExit {
#[cfg(target_arch = "x86_64")]
fn is_hlt(&self) -> bool;
pub trait CpuExit: Sized {
type Io: CpuIo;

#[cfg(target_arch = "x86_64")]
fn is_io(&mut self) -> Option<CpuIo>;
fn into_hlt(self) -> Result<(), Self>;

fn into_io(self) -> Result<Self::Io, Self>;
}

/// Contains information when a VM exited because of memory-mapped I/O.
pub trait CpuIo {
/// Returns physical address where the VM attempting to be accessed.
fn addr(&self) -> usize;
fn buffer(&mut self) -> IoBuf;
fn translate(&self, vaddr: usize) -> Result<usize, Box<dyn Error>>;
}

/// Contains information when a VM exited because of I/O instructions.
#[cfg(target_arch = "x86_64")]
pub enum CpuIo<'a> {
Out(u16, &'a [u8]),
/// Encapsulates a buffer for memory-mapped I/O.
pub enum IoBuf<'a> {
Write(&'a [u8]),
Read(&'a mut [u8]),
}
35 changes: 30 additions & 5 deletions src/core/src/vmm/hv/windows/cpu.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::vmm::hv::{Cpu, CpuExit, CpuIo, CpuStates};
use crate::vmm::hv::{Cpu, CpuExit, CpuIo, CpuStates, IoBuf};
use std::error::Error;
use std::marker::PhantomData;
use std::mem::{size_of, zeroed, MaybeUninit};
use thiserror::Error;
Expand Down Expand Up @@ -273,13 +274,37 @@ pub struct WhpExit<'a, 'b> {
}

impl<'a, 'b> CpuExit for WhpExit<'a, 'b> {
type Io = WhpIo<'a, 'b>;

#[cfg(target_arch = "x86_64")]
fn is_hlt(&self) -> bool {
self.cx.ExitReason == WHvRunVpExitReasonX64Halt
fn into_hlt(self) -> Result<(), Self> {
if self.cx.ExitReason == WHvRunVpExitReasonX64Halt {
Ok(())
} else {
Err(self)
}
}

#[cfg(target_arch = "x86_64")]
fn is_io(&mut self) -> Option<CpuIo> {
fn into_io(self) -> Result<Self::Io, Self> {
todo!();
}
}

/// Implementation of [`CpuIo`] for Windows Hypervisor Platform.
pub struct WhpIo<'a, 'b> {
cpu: PhantomData<&'a mut WhpCpu<'b>>,
}

impl<'a, 'b> CpuIo for WhpIo<'a, 'b> {
fn addr(&self) -> usize {
todo!();
}

fn buffer(&mut self) -> IoBuf {
todo!();
}

fn translate(&self, vaddr: usize) -> Result<usize, Box<dyn Error>> {
todo!()
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/core/src/vmm/hv/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use self::cpu::WhpCpu;
use self::partition::Partition;
use super::Hypervisor;
use crate::vmm::hw::Ram;
use crate::vmm::{MemoryAddr, VmmError};
use crate::vmm::VmmError;
use std::sync::Arc;
use thiserror::Error;
use windows_sys::core::HRESULT;
Expand Down Expand Up @@ -30,7 +30,7 @@ impl Whp {
// Map memory.
part.map_gpa(
ram.host_addr().cast(),
ram.vm_addr().try_into().unwrap(),
ram.addr().try_into().unwrap(),
ram.len().try_into().unwrap(),
)
.map_err(VmmError::MapRamFailed)?;
Expand Down
Loading

0 comments on commit b7838d8

Please sign in to comment.