Refactor to use rust-vmm crates for kvm operations

This commit is contained in:
Bruce Leidl 2023-01-31 13:47:55 -05:00
parent 99020a5ad4
commit 41d6d10373
36 changed files with 674 additions and 1352 deletions

92
Cargo.lock generated
View File

@ -149,6 +149,26 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "kvm-bindings"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efe70e65a5b092161d17f5005b66e5eefe7a94a70c332e755036fc4af78c4e79"
dependencies = [
"vmm-sys-util",
]
[[package]]
name = "kvm-ioctls"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3a321cabd827642499c77e27314f388dd83a717a5ca716b86476fb947f73ae4"
dependencies = [
"kvm-bindings",
"libc",
"vmm-sys-util",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@ -235,11 +255,15 @@ name = "ph"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"kvm-bindings",
"kvm-ioctls",
"lazy_static", "lazy_static",
"libc", "libc",
"libcitadel", "libcitadel",
"signal-hook", "signal-hook",
"termios", "termios",
"thiserror",
"vmm-sys-util",
] ]
[[package]] [[package]]
@ -254,16 +278,16 @@ version = "0.4.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
dependencies = [ dependencies = [
"unicode-xid 0.1.0", "unicode-xid",
] ]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.3" version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8" checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
dependencies = [ dependencies = [
"unicode-xid 0.2.0", "unicode-ident",
] ]
[[package]] [[package]]
@ -281,7 +305,7 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
dependencies = [ dependencies = [
"proc-macro2 1.0.3", "proc-macro2 1.0.50",
] ]
[[package]] [[package]]
@ -323,9 +347,9 @@ version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e" checksum = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e"
dependencies = [ dependencies = [
"proc-macro2 1.0.3", "proc-macro2 1.0.50",
"quote 1.0.2", "quote 1.0.2",
"syn 1.0.5", "syn 1.0.107",
] ]
[[package]] [[package]]
@ -367,18 +391,18 @@ checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
dependencies = [ dependencies = [
"proc-macro2 0.4.30", "proc-macro2 0.4.30",
"quote 0.6.13", "quote 0.6.13",
"unicode-xid 0.1.0", "unicode-xid",
] ]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.5" version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [ dependencies = [
"proc-macro2 1.0.3", "proc-macro2 1.0.50",
"quote 1.0.2", "quote 1.0.2",
"unicode-xid 0.2.0", "unicode-ident",
] ]
[[package]] [[package]]
@ -390,7 +414,7 @@ dependencies = [
"proc-macro2 0.4.30", "proc-macro2 0.4.30",
"quote 0.6.13", "quote 0.6.13",
"syn 0.15.44", "syn 0.15.44",
"unicode-xid 0.1.0", "unicode-xid",
] ]
[[package]] [[package]]
@ -420,6 +444,26 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "thiserror"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2 1.0.50",
"quote 1.0.2",
"syn 1.0.107",
]
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.4.10" version = "0.4.10"
@ -429,24 +473,34 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "unicode-ident"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
version = "0.1.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
[[package]] [[package]]
name = "vcpkg" name = "vcpkg"
version = "0.2.7" version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33dd455d0f96e90a75803cfeb7f948768c08d70a6de9a8d2362461935698bf95" checksum = "33dd455d0f96e90a75803cfeb7f948768c08d70a6de9a8d2362461935698bf95"
[[package]]
name = "vmm-sys-util"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd64fe09d8e880e600c324e7d664760a17f56e9672b7495a86381b49e4f72f46"
dependencies = [
"bitflags",
"libc",
]
[[package]] [[package]]
name = "void" name = "void"
version = "1.0.2" version = "1.0.2"

View File

@ -4,10 +4,16 @@ version = "0.1.0"
authors = ["Bruce Leidl <bruce@subgraph.com>"] authors = ["Bruce Leidl <bruce@subgraph.com>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
byteorder="1.0.0" byteorder="1.0.0"
libc = "*" libc = "*"
termios = "0.2.2" termios = "0.2.2"
lazy_static = "1.4.0" lazy_static = "1.4.0"
signal-hook = "0.1.10" signal-hook = "0.1.10"
thiserror = "1.0"
vmm-sys-util = "0.11.1"
kvm-ioctls = "0.12.0"
kvm-bindings = "0.6.0"
libcitadel = { git = "https://github.com/brl/citadel-tools", rev="44d5ce660f1f5cf8a3ad1060b143926a99be5148" } libcitadel = { git = "https://github.com/brl/citadel-tools", rev="44d5ce660f1f5cf8a3ad1060b143926a99be5148" }

56
ph-init/Cargo.lock generated
View File

@ -20,4 +20,60 @@ version = "0.1.0"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
"libc", "libc",
"thiserror",
] ]
[[package]]
name = "proc-macro2"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
dependencies = [
"proc-macro2",
]
[[package]]
name = "syn"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"

View File

@ -226,7 +226,6 @@ impl InitServer {
let sommelierx = ServiceLaunch::new("sommelier-x", "/opt/ph/usr/bin/sommelier") let sommelierx = ServiceLaunch::new("sommelier-x", "/opt/ph/usr/bin/sommelier")
.base_environment() .base_environment()
.uidgid(1000,1000) .uidgid(1000,1000)
.env("SOMMELIER_SHM_DRIVER", shm_driver)
.arg("-X") .arg("-X")
.arg("--x-display=0") .arg("--x-display=0")
.arg("--no-exit-with-child") .arg("--no-exit-with-child")

View File

@ -70,7 +70,7 @@ pub fn move_mount(source: &str, target: &str) -> Result<()> {
} }
pub fn mount_9p(name: &str, target: &str) -> Result<()> { pub fn mount_9p(name: &str, target: &str) -> Result<()> {
const MS_LAZYTIME: libc::c_ulong = (1 << 25); const MS_LAZYTIME: libc::c_ulong = 1 << 25;
mount(name, target, "9p", mount(name, target, "9p",
libc::MS_NOATIME|MS_LAZYTIME, libc::MS_NOATIME|MS_LAZYTIME,
Some("trans=virtio,cache=loose")) Some("trans=virtio,cache=loose"))

View File

@ -2,7 +2,7 @@ use std::sync::{Arc, RwLock};
use std::io::{self, Write}; use std::io::{self, Write};
use crate::vm::io::{IoPortOps,IoDispatcher}; use crate::vm::io::{IoPortOps,IoDispatcher};
use crate::kvm::Kvm; use crate::vm::KvmVm;
const UART_TX: u16 = 0; const UART_TX: u16 = 0;
const UART_RX: u16 = 0; const UART_RX: u16 = 0;
@ -67,7 +67,7 @@ impl Bits for u8 {
pub struct SerialDevice { pub struct SerialDevice {
iobase: u16, iobase: u16,
kvm: Kvm, kvm_vm: KvmVm,
irq: u8, irq: u8,
irq_state: u8, irq_state: u8,
txcnt: usize, txcnt: usize,
@ -134,12 +134,12 @@ impl SerialDevice {
if iir == 0 { if iir == 0 {
self.iir = UART_IIR_NO_INT; self.iir = UART_IIR_NO_INT;
if self.irq_state != 0 { if self.irq_state != 0 {
self.kvm.irq_line(self.irq as u32, 0).unwrap(); self.kvm_vm.set_irq_line(self.irq as u32, false).unwrap();
} }
} else { } else {
self.iir = iir; self.iir = iir;
if self.irq_state == 0 { if self.irq_state == 0 {
self.kvm.irq_line(self.irq as u32, 1).unwrap(); self.kvm_vm.set_irq_line(self.irq as u32, true).unwrap();
} }
} }
self.irq_state = iir; self.irq_state = iir;
@ -270,9 +270,9 @@ impl SerialDevice {
} }
} }
pub fn register(kvm: Kvm, io: Arc<IoDispatcher>, id: u8) { pub fn register(kvm_vm: KvmVm, io: Arc<IoDispatcher>, id: u8) {
if let Some((base,irq)) = SerialDevice::base_irq_for_id(id) { if let Some((base,irq)) = SerialDevice::base_irq_for_id(id) {
let dev = SerialDevice::new(kvm, base, irq); let dev = SerialDevice::new(kvm_vm, base, irq);
io.register_ioports(base, 8, Arc::new(RwLock::new(dev))); io.register_ioports(base, 8, Arc::new(RwLock::new(dev)));
} }
} }
@ -287,10 +287,10 @@ impl SerialDevice {
} }
} }
fn new(kvm: Kvm, iobase: u16, irq: u8) -> SerialDevice { fn new(kvm_vm: KvmVm, iobase: u16, irq: u8) -> SerialDevice {
SerialDevice { SerialDevice {
iobase, iobase,
kvm, kvm_vm,
irq, irq,
irq_state: 0, irq_state: 0,
txcnt: 0, txcnt: 0,

View File

@ -3,13 +3,14 @@ use std::sync::{RwLock, Arc};
use std::thread; use std::thread;
use crate::{system, virtio}; use crate::{system, virtio};
use crate::system::{EPoll,EventFd}; use crate::system::EPoll;
use crate::memory::{MemoryManager, DrmDescriptor}; use crate::memory::{MemoryManager, DrmDescriptor};
use crate::virtio::{VirtQueue, VirtioBus, VirtioDeviceOps, Chain}; use crate::virtio::{VirtQueue, VirtioBus, VirtioDeviceOps, Chain};
use crate::devices::virtio_wl::{vfd::VfdManager, consts::*, Error, Result, VfdObject}; use crate::devices::virtio_wl::{vfd::VfdManager, consts::*, Error, Result, VfdObject};
use crate::system::ioctl::ioctl_with_ref; use crate::system::ioctl::ioctl_with_ref;
use std::os::raw::{c_ulong, c_uint, c_ulonglong}; use std::os::raw::{c_ulong, c_uint, c_ulonglong};
use vmm_sys_util::eventfd::EventFd;
#[repr(C)] #[repr(C)]
struct dma_buf_sync { struct dma_buf_sync {
@ -41,7 +42,7 @@ impl VirtioWayland {
} }
fn create_device(memory: MemoryManager, in_vq: VirtQueue, out_vq: VirtQueue, transition: bool, enable_dmabuf: bool) -> Result<WaylandDevice> { fn create_device(memory: MemoryManager, in_vq: VirtQueue, out_vq: VirtQueue, transition: bool, enable_dmabuf: bool) -> Result<WaylandDevice> {
let kill_evt = EventFd::new().map_err(Error::EventFdCreate)?; let kill_evt = EventFd::new(0).map_err(Error::EventFdCreate)?;
let dev = WaylandDevice::new(memory, in_vq, out_vq, kill_evt, transition, enable_dmabuf)?; let dev = WaylandDevice::new(memory, in_vq, out_vq, kill_evt, transition, enable_dmabuf)?;
Ok(dev) Ok(dev)
} }

View File

@ -1,36 +0,0 @@
use std::{fmt, result};
use crate::system::Error as SysError;
use crate::system::ErrnoError;
pub type Result<T> = result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
OpenKvm(ErrnoError),
MissingRequiredExtension(u32),
BadVersion,
IoctlError(&'static str, ErrnoError),
IoEventCreate(SysError),
}
impl Error {
pub fn is_interrupted(&self) -> bool {
match self {
Error::IoctlError(_, e) => e.is_interrupted(),
_ => false,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Error::*;
match self {
OpenKvm(e) => write!(f, "could not open /dev/kvm: {}", e),
MissingRequiredExtension(ext) => write!(f, "kernel does not support a required kvm extension: {}", ext),
BadVersion => write!(f, "unexpected kvm api version"),
IoctlError(name, err) => write!(f, "failed to call {} ioctl: {}", name, err),
IoEventCreate(e) => write!(f, "failed to create ioeventfd: {}", e),
}
}
}

View File

@ -1,256 +0,0 @@
use libc::{self, c_char, c_ulong};
use std::os::unix::io::RawFd;
use std::ffi::CString;
use crate::system::ioctl::{ioctl_with_val,ioctl_with_ref,ioctl_with_mut_ref};
use crate::kvm::{Result, Error};
use crate::system::ErrnoError;
use crate::vm::arch::KvmRegs;
use std::result;
const KVMIO: u64 = 0xAE;
const KVM_GET_API_VERSION: c_ulong = io! (KVMIO, 0x00);
const KVM_CREATE_VM: c_ulong = io! (KVMIO, 0x01);
const KVM_CHECK_EXTENSION: c_ulong = io! (KVMIO, 0x03);
const KVM_CREATE_IRQCHIP: c_ulong = io! (KVMIO, 0x60);
const KVM_GET_VCPU_MMAP_SIZE: c_ulong = io! (KVMIO, 0x04);
const KVM_CREATE_VCPU: c_ulong = io! (KVMIO, 0x41);
const KVM_SET_USER_MEMORY_REGION: c_ulong = iow! (KVMIO, 0x46, 32);
const KVM_IRQ_LINE: c_ulong = iow! (KVMIO, 0x61, 8);
const KVM_IRQFD: c_ulong = iow! (KVMIO, 0x76, 32);
const KVM_IOEVENTFD: c_ulong = iow! (KVMIO, 0x79, 64);
const KVM_RUN: c_ulong = io! (KVMIO, 0x80);
const KVM_GET_REGS: c_ulong = ior! (KVMIO, 0x81, 144);
const KVM_SET_REGS: c_ulong = iow! (KVMIO, 0x82, 144);
struct InnerFd(RawFd);
impl InnerFd {
fn raw(&self) -> RawFd { self.0 }
}
impl Drop for InnerFd {
fn drop(&mut self) {
let _ = unsafe { libc::close(self.0) };
}
}
pub struct SysFd(InnerFd);
fn raw_open_kvm() -> Result<RawFd> {
let path = CString::new("/dev/kvm").unwrap();
let fd = unsafe { libc::open(path.as_ptr() as *const c_char, libc::O_RDWR) };
if fd < 0 {
return Err(Error::OpenKvm(ErrnoError::last_os_error()));
}
Ok(fd)
}
impl SysFd {
pub fn open() -> Result<SysFd> {
let fd = raw_open_kvm()?;
Ok(SysFd(InnerFd(fd)))
}
pub fn raw(&self) -> RawFd { self.0.raw() }
}
pub struct VmFd(InnerFd);
impl VmFd {
fn new(fd: RawFd) -> VmFd {
VmFd( InnerFd(fd) )
}
pub fn raw(&self) -> RawFd { self.0.raw() }
}
pub struct VcpuFd(InnerFd);
impl VcpuFd {
fn new(fd: RawFd) -> VcpuFd {
VcpuFd( InnerFd(fd) )
}
pub fn raw(&self) -> RawFd { self.0.raw() }
}
pub fn kvm_check_extension(sysfd: &SysFd, extension: u32) -> Result<u32> {
unsafe {
ioctl_with_val(sysfd.raw(), KVM_CHECK_EXTENSION, extension as c_ulong)
.map_err(|e| Error::IoctlError("KVM_CHECK_EXTENSION", e))
}
}
pub fn kvm_get_api_version(sysfd: &SysFd) -> Result<u32> {
unsafe {
ioctl_with_val(sysfd.raw(), KVM_GET_API_VERSION, 0)
.map_err(|e| Error::IoctlError("KVM_GET_API_VERSION", e))
}
}
pub fn kvm_create_vm(sysfd: &SysFd) -> Result<VmFd> {
let fd = unsafe {
ioctl_with_val(sysfd.raw(), KVM_CREATE_VM, 0)
.map_err(|e| Error::IoctlError("KVM_CREATE_VM", e))?
};
Ok(VmFd::new(fd as RawFd))
}
pub fn kvm_get_vcpu_mmap_size(sysfd: &SysFd) -> Result<u32> {
unsafe {
ioctl_with_val(sysfd.raw(), KVM_GET_VCPU_MMAP_SIZE, 0)
.map_err(|e| Error::IoctlError("KVM_GET_VCPU_MMAP_SIZE", e))
}
}
#[repr(C)]
pub struct KvmUserspaceMemoryRegion {
slot: u32,
flags: u32,
guest_phys_addr: u64,
memory_size: u64,
userspace_addr: u64,
}
impl KvmUserspaceMemoryRegion {
pub fn new(slot: u32, guest_address: u64, host_address: u64, size: u64) -> KvmUserspaceMemoryRegion {
KvmUserspaceMemoryRegion {
slot,
flags: 0,
guest_phys_addr: guest_address,
memory_size: size,
userspace_addr: host_address,
}
}
}
pub fn kvm_set_user_memory_region(vmfd: &VmFd, region: &KvmUserspaceMemoryRegion) -> Result<()> {
call_ioctl_with_ref("KVM_SET_USER_MEMORY_REGION",vmfd.raw(), KVM_SET_USER_MEMORY_REGION, region)
}
pub fn kvm_create_irqchip(vmfd: &VmFd) -> Result<()> {
call_ioctl_with_val("KVM_CREATE_IRQCHIP", vmfd.raw(), KVM_CREATE_IRQCHIP, 0)
}
pub fn kvm_create_vcpu(vmfd: &VmFd, cpu_id: u32) -> Result<VcpuFd> {
let fd = unsafe {
ioctl_with_val(vmfd.raw(), KVM_CREATE_VCPU, cpu_id as c_ulong)
.map_err(|e| Error::IoctlError("KVM_CREATE_VCPU", e))?
};
Ok(VcpuFd::new(fd as RawFd))
}
#[repr(C)]
pub struct KvmIrqLevel {
irq: u32,
level: u32,
}
impl KvmIrqLevel {
pub fn new(irq: u32, level: u32) -> KvmIrqLevel {
KvmIrqLevel { irq, level }
}
}
pub fn kvm_irq_line(vmfd: &VmFd, level: &KvmIrqLevel) -> Result<()> {
call_ioctl_with_ref("KVM_IRQ_LINE", vmfd.raw(), KVM_IRQ_LINE, level)
}
#[repr(C)]
pub struct KvmIrqFd {
fd: u32,
gsi: u32,
flags: u32,
resample_fd: u32,
pad1: u64,
pad2: u64,
}
impl KvmIrqFd {
pub fn new(fd: u32, gsi: u32) -> KvmIrqFd {
KvmIrqFd{fd, gsi, flags:0, resample_fd: 0, pad1: 0, pad2: 0}
}
}
pub fn kvm_irqfd(vmfd: &VmFd, irqfd: &KvmIrqFd) -> Result<()> {
call_ioctl_with_ref("KVM_IRQFD", vmfd.raw(), KVM_IRQFD, irqfd)
}
pub const IOEVENTFD_FLAG_DATAMATCH: u32 = 1;
pub const _IOEVENTFD_FLAG_PIO : u32 = 2;
pub const IOEVENTFD_FLAG_DEASSIGN: u32 = 4;
#[repr(C)]
pub struct KvmIoEventFd {
datamatch: u64,
addr: u64,
len: u32,
fd: u32,
flags: u32,
padding: [u8; 36],
}
impl KvmIoEventFd {
pub fn new_with_addr_fd(addr: u64, fd: RawFd) -> KvmIoEventFd {
KvmIoEventFd::new(0, addr, 0, fd as u32, 0)
}
fn new(datamatch: u64, addr: u64, len: u32, fd: u32, flags: u32) -> KvmIoEventFd {
KvmIoEventFd{datamatch, addr, len, fd, flags, padding: [0;36]}
}
#[allow(dead_code)]
pub fn set_datamatch(&mut self, datamatch: u64, len: u32) {
self.flags |= IOEVENTFD_FLAG_DATAMATCH;
self.datamatch = datamatch;
self.len = len;
}
pub fn set_deassign(&mut self) {
self.flags |= IOEVENTFD_FLAG_DEASSIGN;
}
}
pub fn kvm_ioeventfd(vmfd: &VmFd, ioeventfd: &KvmIoEventFd) -> Result<()> {
call_ioctl_with_ref("KVM_IOEVENTFD", vmfd.raw(), KVM_IOEVENTFD, ioeventfd)
}
pub fn kvm_get_regs(cpufd: &VcpuFd, regs: &mut KvmRegs) -> Result<()> {
call_ioctl_with_mut_ref("KVM_GET_REGS", cpufd.raw(), KVM_GET_REGS, regs)
}
pub fn kvm_set_regs(cpufd: &VcpuFd, regs: &KvmRegs) -> Result<()> {
call_ioctl_with_ref("KVM_SET_REGS", cpufd.raw(), KVM_SET_REGS, regs)
}
pub fn kvm_run(cpufd: &VcpuFd) -> Result<()> {
call_ioctl_with_val("KVM_RUN", cpufd.raw(), KVM_RUN, 0)
}
fn call_ioctl(name: &'static str, result: result::Result<u32, ErrnoError>) -> Result<()> {
result.map_err(|e| Error::IoctlError(name, e))?;
Ok(())
}
fn call_ioctl_with_ref<T>(name: &'static str, fd: RawFd, request: c_ulong, arg: &T) -> Result<()> {
unsafe {
ioctl_with_ref(fd, request, arg)
.map_err(|e| Error::IoctlError(name, e))?;
Ok(())
}
}
fn call_ioctl_with_mut_ref<T>(name: &'static str, fd: RawFd, request: c_ulong, arg: &mut T) -> Result<()> {
unsafe {
ioctl_with_mut_ref(fd, request, arg)
.map_err(|e| Error::IoctlError(name, e))?;
Ok(())
}
}
fn call_ioctl_with_val(name: &'static str, fd: RawFd, request: c_ulong, val: c_ulong) -> Result<()> {
unsafe {
call_ioctl(name, ioctl_with_val(fd, request, val))
}
}

View File

@ -1,43 +0,0 @@
use std::sync::Arc;
use std::os::unix::io::{AsRawFd,RawFd};
use crate::kvm::{Kvm,Result,Error};
use crate::system::EventFd;
use crate::system;
pub struct IoEventFd {
kvm: Kvm,
addr: u64,
evt: Arc<EventFd>
}
impl IoEventFd {
pub fn new(kvm: &Kvm, address: u64) -> Result<IoEventFd> {
let evt = EventFd::new().map_err(Error::IoEventCreate)?;
kvm.ioeventfd_add(address, evt.as_raw_fd())?;
Ok(IoEventFd {
kvm: kvm.clone(),
addr: address,
evt: evt.into(),
})
}
pub fn read(&self) -> system::Result<u64> {
self.evt.read()
}
pub fn write(&self, v: u64) -> system::Result<()> {
self.evt.write(v)
}
}
impl Drop for IoEventFd {
fn drop(&mut self) {
let _ = self.kvm.ioeventfd_del(self.addr, self.evt.as_raw_fd());
}
}
impl AsRawFd for IoEventFd {
fn as_raw_fd(&self) -> RawFd {
self.evt.as_raw_fd()
}
}

View File

@ -1,160 +0,0 @@
use std::os::unix::io::RawFd;
use std::sync::Arc;
mod ioctl;
mod ioeventfd;
mod error;
pub use error::{Result,Error};
pub use ioeventfd::IoEventFd;
use crate::vm::arch::KvmRegs;
pub const KVM_CAP_IRQCHIP: u32 = 0;
pub const KVM_CAP_HLT: u32 = 1;
pub const KVM_CAP_USER_MEMORY: u32 = 3;
pub const KVM_CAP_SET_TSS_ADDR: u32 = 4;
pub const KVM_CAP_EXT_CPUID: u32 = 7;
pub const KVM_CAP_IRQ_ROUTING: u32 = 25;
pub const KVM_CAP_IRQ_INJECT_STATUS: u32 = 26;
pub const KVM_CAP_PIT2: u32 = 33;
pub const KVM_CAP_IOEVENTFD: u32 = 36;
#[derive(Clone)]
pub struct Kvm {
sysfd: Arc<ioctl::SysFd>,
vmfd: Arc<ioctl::VmFd>,
}
fn check_extensions(sysfd: &ioctl::SysFd, extensions: &[u32]) -> Result<()> {
for e in extensions {
check_extension(sysfd, *e)?;
}
Ok(())
}
fn check_extension(sysfd: &ioctl::SysFd, extension: u32) -> Result<()> {
let ret = ioctl::kvm_check_extension(&sysfd, extension)?;
if ret == 0 {
Err(Error::MissingRequiredExtension(extension))
} else {
Ok(())
}
}
fn check_version(sysfd: &ioctl::SysFd) -> Result<()> {
let version= ioctl::kvm_get_api_version(&sysfd)?;
if version != 12 {
return Err(Error::BadVersion);
}
Ok(())
}
impl Kvm {
pub fn open(required_extensions: &[u32]) -> Result<Kvm> {
let sysfd = ioctl::SysFd::open()?;
check_version(&sysfd)?;
check_extensions(&sysfd, &required_extensions)?;
let vmfd= ioctl::kvm_create_vm(&sysfd)?;
Ok(Kvm{
sysfd: Arc::new(sysfd),
vmfd: Arc::new(vmfd),
})
}
pub fn add_memory_region(&self, slot: u32, guest_address: u64, host_address: u64, size: usize) -> Result<()> {
let region = ioctl::KvmUserspaceMemoryRegion::new(slot, guest_address, host_address, size as u64);
ioctl::kvm_set_user_memory_region(&self.vmfd, &region)?;
Ok(())
}
pub fn remove_memory_region(&self, slot: u32) -> Result<()> {
let region = ioctl::KvmUserspaceMemoryRegion::new(slot, 0, 0, 0);
ioctl::kvm_set_user_memory_region(&self.vmfd, &region)?;
Ok(())
}
pub fn create_irqchip(&self) -> Result<()> {
ioctl::kvm_create_irqchip(&self.vmfd)?;
Ok(())
}
pub fn irq_line(&self, irq: u32, level: u32) -> Result<()> {
let irq_level = ioctl::KvmIrqLevel::new(irq, level);
ioctl::kvm_irq_line(&self.vmfd, &irq_level)?;
Ok(())
}
pub fn irqfd(&self, fd: u32, gsi: u32) -> Result<()> {
let irqfd = ioctl::KvmIrqFd::new(fd, gsi);
ioctl::kvm_irqfd(&self.vmfd, &irqfd)
}
pub fn ioeventfd_add(&self, address: u64, fd: RawFd) -> Result<()> {
// XXX check for zero length capability
let ioeventfd = ioctl::KvmIoEventFd::new_with_addr_fd(address, fd);
ioctl::kvm_ioeventfd(&self.vmfd, &ioeventfd)
}
pub fn ioeventfd_del(&self, address: u64, fd: RawFd) -> Result<()> {
let mut ioeventfd = ioctl::KvmIoEventFd::new_with_addr_fd(address, fd);
ioeventfd.set_deassign();
ioctl::kvm_ioeventfd(&self.vmfd, &ioeventfd)
}
pub fn new_vcpu(&self, id: usize) -> Result<KvmVcpu> {
let cpufd = ioctl::kvm_create_vcpu(&self.vmfd, id as u32)?;
Ok(KvmVcpu::new(id, Arc::new(cpufd), self.sysfd.clone()))
}
pub fn vmfd(&self) -> RawFd {
self.vmfd.raw()
}
}
#[derive(Clone)]
pub struct KvmVcpu {
_id: usize,
cpufd: Arc<ioctl::VcpuFd>,
sysfd: Arc<ioctl::SysFd>,
}
impl KvmVcpu {
fn new(id: usize, cpufd: Arc<ioctl::VcpuFd>, sysfd: Arc<ioctl::SysFd>) -> KvmVcpu {
KvmVcpu { _id: id, cpufd, sysfd }
}
pub fn raw_fd(&self) -> RawFd {
self.cpufd.raw()
}
pub fn sys_raw_fd(&self) -> RawFd {
self.sysfd.raw()
}
pub fn get_regs(&self) -> Result<KvmRegs> {
let mut regs = KvmRegs::new();
ioctl::kvm_get_regs(&self.cpufd, &mut regs)?;
Ok(regs)
}
pub fn set_regs(&self, regs: &KvmRegs) -> Result<()> {
ioctl::kvm_set_regs(&self.cpufd, regs)?;
Ok(())
}
pub fn run(&self) -> Result<()> {
ioctl::kvm_run(&self.cpufd)?;
Ok(())
}
pub fn get_vcpu_mmap_size(&self) -> Result<usize> {
Ok(ioctl::kvm_get_vcpu_mmap_size(&self.sysfd)? as usize)
}
}

View File

@ -7,7 +7,6 @@ pub mod util;
mod vm; mod vm;
mod memory; mod memory;
mod devices; mod devices;
mod kvm;
mod virtio; mod virtio;
mod disk; mod disk;

View File

@ -3,16 +3,16 @@ use std::os::unix::io::{AsRawFd,RawFd};
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
use crate::memory::{GuestRam, SystemAllocator, Mapping, Error, Result}; use crate::memory::{GuestRam, SystemAllocator, Mapping, Error, Result};
use crate::kvm::Kvm;
use crate::system::FileDesc; use crate::system::FileDesc;
use crate::util::BitSet; use crate::util::BitSet;
use crate::memory::drm::{DrmBufferAllocator, DrmDescriptor}; use crate::memory::drm::{DrmBufferAllocator, DrmDescriptor};
use std::io::SeekFrom; use std::io::SeekFrom;
use crate::memory::ram::MemoryRegion; use crate::memory::ram::MemoryRegion;
use crate::vm::KvmVm;
#[derive(Clone)] #[derive(Clone)]
pub struct MemoryManager { pub struct MemoryManager {
kvm: Kvm, kvm_vm: KvmVm,
ram: GuestRam, ram: GuestRam,
device_memory: Arc<RwLock<DeviceMemory>>, device_memory: Arc<RwLock<DeviceMemory>>,
drm_allocator: Option<DrmBufferAllocator>, drm_allocator: Option<DrmBufferAllocator>,
@ -20,7 +20,7 @@ pub struct MemoryManager {
impl MemoryManager { impl MemoryManager {
pub fn new(kvm: Kvm, ram: GuestRam, allocator: SystemAllocator, use_drm: bool) -> Result<Self> { pub fn new(kvm_vm: KvmVm, ram: GuestRam, allocator: SystemAllocator, use_drm: bool) -> Result<Self> {
let device_memory = RwLock::new(DeviceMemory::new(ram.region_count(), allocator)).into(); let device_memory = RwLock::new(DeviceMemory::new(ram.region_count(), allocator)).into();
let drm_allocator = if use_drm { let drm_allocator = if use_drm {
DrmBufferAllocator::open().ok() DrmBufferAllocator::open().ok()
@ -28,7 +28,7 @@ impl MemoryManager {
None None
}; };
Ok(MemoryManager { Ok(MemoryManager {
kvm, ram, device_memory, kvm_vm, ram, device_memory,
drm_allocator, drm_allocator,
}) })
} }
@ -37,8 +37,8 @@ impl MemoryManager {
&self.ram &self.ram
} }
pub fn kvm(&self) -> &Kvm { pub fn kvm_vm(&self) -> &KvmVm {
&self.kvm &self.kvm_vm
} }
pub fn set_ram_regions(&mut self, regions: Vec<MemoryRegion>) { pub fn set_ram_regions(&mut self, regions: Vec<MemoryRegion>) {
@ -49,12 +49,12 @@ impl MemoryManager {
pub fn register_device_memory(&self, fd: RawFd, size: usize) -> Result<(u64, u32)> { pub fn register_device_memory(&self, fd: RawFd, size: usize) -> Result<(u64, u32)> {
let mut devmem = self.device_memory.write().unwrap(); let mut devmem = self.device_memory.write().unwrap();
devmem.register(self.kvm(), fd, size) devmem.register(self.kvm_vm(), fd, size)
} }
pub fn unregister_device_memory(&self, slot: u32) -> Result<()> { pub fn unregister_device_memory(&self, slot: u32) -> Result<()> {
let mut devmem = self.device_memory.write().unwrap(); let mut devmem = self.device_memory.write().unwrap();
devmem.unregister(self.kvm(), slot) devmem.unregister(self.kvm_vm(), slot)
} }
pub fn drm_available(&self) -> bool { pub fn drm_available(&self) -> bool {
@ -108,13 +108,13 @@ impl DeviceMemory {
} }
} }
fn register(&mut self, kvm: &Kvm, fd: RawFd, size: usize) -> Result<(u64, u32)> { fn register(&mut self, kvm_vm: &KvmVm, fd: RawFd, size: usize) -> Result<(u64, u32)> {
let mapping = Mapping::new_from_fd(fd, size) let mapping = Mapping::new_from_fd(fd, size)
.map_err(Error::MappingFailed)?; .map_err(Error::MappingFailed)?;
let (addr, slot) = self.allocate_addr_and_slot(size)?; let (addr, slot) = self.allocate_addr_and_slot(size)?;
if let Err(e) = kvm.add_memory_region(slot, addr, mapping.address(), size) { if let Err(e) = kvm_vm.add_memory_region(slot, addr, mapping.address(), size) {
self.free_addr_and_slot(addr, slot); self.free_addr_and_slot(addr, slot);
Err(Error::RegisterMemoryFailed(e)) Err(Error::RegisterMemoryFailed(e))
} else { } else {
@ -123,9 +123,9 @@ impl DeviceMemory {
} }
} }
fn unregister(&mut self, kvm: &Kvm, slot: u32) -> Result<()> { fn unregister(&mut self, kvm_vm: &KvmVm, slot: u32) -> Result<()> {
if let Some(registration) = self.mappings.remove(&slot) { if let Some(registration) = self.mappings.remove(&slot) {
kvm.remove_memory_region(slot) kvm_vm.remove_memory_region(slot)
.map_err(Error::UnregisterMemoryFailed)?; .map_err(Error::UnregisterMemoryFailed)?;
self.free_addr_and_slot(registration.guest_addr, slot); self.free_addr_and_slot(registration.guest_addr, slot);
} }

View File

@ -1,54 +0,0 @@
use std::os::unix::io::{RawFd,AsRawFd};
use libc;
use crate::system::{Result,Error};
pub struct EventFd(RawFd);
const U64_SZ: usize = 8;
impl EventFd {
pub fn new() -> Result<EventFd> {
let fd = unsafe { libc::eventfd(0, 0) };
if fd < 0 {
return Err(Error::last_os_error());
}
Ok(EventFd(fd))
}
pub fn write(&self, v: u64) -> Result<()> {
let ret = unsafe { libc::write(self.0, &v as *const _ as *const libc::c_void, U64_SZ) };
if ret as usize != U64_SZ {
if ret < 0 {
return Err(Error::last_os_error())
}
return Err(Error::EventFdWrite);
}
Ok(())
}
pub fn read(&self) -> Result<u64> {
let mut v = 0u64;
let ret = unsafe { libc::read(self.0, &mut v as *mut _ as *mut libc::c_void, U64_SZ) };
if ret as usize != U64_SZ {
if ret < 0 {
return Err(Error::last_os_error());
}
return Err(Error::EventFdRead);
}
Ok(v)
}
}
impl Drop for EventFd {
fn drop(&mut self) {
let _ = unsafe { libc::close(self.0) };
}
}
impl AsRawFd for EventFd {
fn as_raw_fd(&self) -> RawFd {
self.0
}
}

View File

@ -5,7 +5,6 @@ use crate::system::errno::{Result,Error};
pub const IOC_SIZEBITS: u64 = 14; pub const IOC_SIZEBITS: u64 = 14;
pub const IOC_DIRBITS: u64 = 2; pub const IOC_DIRBITS: u64 = 2;
pub const IOC_NONE: u64 = 0;
pub const IOC_READ: u64 = 2; pub const IOC_READ: u64 = 2;
pub const IOC_WRITE: u64 = 1; pub const IOC_WRITE: u64 = 1;
pub const IOC_RDWR: u64 = IOC_READ | IOC_WRITE; pub const IOC_RDWR: u64 = IOC_READ | IOC_WRITE;
@ -29,18 +28,10 @@ macro_rules! ioc {
(($sz as u64 & $crate::system::ioctl::IOC_SIZEMASK) << $crate::system::ioctl::IOC_SIZESHIFT)) as ::libc::c_ulong) (($sz as u64 & $crate::system::ioctl::IOC_SIZEMASK) << $crate::system::ioctl::IOC_SIZESHIFT)) as ::libc::c_ulong)
} }
macro_rules! io {
($ty:expr, $nr:expr) => (ioc!($crate::system::ioctl::IOC_NONE, $ty, $nr, 0))
}
macro_rules! iow { macro_rules! iow {
($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::system::ioctl::IOC_WRITE, $ty, $nr, $sz)) ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::system::ioctl::IOC_WRITE, $ty, $nr, $sz))
} }
macro_rules! ior {
($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::system::ioctl::IOC_READ, $ty, $nr, $sz))
}
macro_rules! iorw { macro_rules! iorw {
($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::system::ioctl::IOC_RDWR, $ty, $nr, $sz)) ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::system::ioctl::IOC_RDWR, $ty, $nr, $sz))
} }

View File

@ -1,16 +1,16 @@
use std::sync::{Arc,RwLock}; use std::sync::{Arc,RwLock};
use crate::vm::io::IoDispatcher; use crate::vm::io::IoDispatcher;
use crate::kvm::Kvm;
use crate::memory::{AddressRange, MemoryManager}; use crate::memory::{AddressRange, MemoryManager};
use super::{VirtioDevice,VirtioDeviceOps,PciIrq}; use super::{VirtioDevice,VirtioDeviceOps,PciIrq};
use super::consts::*; use super::consts::*;
use super::pci::PciBus; use super::pci::PciBus;
use crate::virtio::Result; use crate::virtio::Result;
use std::iter; use std::iter;
use crate::vm::KvmVm;
pub struct VirtioBus { pub struct VirtioBus {
kvm: Kvm, kvm_vm: KvmVm,
memory: MemoryManager, memory: MemoryManager,
io_dispatcher: Arc<IoDispatcher>, io_dispatcher: Arc<IoDispatcher>,
pci_bus: Arc<RwLock<PciBus>>, pci_bus: Arc<RwLock<PciBus>>,
@ -18,9 +18,9 @@ pub struct VirtioBus {
} }
impl VirtioBus { impl VirtioBus {
pub fn new(memory: MemoryManager, io_dispatcher: Arc<IoDispatcher>, kvm: Kvm) -> VirtioBus { pub fn new(memory: MemoryManager, io_dispatcher: Arc<IoDispatcher>, kvm_vm: KvmVm) -> VirtioBus {
VirtioBus { VirtioBus {
kvm, kvm_vm,
memory, memory,
io_dispatcher: io_dispatcher.clone(), io_dispatcher: io_dispatcher.clone(),
pci_bus: PciBus::new(&io_dispatcher), pci_bus: PciBus::new(&io_dispatcher),
@ -41,7 +41,7 @@ pub struct VirtioDeviceConfig<'a> {
virtio_bus: &'a mut VirtioBus, virtio_bus: &'a mut VirtioBus,
device_type: u16, device_type: u16,
irq: u8, irq: u8,
kvm: Kvm, kvm_vm: KvmVm,
ops: Arc<RwLock<dyn VirtioDeviceOps>>, ops: Arc<RwLock<dyn VirtioDeviceOps>>,
mmio: AddressRange, mmio: AddressRange,
queue_sizes: Vec<usize>, queue_sizes: Vec<usize>,
@ -53,13 +53,13 @@ pub struct VirtioDeviceConfig<'a> {
impl <'a> VirtioDeviceConfig<'a> { impl <'a> VirtioDeviceConfig<'a> {
fn new(virtio_bus: &mut VirtioBus, device_type: u16, ops: Arc<RwLock<dyn VirtioDeviceOps>>) -> VirtioDeviceConfig { fn new(virtio_bus: &mut VirtioBus, device_type: u16, ops: Arc<RwLock<dyn VirtioDeviceOps>>) -> VirtioDeviceConfig {
let kvm = virtio_bus.kvm.clone(); let kvm_vm = virtio_bus.kvm_vm.clone();
let mmio = virtio_bus.pci_bus.write().unwrap().allocate_mmio_space(VIRTIO_MMIO_AREA_SIZE); let mmio = virtio_bus.pci_bus.write().unwrap().allocate_mmio_space(VIRTIO_MMIO_AREA_SIZE);
VirtioDeviceConfig { VirtioDeviceConfig {
virtio_bus, virtio_bus,
device_type, device_type,
irq: 0, irq: 0,
kvm, kvm_vm,
ops, ops,
mmio, mmio,
queue_sizes: Vec::new(), queue_sizes: Vec::new(),
@ -69,7 +69,7 @@ impl <'a> VirtioDeviceConfig<'a> {
} }
} }
pub fn kvm(&self) -> &Kvm { &self.kvm } pub fn kvm_vm(&self) -> &KvmVm { &self.kvm_vm }
pub fn ops(&self) -> Arc<RwLock<dyn VirtioDeviceOps>> { pub fn ops(&self) -> Arc<RwLock<dyn VirtioDeviceOps>> {
self.ops.clone() self.ops.clone()

View File

@ -1,12 +1,13 @@
use crate::memory::GuestRam; use crate::memory::GuestRam;
use std::sync::Arc; use std::sync::Arc;
use kvm_ioctls::{IoEventAddress, NoDatamatch};
use vmm_sys_util::eventfd::EventFd;
use super::VirtQueue; use super::VirtQueue;
use super::vring::Vring; use super::vring::Vring;
use super::virtqueue::InterruptLine; use super::virtqueue::InterruptLine;
use super::bus::VirtioDeviceConfig; use super::bus::VirtioDeviceConfig;
use crate::virtio::{Result, Error}; use crate::virtio::{Error, Result};
use crate::kvm::IoEventFd;
/// ///
/// Manages a set of virtqueues during device intitialization. /// Manages a set of virtqueues during device intitialization.
@ -17,7 +18,7 @@ pub struct VirtQueueConfig {
enabled_features: u64, enabled_features: u64,
vrings: Vec<Vring>, vrings: Vec<Vring>,
interrupt: Arc<InterruptLine>, interrupt: Arc<InterruptLine>,
events: Vec<Arc<IoEventFd>>, events: Vec<Arc<EventFd>>,
} }
impl VirtQueueConfig { impl VirtQueueConfig {
@ -110,12 +111,15 @@ impl VirtQueueConfig {
} }
} }
fn create_ioeventfds(conf: &VirtioDeviceConfig) -> Result<Vec<Arc<IoEventFd>>> { fn create_ioeventfds(conf: &VirtioDeviceConfig) -> Result<Vec<Arc<EventFd>>> {
let mut v = Vec::with_capacity(conf.num_queues()); let mut v = Vec::with_capacity(conf.num_queues());
let notify_base = conf.notify_mmio().base(); let notify_base = conf.notify_mmio().base();
for i in 0..conf.num_queues() { for i in 0..conf.num_queues() {
let evt = IoEventFd::new(conf.kvm(), notify_base + (4 * i as u64)) let evt = EventFd::new(0)
.map_err(Error::CreateEventFd)?;
let addr = IoEventAddress::Mmio(notify_base + (4 * i as u64));
conf.kvm_vm().vm_fd().register_ioevent(&evt, &addr, NoDatamatch)
.map_err(Error::CreateIoEventFd)?; .map_err(Error::CreateIoEventFd)?;
v.push(Arc::new(evt)); v.push(Arc::new(evt));
} }

View File

@ -1,29 +1,27 @@
use std::sync::atomic::{Ordering, AtomicUsize, AtomicBool}; use std::sync::atomic::{Ordering, AtomicUsize, AtomicBool};
use std::sync::Arc; use std::sync::Arc;
use std::os::unix::io::AsRawFd; use vmm_sys_util::eventfd::EventFd;
use crate::memory::GuestRam; use crate::memory::GuestRam;
use crate::kvm::Kvm;
use crate::virtio::{Result,Error}; use crate::virtio::{Result,Error};
use crate::system::EventFd;
use crate::kvm::IoEventFd;
use super::consts::*; use super::consts::*;
use super::vring::{Vring,Descriptor}; use super::vring::{Vring,Descriptor};
use super::bus::VirtioDeviceConfig; use super::bus::VirtioDeviceConfig;
use crate::virtio::chain::Chain; use crate::virtio::chain::Chain;
use crate::vm::KvmVm;
#[derive(Clone)] #[derive(Clone)]
pub struct VirtQueue { pub struct VirtQueue {
memory: GuestRam, memory: GuestRam,
vring: Vring, vring: Vring,
features: u64, features: u64,
ioeventfd: Arc<IoEventFd>, ioeventfd: Arc<EventFd>,
interrupt: Arc<InterruptLine>, interrupt: Arc<InterruptLine>,
closed: Arc<AtomicBool>, closed: Arc<AtomicBool>,
} }
impl VirtQueue { impl VirtQueue {
pub fn new(memory: GuestRam, vring: Vring, interrupt: Arc<InterruptLine>, ioeventfd: Arc<IoEventFd>) -> VirtQueue { pub fn new(memory: GuestRam, vring: Vring, interrupt: Arc<InterruptLine>, ioeventfd: Arc<EventFd>) -> VirtQueue {
VirtQueue { VirtQueue {
memory, memory,
vring, vring,
@ -120,7 +118,7 @@ impl VirtQueue {
self.vring.load_descriptor(idx) self.vring.load_descriptor(idx)
} }
pub fn ioevent(&self) -> &IoEventFd { pub fn ioevent(&self) -> &EventFd {
&self.ioeventfd &self.ioeventfd
} }
} }
@ -147,12 +145,13 @@ pub struct InterruptLine {
impl InterruptLine { impl InterruptLine {
pub fn from_config(conf: &VirtioDeviceConfig) -> Result<Arc<InterruptLine>> { pub fn from_config(conf: &VirtioDeviceConfig) -> Result<Arc<InterruptLine>> {
InterruptLine::new(conf.kvm(), conf.irq()) InterruptLine::new(conf.kvm_vm(), conf.irq())
} }
fn new(kvm: &Kvm, irq: u8) -> Result<Arc<InterruptLine>> { fn new(kvm_vm: &KvmVm, irq: u8) -> Result<Arc<InterruptLine>> {
let irqfd = EventFd::new().map_err(Error::CreateEventFd)?; let irqfd = EventFd::new(0)
kvm.irqfd(irqfd.as_raw_fd() as u32, irq as u32) .map_err(Error::CreateEventFd)?;
kvm_vm.vm_fd().register_irqfd(&irqfd, irq as u32)
.map_err(Error::IrqFd)?; .map_err(Error::IrqFd)?;
Ok(Arc::new(InterruptLine{ Ok(Arc::new(InterruptLine{
irqfd, irqfd,

View File

@ -1,4 +1,5 @@
use crate::kvm::{KvmVcpu, Kvm}; use kvm_bindings::CpuId;
use kvm_ioctls::VcpuFd;
pub use crate::vm::arch::x86::X86ArchSetup; pub use crate::vm::arch::x86::X86ArchSetup;
use crate::memory::MemoryManager; use crate::memory::MemoryManager;
@ -7,21 +8,20 @@ mod x86;
pub use x86::PCI_MMIO_RESERVED_BASE; pub use x86::PCI_MMIO_RESERVED_BASE;
pub use x86::KvmRegs;
pub use error::{Error,Result}; pub use error::{Error,Result};
use crate::vm::kernel_cmdline::KernelCmdLine; use crate::vm::kernel_cmdline::KernelCmdLine;
use crate::vm::VmConfig; use crate::vm::VmConfig;
use crate::virtio::PciIrq; use crate::virtio::PciIrq;
use crate::vm::kvm_vm::KvmVm;
pub fn create_setup(config: &VmConfig) -> X86ArchSetup { pub fn create_setup(config: &VmConfig) -> X86ArchSetup {
X86ArchSetup::create(config) X86ArchSetup::create(config)
} }
pub trait ArchSetup { pub trait ArchSetup {
fn open_kvm(&self) -> Result<Kvm>; fn create_memory(&mut self, kvm_vm: KvmVm) -> Result<MemoryManager>;
fn create_memory(&mut self, kvm: &Kvm) -> Result<MemoryManager>;
fn setup_memory(&mut self, cmdline: &KernelCmdLine, pci_irqs: &[PciIrq]) -> Result<()>; fn setup_memory(&mut self, cmdline: &KernelCmdLine, pci_irqs: &[PciIrq]) -> Result<()>;
fn setup_vcpu(&self, vcpu: &KvmVcpu) -> Result<()>; fn setup_vcpu(&self, vcpu: &VcpuFd, cpuid: CpuId) -> Result<()>;
} }

View File

@ -1,8 +1,6 @@
use kvm_bindings::CpuId;
use std::os::unix::io::RawFd; use kvm_ioctls::VcpuFd;
use crate::vm::arch::Result; use crate::vm::arch::{Error, Result};
use crate::kvm::KvmVcpu;
use crate::vm::arch::x86::ioctl::{KVM_GET_SUPPORTED_CPUID, KVM_SET_CPUID2, call_ioctl_with_ref, call_ioctl_with_mut_ref};
const EBX_CLFLUSH_CACHELINE: u32 = 8; // Flush a cache line size. const EBX_CLFLUSH_CACHELINE: u32 = 8; // Flush a cache line size.
const EBX_CLFLUSH_SIZE_SHIFT: u32 = 8; // Bytes flushed when executing CLFLUSH. const EBX_CLFLUSH_SIZE_SHIFT: u32 = 8; // Bytes flushed when executing CLFLUSH.
@ -16,11 +14,12 @@ const INTEL_EBX: u32 = u32::from_le_bytes([b'G', b'e', b'n', b'u']);
const INTEL_EDX: u32 = u32::from_le_bytes([b'i', b'n', b'e', b'I']); const INTEL_EDX: u32 = u32::from_le_bytes([b'i', b'n', b'e', b'I']);
const INTEL_ECX: u32 = u32::from_le_bytes([b'n', b't', b'e', b'l']); const INTEL_ECX: u32 = u32::from_le_bytes([b'n', b't', b'e', b'l']);
pub fn setup_cpuid(vcpu: &KvmVcpu) -> Result<()> { pub fn setup_cpuid(vcpu: &VcpuFd, cpuid: CpuId) -> Result<()> {
let mut cpuid = kvm_get_supported_cpuid(vcpu.sys_raw_fd())?; let mut cpuid = cpuid;
let cpu_id = 0u32; // first vcpu let cpu_id = 0u32; // first vcpu
for e in &mut cpuid { for e in cpuid.as_mut_slice() {
match e.function { match e.function {
0 => { 0 => {
e.ebx = INTEL_EBX; e.ebx = INTEL_EBX;
@ -57,69 +56,7 @@ pub fn setup_cpuid(vcpu: &KvmVcpu) -> Result<()> {
_ => {} _ => {}
} }
} }
kvm_set_cpuid2(vcpu.raw_fd(), cpuid) vcpu.set_cpuid2(&cpuid)
} .map_err(Error::SetupError)?;
Ok(())
pub fn kvm_get_supported_cpuid(sysfd: RawFd) -> Result<Vec<KvmCpuIdEntry>> {
let mut cpuid = KvmCpuId2::new();
call_ioctl_with_mut_ref("KVM_GET_SUPPORTED_CPUID", sysfd, KVM_GET_SUPPORTED_CPUID, &mut cpuid)?;
Ok(cpuid.get_entries())
}
pub fn kvm_set_cpuid2(cpufd: RawFd, entries: Vec<KvmCpuIdEntry>) -> Result<()> {
let cpuid = KvmCpuId2::new_from_entries(entries);
call_ioctl_with_ref("KVM_SET_CPUID2", cpufd, KVM_SET_CPUID2, &cpuid)
}
#[derive(Copy, Clone, Default)]
#[repr(C)]
pub struct KvmCpuIdEntry {
pub function: u32,
pub index: u32,
pub flags: u32,
pub eax: u32,
pub ebx: u32,
pub ecx: u32,
pub edx: u32,
padding: [u32; 3]
}
const KVM_CPUID_MAX_ENTRIES:usize = 256;
#[repr(C)]
pub struct KvmCpuId2 {
nent: u32,
padding: u32,
entries: [KvmCpuIdEntry; KVM_CPUID_MAX_ENTRIES]
}
impl KvmCpuId2 {
pub fn new() -> KvmCpuId2 {
KvmCpuId2 {
nent: KVM_CPUID_MAX_ENTRIES as u32,
padding: 0,
entries: [Default::default(); KVM_CPUID_MAX_ENTRIES],
}
}
pub fn new_from_entries(entries: Vec<KvmCpuIdEntry>) -> KvmCpuId2 {
let mut cpuid = KvmCpuId2::new();
let sz = entries.len();
assert!(sz <= KVM_CPUID_MAX_ENTRIES, "Too many cpuid entries");
for i in 0..sz {
cpuid.entries[i] = entries[i];
}
cpuid.nent = sz as u32;
cpuid
}
pub fn get_entries(&self) -> Vec<KvmCpuIdEntry> {
let mut entries = Vec::new();
let sz = self.nent as usize;
for i in 0..sz {
entries.push(self.entries[i]);
}
entries
}
} }

76
src/vm/arch/x86/gdt.rs Normal file
View File

@ -0,0 +1,76 @@
use std::fmt;
use kvm_bindings::kvm_segment;
pub struct GdtEntry(u64);
impl GdtEntry {
pub fn new(flags: u16, base: u32, limit: u32) -> Self {
let flags = flags as u64;
let base = base as u64;
let limit = limit as u64;
GdtEntry(
((base & 0xff00_0000_u64) << (56 - 24))
| ((flags & 0x0000_f0ff_u64) << 40)
| ((limit & 0x000f_0000_u64) << (48 - 16))
| ((base & 0x00ff_ffff_u64) << 16)
| (limit & 0x0000_ffff_u64))
}
fn get_base(&self) -> u64 {
(((self.0) & 0xFF00_0000_0000_0000) >> 32)
| (((self.0) & 0x0000_00ff_ffff_0000) >> 16)
}
fn get_limit(&self) -> u32 {
((((self.0) & 0x000f_0000_0000_0000) >> 32)
| ((self.0) & 0x0000_0000_0000_ffff)) as u32
}
const BIT_G: usize = 55;
const BIT_DB: usize = 54;
const BIT_L: usize = 53;
const BIT_AVL: usize = 52;
const BIT_P: usize = 47;
const BIT_S: usize = 44;
const BITS_DPL: usize = 45;
const BITS_TYPE: usize = 40;
fn get_type(&self) -> u8 {
((self.0 & 0x0000_0f00_0000_0000) >> GdtEntry::BITS_TYPE) as u8
}
fn get_dpl(&self) -> u8 {
((self.0 & 0x0000_6000_0000_0000) >> GdtEntry::BITS_DPL) as u8
}
fn get_bit(&self, bit: usize) -> u8 {
((self.0 & (1u64 << bit)) >> bit) as u8
}
pub fn kvm_segment(&self, table_index: u16) -> kvm_segment {
kvm_segment {
base: self.get_base(),
limit: self.get_limit(),
selector: table_index * 8,
type_: self.get_type(),
present: self.get_bit(GdtEntry::BIT_P),
dpl: self.get_dpl(),
db: self.get_bit(GdtEntry::BIT_DB),
s: self.get_bit(GdtEntry::BIT_S),
l: self.get_bit(GdtEntry::BIT_L),
g: self.get_bit(GdtEntry::BIT_G),
avl: self.get_bit(GdtEntry::BIT_AVL),
padding: 0,
unusable: if self.get_bit(GdtEntry::BIT_P) == 0 { 1 } else { 0 },
}
}
}
impl fmt::Debug for GdtEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "(base: {:x} limit {:x} type: {:x} p: {} dpl: {})",
self.get_base(), self.get_limit(), self.get_type(), self.get_bit(GdtEntry::BIT_P), self.get_dpl())
}
}

View File

@ -1,50 +1,44 @@
use std::os::unix::io::RawFd; use kvm_bindings::kvm_lapic_state;
use kvm_ioctls::VcpuFd;
use crate::system::ioctl::{ioctl_with_mut_ref, ioctl_with_ref};
use crate::vm::arch::{Error, Result}; use crate::vm::arch::{Error, Result};
use crate::vm::arch::x86::ioctl::{KVM_GET_LAPIC, KVM_SET_LAPIC};
#[repr(C)] const APIC_MODE_EXTINT: u32 = 0x7;
pub struct KvmLapicState { const APIC_MODE_NMI: u32 = 0x4;
pub regs: [u8; 1024]
}
impl KvmLapicState {
pub fn new() -> KvmLapicState {
KvmLapicState { regs: [0; 1024] }
}
}
pub fn kvm_get_lapic(cpufd: RawFd) -> Result<KvmLapicState> {
let mut lapic_state = KvmLapicState::new();
unsafe {
ioctl_with_mut_ref(cpufd, KVM_GET_LAPIC, &mut lapic_state)
.map_err(|e| Error::IoctlError("KVM_GET_LAPIC", e))?;
}
Ok(lapic_state)
}
pub fn kvm_set_lapic(cpufd: RawFd, lapic_state: &KvmLapicState) -> Result<()> {
unsafe {
ioctl_with_ref(cpufd, KVM_SET_LAPIC, lapic_state)
.map_err(|e| Error::IoctlError("KVM_SET_LAPIC", e))?;
}
Ok(())
}
const APIC_MODE_EXTINT: u8 = 0x7;
const APIC_MODE_NMI: u8 = 0x4;
const APIC_LVT_LINT0_OFFSET: usize = 0x350; const APIC_LVT_LINT0_OFFSET: usize = 0x350;
const APIC_LVT_LINT1_OFFSET: usize = 0x360; const APIC_LVT_LINT1_OFFSET: usize = 0x360;
pub fn setup_lapic(cpufd: RawFd) -> Result<()> { fn get_klapic_reg(klapic: &kvm_lapic_state, offset: usize) -> u32 {
let mut lapic = kvm_get_lapic(cpufd)?; let mut bytes = [0u8; 4];
// delivery mode for idx in 0..4 {
lapic.regs[APIC_LVT_LINT0_OFFSET + 1] &= 0xF8; bytes[idx] = klapic.regs[offset + idx] as u8;
lapic.regs[APIC_LVT_LINT0_OFFSET + 1] |= APIC_MODE_EXTINT; }
lapic.regs[APIC_LVT_LINT1_OFFSET + 1] &= 0xF8; u32::from_le_bytes(bytes)
lapic.regs[APIC_LVT_LINT1_OFFSET + 1] |= APIC_MODE_NMI; }
kvm_set_lapic(cpufd, &lapic)
fn set_klapic_reg(klapic: &mut kvm_lapic_state, offset: usize, value: u32) {
let bytes = value.to_le_bytes();
for idx in 0..4 {
klapic.regs[offset + idx] = bytes[idx] as i8;
}
}
fn set_apic_delivery_mode(reg: u32, mode: u32) -> u32 {
(reg & !0x700) | (mode << 8)
}
pub fn setup_lapic(vcpu: &VcpuFd) -> Result<()> {
let mut lapic = vcpu.get_lapic()
.map_err(Error::SetupError)?;
let lvt_lint0 = get_klapic_reg(&lapic, APIC_LVT_LINT0_OFFSET);
set_klapic_reg(&mut lapic, APIC_LVT_LINT0_OFFSET, set_apic_delivery_mode(lvt_lint0, APIC_MODE_EXTINT));
let lvt_lint1 = get_klapic_reg(&lapic, APIC_LVT_LINT1_OFFSET);
set_klapic_reg(&mut lapic, APIC_LVT_LINT1_OFFSET, set_apic_delivery_mode(lvt_lint1, APIC_MODE_NMI));
vcpu.set_lapic(&lapic)
.map_err(Error::SetupError)?;
Ok(())
} }

View File

@ -1,44 +0,0 @@
use std::os::unix::io::RawFd;
use libc::{self, c_ulong};
use crate::system::ioctl::{ioctl_with_ref, ioctl_with_mut_ref, ioctl_with_val};
use crate::vm::arch::{Error,Result};
const KVMIO: u64 = 0xAE;
pub const KVM_GET_SUPPORTED_CPUID: libc::c_ulong = iorw! (KVMIO, 0x05, 8);
pub const KVM_SET_CPUID2: libc::c_ulong = iow! (KVMIO, 0x90, 8);
pub const KVM_SET_TSS_ADDR: c_ulong = io! (KVMIO, 0x47);
pub const KVM_CREATE_PIT2: c_ulong = iow! (KVMIO, 0x77, 64);
pub const KVM_SET_FPU: c_ulong = iow! (KVMIO, 0x8d, 416);
pub const KVM_SET_MSRS: c_ulong = iow! (KVMIO, 0x89, 8);
pub const KVM_GET_SREGS: c_ulong = ior! (KVMIO, 0x83, 312);
pub const KVM_SET_SREGS: c_ulong = iow! (KVMIO, 0x84, 312);
pub const KVM_GET_LAPIC: c_ulong = ior! (KVMIO, 0x8e, 1024);
pub const KVM_SET_LAPIC: c_ulong = iow! (KVMIO, 0x8f, 1024);
pub fn call_ioctl_with_ref<T>(name: &'static str, fd: RawFd, request: c_ulong, arg: &T) -> Result<()> {
unsafe {
ioctl_with_ref(fd, request, arg)
.map_err(|e| Error::IoctlError(name, e))?;
Ok(())
}
}
pub fn call_ioctl_with_mut_ref<T>(name: &'static str, fd: RawFd, request: c_ulong, arg: &mut T) -> Result<()> {
unsafe {
ioctl_with_mut_ref(fd, request, arg)
.map_err(|e| Error::IoctlError(name, e))?;
Ok(())
}
}
pub fn call_ioctl_with_val(name: &'static str, fd: RawFd, request: c_ulong, val: c_ulong) -> Result<()> {
unsafe {
ioctl_with_val(fd, request, val)
.map_err(|e| Error::IoctlError(name, e))?;
Ok(())
}
}

View File

@ -1,50 +0,0 @@
use std::os::unix::io::RawFd;
use crate::kvm::{Kvm, KVM_CAP_IOEVENTFD, KVM_CAP_PIT2, KVM_CAP_IRQ_INJECT_STATUS, KVM_CAP_IRQ_ROUTING, KVM_CAP_EXT_CPUID, KVM_CAP_SET_TSS_ADDR, KVM_CAP_USER_MEMORY, KVM_CAP_HLT, KVM_CAP_IRQCHIP};
use crate::vm::arch::{Result,Error};
use libc::c_ulong;
use crate::vm::arch::x86::ioctl::{
call_ioctl_with_ref, call_ioctl_with_val, KVM_CREATE_PIT2, KVM_SET_TSS_ADDR
};
static REQUIRED_EXTENSIONS: &[u32] = &[
KVM_CAP_IRQCHIP,
KVM_CAP_HLT,
KVM_CAP_USER_MEMORY,
KVM_CAP_SET_TSS_ADDR,
KVM_CAP_EXT_CPUID,
KVM_CAP_IRQ_ROUTING,
KVM_CAP_IRQ_INJECT_STATUS,
KVM_CAP_PIT2,
KVM_CAP_IOEVENTFD,
];
pub fn x86_open_kvm() -> Result<Kvm> {
let kvm = Kvm::open(REQUIRED_EXTENSIONS)
.map_err(Error::KvmError)?;
kvm.create_irqchip().map_err(Error::KvmError)?;
kvm_set_tss_addr(kvm.vmfd(), 0xFFFbd000)?;
kvm_create_pit2(kvm.vmfd())?;
Ok(kvm)
}
#[repr(C)]
struct KvmPitConfig {
flags: u32,
padding: [u32; 15],
}
impl KvmPitConfig {
pub fn new(flags: u32) -> KvmPitConfig {
KvmPitConfig { flags, padding: [0; 15] }
}
}
fn kvm_create_pit2(vmfd: RawFd) -> Result<()> {
let pit_config = KvmPitConfig::new(0);
call_ioctl_with_ref("KVM_CREATE_PIT2", vmfd, KVM_CREATE_PIT2, &pit_config)
}
fn kvm_set_tss_addr(vmfd: RawFd, addr: u32) -> Result<()> {
call_ioctl_with_val("KVM_SET_TSS_ADDR", vmfd, KVM_SET_TSS_ADDR, addr as c_ulong)
}

View File

@ -1,4 +1,3 @@
use crate::kvm::Kvm;
use crate::memory::{MemoryManager, MemoryRegion, GuestRam}; use crate::memory::{MemoryManager, MemoryRegion, GuestRam};
use crate::vm::arch::{Error, Result}; use crate::vm::arch::{Error, Result};
use std::cmp; use std::cmp;
@ -7,6 +6,7 @@ use crate::vm::arch::x86::kernel::{load_pm_kernel, KERNEL_CMDLINE_ADDRESS};
use crate::system; use crate::system;
use crate::vm::arch::x86::mptable::setup_mptable; use crate::vm::arch::x86::mptable::setup_mptable;
use crate::virtio::PciIrq; use crate::virtio::PciIrq;
use crate::vm::KvmVm;
pub const HIMEM_BASE: u64 = 1 << 32; pub const HIMEM_BASE: u64 = 1 << 32;
pub const PCI_MMIO_RESERVED_SIZE: usize = 512 << 20; pub const PCI_MMIO_RESERVED_SIZE: usize = 512 << 20;
@ -16,20 +16,20 @@ pub const PCI_MMIO_RESERVED_BASE: u64 = HIMEM_BASE - PCI_MMIO_RESERVED_SIZE as u
pub fn x86_setup_memory_regions(memory: &mut MemoryManager, ram_size: usize) -> Result<()> { pub fn x86_setup_memory_regions(memory: &mut MemoryManager, ram_size: usize) -> Result<()> {
let mut regions = Vec::new(); let mut regions = Vec::new();
let lowmem_sz = cmp::min(ram_size, PCI_MMIO_RESERVED_BASE as usize); let lowmem_sz = cmp::min(ram_size, PCI_MMIO_RESERVED_BASE as usize);
regions.push(create_region(memory.kvm(), 0, lowmem_sz, 0)?); regions.push(create_region(memory.kvm_vm(), 0, lowmem_sz, 0)?);
if lowmem_sz < ram_size { if lowmem_sz < ram_size {
let himem_sz = ram_size - lowmem_sz; let himem_sz = ram_size - lowmem_sz;
regions.push(create_region(memory.kvm(), HIMEM_BASE, himem_sz, 1)?); regions.push(create_region(memory.kvm_vm(), HIMEM_BASE, himem_sz, 1)?);
} }
memory.set_ram_regions(regions); memory.set_ram_regions(regions);
Ok(()) Ok(())
} }
fn create_region(kvm: &Kvm, base: u64, size: usize, slot: u32) -> Result<MemoryRegion> { fn create_region(kvm_vm: &KvmVm, base: u64, size: usize, slot: u32) -> Result<MemoryRegion> {
let mr = MemoryRegion::new(base, size) let mr = MemoryRegion::new(base, size)
.map_err(Error::MemoryRegionCreate)?; .map_err(Error::MemoryRegionCreate)?;
kvm.add_memory_region(slot, base, mr.base_address(), size) kvm_vm.add_memory_region(slot, base, mr.base_address(), size)
.map_err(Error::MemoryRegister)?; .map_err(Error::MemoryRegister)?;
Ok(mr) Ok(mr)
} }

View File

@ -1,13 +1,11 @@
mod cpuid; mod cpuid;
mod gdt;
mod interrupts; mod interrupts;
mod kvm;
mod memory; mod memory;
mod mptable; mod mptable;
mod registers; mod registers;
mod kernel; mod kernel;
mod ioctl;
mod setup; mod setup;
pub use setup::X86ArchSetup; pub use setup::X86ArchSetup;
pub use memory::PCI_MMIO_RESERVED_BASE; pub use memory::PCI_MMIO_RESERVED_BASE;
pub use registers::KvmRegs;

View File

@ -1,12 +1,9 @@
use std::fmt; use kvm_bindings::{kvm_fpu, kvm_msr_entry, kvm_regs, Msrs};
use std::os::unix::io::RawFd; use kvm_ioctls::VcpuFd;
use crate::kvm::KvmVcpu; use crate::vm::arch::{Error, Result};
use crate::vm::arch::{Result, Error}; use crate::vm::arch::x86::gdt::GdtEntry;
use crate::vm::arch::x86::kernel::KERNEL_ZERO_PAGE; use crate::vm::arch::x86::kernel::KERNEL_ZERO_PAGE;
use crate::vm::arch::x86::ioctl::{
call_ioctl_with_ref, KVM_SET_FPU, KVM_SET_MSRS, call_ioctl_with_mut_ref, KVM_GET_SREGS, KVM_SET_SREGS
};
const MSR_IA32_SYSENTER_CS: u32 = 0x00000174; const MSR_IA32_SYSENTER_CS: u32 = 0x00000174;
const MSR_IA32_SYSENTER_ESP: u32 = 0x00000175; const MSR_IA32_SYSENTER_ESP: u32 = 0x00000175;
@ -21,27 +18,38 @@ const MSR_IA32_MISC_ENABLE: u32 = 0x000001a0;
const MSR_IA32_MISC_ENABLE_FAST_STRING: u64 = 0x01; const MSR_IA32_MISC_ENABLE_FAST_STRING: u64 = 0x01;
pub fn setup_fpu(vcpu: &KvmVcpu) -> Result<()> { pub fn setup_fpu(vcpu: &VcpuFd) -> Result<()> {
let mut fpu = KvmFpu::new(); let fpu = kvm_fpu {
fpu.fcw = 0x37f; fcw: 0x37f,
fpu.mxcsr = 0x1f80; mxcsr: 0x1f80,
kvm_set_fpu(vcpu.raw_fd(), &fpu)?; ..Default::default()
};
vcpu.set_fpu(&fpu)
.map_err(Error::SetupError)?;
Ok(()) Ok(())
} }
pub fn setup_msrs(vcpu: &KvmVcpu) -> Result<()> { pub fn setup_msrs(vcpu: &VcpuFd) -> Result<()> {
let mut msrs = KvmMsrs::new(); let msr = | index, data| kvm_msr_entry {
msrs.add(MSR_IA32_SYSENTER_CS, 0); index, data, ..Default::default()
msrs.add(MSR_IA32_SYSENTER_ESP, 0); };
msrs.add(MSR_IA32_SYSENTER_EIP, 0); let entries = vec![
msrs.add(MSR_STAR, 0); msr(MSR_IA32_SYSENTER_CS, 0),
msrs.add(MSR_CSTAR, 0); msr(MSR_IA32_SYSENTER_ESP, 0),
msrs.add(MSR_KERNEL_GS_BASE, 0); msr(MSR_IA32_SYSENTER_EIP, 0),
msrs.add(MSR_SYSCALL_MASK, 0); msr(MSR_STAR, 0),
msrs.add(MSR_LSTAR, 0); msr(MSR_CSTAR, 0),
msrs.add(MSR_IA32_TSC, 0); msr(MSR_KERNEL_GS_BASE, 0),
msrs.add(MSR_IA32_MISC_ENABLE, MSR_IA32_MISC_ENABLE_FAST_STRING); msr(MSR_SYSCALL_MASK, 0),
kvm_set_msrs(vcpu.raw_fd(), &msrs)?; msr(MSR_LSTAR, 0),
msr(MSR_IA32_TSC, 0),
msr(MSR_IA32_MISC_ENABLE, MSR_IA32_MISC_ENABLE_FAST_STRING),
];
let msrs = Msrs::from_entries(&entries)
.expect("Failed to create msr entries");
vcpu.set_msrs(&msrs)
.map_err(Error::SetupError)?;
Ok(()) Ok(())
} }
@ -58,19 +66,23 @@ const X86_CR4_PAE: u64 = 0x20;
const EFER_LME: u64 = 0x100; const EFER_LME: u64 = 0x100;
const EFER_LMA: u64 = 1 << 10; const EFER_LMA: u64 = 1 << 10;
pub fn setup_pm_sregs(vcpu: &KvmVcpu) -> Result<()> { pub fn setup_pm_sregs(vcpu: &VcpuFd) -> Result<()> {
let code = KvmSegment::new(0, 0xfffff, 1 * 8, 0xa09b); let code = GdtEntry::new(0xa09b, 0, 0xFFFFF)
let data = KvmSegment::new(0, 0xfffff, 2 * 8, 0xc093); .kvm_segment(1);
let tss = KvmSegment::new(0, 0xfffff, 3 * 8, 0x808b); let data = GdtEntry::new(0xc093, 0, 0xFFFFF)
.kvm_segment(2);
let tss = GdtEntry::new(0x808b, 0, 0xFFFFF)
.kvm_segment(3);
let mut regs = kvm_get_sregs(vcpu.raw_fd())?; let mut regs = vcpu.get_sregs()
.map_err(Error::SetupError)?;
regs.gdt.base = BOOT_GDT_OFFSET as u64; regs.gdt.base = BOOT_GDT_OFFSET as u64;
regs.gdt.limit = 32 - 1; regs.gdt.limit = 32 - 1;
regs.itd.base = BOOT_IDT_OFFSET as u64; regs.idt.base = BOOT_IDT_OFFSET as u64;
regs.itd.limit = 8 - 1; regs.idt.limit = 8 - 1;
regs.cs = code; regs.cs = code;
regs.ds = data; regs.ds = data;
@ -89,226 +101,24 @@ pub fn setup_pm_sregs(vcpu: &KvmVcpu) -> Result<()> {
regs.cr0 |= X86_CR0_PG; regs.cr0 |= X86_CR0_PG;
regs.efer |= EFER_LMA; regs.efer |= EFER_LMA;
kvm_set_sregs(vcpu.raw_fd(), &regs)?; vcpu.set_sregs(&regs)
.map_err(Error::SetupError)?;
Ok(()) Ok(())
} }
pub fn setup_pm_regs(vcpu: &KvmVcpu, kernel_entry: u64) -> Result<()> { pub fn setup_pm_regs(vcpu: &VcpuFd, kernel_entry: u64) -> Result<()> {
let mut regs = KvmRegs::new(); let regs = kvm_regs {
regs.rflags = 0x0000000000000002; rflags: 0x0000000000000002,
regs.rip = kernel_entry; rip: kernel_entry,
regs.rsp = BOOT_STACK; rsp: BOOT_STACK,
regs.rbp = BOOT_STACK; rbp: BOOT_STACK,
regs.rsi = KERNEL_ZERO_PAGE; rsi: KERNEL_ZERO_PAGE,
..Default::default()
};
vcpu.set_regs(&regs) vcpu.set_regs(&regs)
.map_err(Error::KvmError)?; .map_err(Error::SetupError)?;
Ok(()) Ok(())
} }
#[derive(Copy)]
#[repr(C)]
pub struct KvmFpu {
fpr: [u8; 128],
pub fcw: u16,
fsw: u16,
ftwx: u8,
pad1: u8,
last_opcode: u16,
last_ip: u64,
last_dp: u64,
xmm: [u8; 256],
pub mxcsr: u32,
pad2: u32,
}
impl Clone for KvmFpu {
fn clone(&self) -> KvmFpu { *self }
}
impl KvmFpu {
pub fn new() -> KvmFpu {
KvmFpu {
fpr: [0; 128],
fcw: 0,
fsw: 0,
ftwx: 0, pad1: 0,
last_opcode: 0,
last_ip: 0,
last_dp: 0,
xmm: [0; 256],
mxcsr: 0,
pad2: 0
}
}
}
pub fn kvm_set_fpu(cpufd: RawFd, fpu: &KvmFpu) -> Result<()> {
call_ioctl_with_ref("KVM_SET_FPU", cpufd, KVM_SET_FPU, fpu)
}
#[derive(Copy, Clone, Default)]
#[repr(C)]
struct KvmMsrEntry {
index: u32,
reserved: u32,
data: u64
}
#[repr(C)]
pub struct KvmMsrs {
nent: u32,
padding: u32,
entries: [KvmMsrEntry; 100]
}
impl KvmMsrs {
pub fn new() -> KvmMsrs {
KvmMsrs{ nent: 0, padding: 0, entries: [Default::default(); 100]}
}
pub fn add(&mut self, index: u32, data: u64) {
self.entries[self.nent as usize].index = index;
self.entries[self.nent as usize].data = data;
self.nent += 1;
}
}
pub fn kvm_set_msrs(cpufd: RawFd, msrs: &KvmMsrs) -> Result<()> {
call_ioctl_with_ref("KVM_SET_MSRS", cpufd, KVM_SET_MSRS, msrs)
}
#[derive(Copy, Clone, Default)]
#[repr(C)]
pub struct KvmSegment {
base: u64,
limit: u32,
selector: u16,
stype: u8,
present: u8,
dpl: u8,
db: u8,
s: u8,
l: u8,
g: u8,
avl: u8,
unusable: u8,
padding: u8,
}
impl KvmSegment {
pub fn new(base: u64, limit: u32, selector: u16, flags: u16) -> KvmSegment {
let mut seg = KvmSegment{ ..Default::default() };
seg.setup(base, limit, selector, flags);
seg
}
pub fn setup(&mut self, base: u64, limit: u32, selector: u16, flags: u16) {
self.base = base;
self.limit = limit;
self.selector = selector;
self.stype = (flags & 0xF) as u8;
self.present = ((flags >> 7) & 0x1) as u8;
self.dpl = ((flags >> 5) & 0x3) as u8;
self.db = ((flags >> 14) & 0x1) as u8;
self.s = ((flags >> 4) & 0x1) as u8;
self.l = ((flags >> 13) & 0x1) as u8;
self.g = ((flags >> 15) & 0x1) as u8;
self.avl = ((flags >> 12) & 0x1) as u8;
self.unusable = if self.present == 1 { 0 } else { 1 }
}
}
impl fmt::Debug for KvmSegment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "(base: {:x} limit {:x} selector: {:x} type: {:x} p: {} dpl: {} db: {} s: {} l: {} g: {} avl: {} unuse: {})",
self.base, self.limit, self.selector, self.stype, self.present, self.dpl, self.db, self.s, self.l, self.g, self.avl, self.unusable)
}
}
#[derive(Copy, Clone, Default)]
#[repr(C)]
pub struct KvmDtable {
pub base: u64,
pub limit: u16,
padding: [u16; 3],
}
impl fmt::Debug for KvmDtable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "(base: {:x} limit {:x})", self.base, self.limit)
}
}
#[derive(Copy, Clone, Default)]
#[repr(C)]
pub struct KvmSRegs {
pub cs: KvmSegment,
pub ds: KvmSegment,
pub es: KvmSegment,
pub fs: KvmSegment,
pub gs: KvmSegment,
pub ss: KvmSegment,
pub tr: KvmSegment,
pub ldt: KvmSegment,
pub gdt: KvmDtable,
pub itd: KvmDtable,
pub cr0: u64,
pub cr2: u64,
pub cr3: u64,
pub cr4: u64,
pub cr8: u64,
pub efer: u64,
pub apic_base: u64,
pub interrupt_bitmap: [u64; 4],
}
impl fmt::Debug for KvmSRegs {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "cs: {:?}\nds: {:?}\nes: {:?}\nfs: {:?}\n", self.cs, self.ds, self.es, self.fs)?;
write!(f, "gs: {:?}\nss: {:?}\ntr: {:?}\nldt: {:?}\n", self.gs, self.ss, self.tr, self.ldt)?;
write!(f, "gdt: {:?} itd: {:?}\n", self.gdt, self.itd)?;
write!(f, "cr0: {:x} cr2: {:x} cr3: {:x} cr4: {:x}\n", self.cr0, self.cr2, self.cr3, self.cr4)?;
write!(f, "efer: {:x} apic_base: {:x}\n", self.efer, self.apic_base)
}
}
impl KvmSRegs {
pub fn new() -> KvmSRegs {
KvmSRegs { ..Default::default() }
}
}
pub fn kvm_get_sregs(cpufd: RawFd) -> Result<KvmSRegs> {
let mut sregs = KvmSRegs::new();
call_ioctl_with_mut_ref("KVM_GET_SREGS", cpufd, KVM_GET_SREGS, &mut sregs)?;
Ok(sregs)
}
pub fn kvm_set_sregs(cpufd: RawFd, sregs: &KvmSRegs) -> Result<()> {
call_ioctl_with_ref("KVM_SET_SREGS", cpufd, KVM_SET_SREGS, sregs)
}
#[derive(Copy, Clone, Default)]
#[repr(C)]
pub struct KvmRegs {
pub rax: u64, pub rbx: u64, pub rcx: u64, pub rdx: u64,
pub rsi: u64, pub rdi: u64, pub rsp: u64, pub rbp: u64,
pub r8: u64, pub r9: u64, pub r10: u64, pub r11: u64,
pub r12: u64, pub r13: u64, pub r14: u64, pub r15: u64,
pub rip: u64, pub rflags: u64,
}
impl fmt::Debug for KvmRegs {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "rax 0x{:x} rbx 0x{:x} rcx 0x{:x} rdx 0x{:x}\n", self.rax, self.rbx, self.rcx, self.rdx)?;
write!(f, "rsi 0x{:x} rdi 0x{:x} rsp 0x{:x} rbp 0x{:x}\n", self.rsi, self.rdi, self.rsp, self.rbp)?;
write!(f, "r8 0x{:x} r9 0x{:x} r10 0x{:x} r11 0x{:x}\n", self.r8, self.r9, self.r10, self.r11)?;
write!(f, "r12 0x{:x} r13 0x{:x} r14 0x{:x} r15 0x{:x}\n", self.r12, self.r13, self.r14, self.r15)?;
write!(f, "rip 0x{:x} rflags 0x{:x}\n", self.rip, self.rflags)
}
}
impl KvmRegs {
pub fn new() -> KvmRegs {
KvmRegs { ..Default::default() }
}
}

View File

@ -1,15 +1,16 @@
use kvm_bindings::CpuId;
use kvm_ioctls::VcpuFd;
use crate::memory::{MemoryManager, GuestRam, SystemAllocator, AddressRange}; use crate::memory::{MemoryManager, GuestRam, SystemAllocator, AddressRange};
use crate::vm::VmConfig; use crate::vm::VmConfig;
use crate::vm::arch::{ArchSetup, Error, Result}; use crate::vm::arch::{ArchSetup, Error, Result};
use crate::vm::kernel_cmdline::KernelCmdLine; use crate::vm::kernel_cmdline::KernelCmdLine;
use crate::virtio::PciIrq; use crate::virtio::PciIrq;
use crate::kvm::{Kvm, KvmVcpu};
use crate::vm::arch::x86::kvm::x86_open_kvm;
use crate::vm::arch::x86::memory::{x86_setup_memory_regions, x86_setup_memory}; use crate::vm::arch::x86::memory::{x86_setup_memory_regions, x86_setup_memory};
use crate::vm::arch::x86::cpuid::setup_cpuid; use crate::vm::arch::x86::cpuid::setup_cpuid;
use crate::vm::arch::x86::registers::{setup_pm_sregs, setup_pm_regs, setup_fpu, setup_msrs}; use crate::vm::arch::x86::registers::{setup_pm_sregs, setup_pm_regs, setup_fpu, setup_msrs};
use crate::vm::arch::x86::interrupts::setup_lapic; use crate::vm::arch::x86::interrupts::setup_lapic;
use crate::vm::arch::x86::kernel::KVM_KERNEL_LOAD_ADDRESS; use crate::vm::arch::x86::kernel::KVM_KERNEL_LOAD_ADDRESS;
use crate::vm::kvm_vm::KvmVm;
pub struct X86ArchSetup { pub struct X86ArchSetup {
ram_size: usize, ram_size: usize,
@ -40,16 +41,12 @@ fn get_base_dev_pfn(mem_size: u64) -> u64 {
} }
impl ArchSetup for X86ArchSetup { impl ArchSetup for X86ArchSetup {
fn open_kvm(&self) -> Result<Kvm> { fn create_memory(&mut self, kvm_vm: KvmVm) -> Result<MemoryManager> {
x86_open_kvm()
}
fn create_memory(&mut self, kvm: &Kvm) -> Result<MemoryManager> {
let ram = GuestRam::new(self.ram_size); let ram = GuestRam::new(self.ram_size);
let dev_addr_start = get_base_dev_pfn(self.ram_size as u64) * 4096; let dev_addr_start = get_base_dev_pfn(self.ram_size as u64) * 4096;
let dev_addr_size = u64::max_value() - dev_addr_start; let dev_addr_size = u64::max_value() - dev_addr_start;
let allocator = SystemAllocator::new(AddressRange::new(dev_addr_start,dev_addr_size as usize)); let allocator = SystemAllocator::new(AddressRange::new(dev_addr_start,dev_addr_size as usize));
let mut mm = MemoryManager::new(kvm.clone(), ram, allocator, self.use_drm) let mut mm = MemoryManager::new(kvm_vm, ram, allocator, self.use_drm)
.map_err(Error::MemoryManagerCreate)?; .map_err(Error::MemoryManagerCreate)?;
x86_setup_memory_regions(&mut mm, self.ram_size)?; x86_setup_memory_regions(&mut mm, self.ram_size)?;
self.memory = Some(mm.clone()); self.memory = Some(mm.clone());
@ -62,12 +59,15 @@ impl ArchSetup for X86ArchSetup {
Ok(()) Ok(())
} }
fn setup_vcpu(&self, vcpu: &KvmVcpu) -> Result<()> { fn setup_vcpu(&self, vcpu_fd: &VcpuFd, cpuid: CpuId) -> Result<()> {
setup_cpuid(vcpu)?; setup_cpuid(vcpu_fd, cpuid)?;
setup_pm_sregs(vcpu)?; setup_pm_sregs(vcpu_fd)?;
setup_pm_regs(&vcpu, KVM_KERNEL_LOAD_ADDRESS)?; setup_pm_regs(&vcpu_fd, KVM_KERNEL_LOAD_ADDRESS)?;
setup_fpu(vcpu)?; setup_fpu(vcpu_fd)?;
setup_msrs(vcpu)?; setup_msrs(vcpu_fd)?;
setup_lapic(vcpu.raw_fd()) setup_lapic(vcpu_fd)?;
Ok(())
} }
} }

View File

@ -124,7 +124,7 @@ impl VmConfig {
} }
} }
let mut setup = self.setup(); let mut setup = self.setup();
let vm = match setup.create_vm() { let mut vm = match setup.create_vm() {
Ok(vm) => vm, Ok(vm) => vm,
Err(err) => { Err(err) => {
warn!("Failed to create VM: {}", err); warn!("Failed to create VM: {}", err);

View File

@ -1,4 +1,5 @@
use std::sync::{Arc,RwLock,RwLockWriteGuard}; use std::sync::{Arc,RwLock,RwLockWriteGuard};
use vmm_sys_util::eventfd::EventFd;
use crate::memory::AddressRange; use crate::memory::AddressRange;
pub trait IoPortOps: Send+Sync { pub trait IoPortOps: Send+Sync {
@ -32,7 +33,7 @@ impl IoPortOps for IoPortPS2Control {
fn io_in(&mut self, _port: u16, _size: usize) -> u32 { 0x02 } fn io_in(&mut self, _port: u16, _size: usize) -> u32 { 0x02 }
} }
struct IoPortFakeI8042(bool); struct IoPortFakeI8042(bool, EventFd);
impl IoPortOps for IoPortFakeI8042 { impl IoPortOps for IoPortFakeI8042 {
fn io_in(&mut self, port: u16, _size: usize) -> u32 { fn io_in(&mut self, port: u16, _size: usize) -> u32 {
@ -47,6 +48,9 @@ impl IoPortOps for IoPortFakeI8042 {
fn io_out(&mut self, port: u16, _size: usize, val: u32) { fn io_out(&mut self, port: u16, _size: usize, val: u32) {
if port == 0x64 && val == 0xfe && !self.0 { if port == 0x64 && val == 0xfe && !self.0 {
self.0 = true; self.0 = true;
if let Err(err) = self.1.write(1) {
warn!("Error triggering reset event: {}", err);
}
println!("Reset signal!"); println!("Reset signal!");
} }
} }
@ -107,9 +111,9 @@ pub struct IoDispatcher {
} }
impl IoDispatcher { impl IoDispatcher {
pub fn new() -> Arc<IoDispatcher> { pub fn new(reset_evt: EventFd) -> Arc<IoDispatcher> {
Arc::new(IoDispatcher{ Arc::new(IoDispatcher{
state: RwLock::new(IoDispatcherState::new()), state: RwLock::new(IoDispatcherState::new(reset_evt)),
}) })
} }
@ -149,13 +153,13 @@ struct IoDispatcherState {
} }
impl IoDispatcherState { impl IoDispatcherState {
pub fn new() -> IoDispatcherState { pub fn new(reset_evt: EventFd) -> IoDispatcherState {
let mut st = IoDispatcherState { let mut st = IoDispatcherState {
last_unhandled_port: 0, last_unhandled_port: 0,
ioport_entries: Vec::new(), ioport_entries: Vec::new(),
mmio_entries: Vec::new(), mmio_entries: Vec::new(),
}; };
st.setup_ioports(); st.setup_ioports(reset_evt);
st st
} }
@ -227,13 +231,13 @@ impl IoDispatcherState {
self.register_ioports(port, count, Arc::new(RwLock::new(IoPortDummy))); self.register_ioports(port, count, Arc::new(RwLock::new(IoPortDummy)));
} }
fn setup_ioports(&mut self) { fn setup_ioports(&mut self, reset_evt: EventFd) {
/* 0000 - 001F - DMA1 controller */ /* 0000 - 001F - DMA1 controller */
self.register_dummy(0x0000, 32); self.register_dummy(0x0000, 32);
/* 0020 - 003F - 8259A PIC 1 */ /* 0020 - 003F - 8259A PIC 1 */
self.register_dummy(0x0020, 2); self.register_dummy(0x0020, 2);
/* 0060 - 0068 - i8042 */ /* 0060 - 0068 - i8042 */
self.register_ioports(0x0060, 8, Arc::new(RwLock::new(IoPortFakeI8042(false)))); self.register_ioports(0x0060, 8, Arc::new(RwLock::new(IoPortFakeI8042(false, reset_evt))));
/* 0040 - 005F - PIT (8253,8254) */ /* 0040 - 005F - PIT (8253,8254) */
self.register_dummy(0x0040, 4); self.register_dummy(0x0040, 4);
/* 0092 - PS/2 system control port A */ /* 0092 - PS/2 system control port A */

View File

@ -15,10 +15,11 @@ fn add_defaults(cmdline: &mut KernelCmdLine) {
.push("init_on_free=0") .push("init_on_free=0")
.push_set_val("console", "hvc0") .push_set_val("console", "hvc0")
.push_set_true("i8042.direct") .push("i8042.direct")
.push_set_true("i8042.dumbkbd") .push("i8042.dumbkbd")
.push_set_true("i8042.nopnp") .push("i8042.nopnp")
.push_set_true("i8042.noaux") .push("i8042.noaux")
.push("i8042.nomux")
// .push("initcall_debug") // .push("initcall_debug")
.push_set_val("iommu", "off") .push_set_val("iommu", "off")
.push("cryptomgr.notests") .push("cryptomgr.notests")

125
src/vm/kvm_vm.rs Normal file
View File

@ -0,0 +1,125 @@
use std::result;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use kvm_bindings::{CpuId, KVM_MAX_CPUID_ENTRIES, kvm_pit_config, KVM_PIT_SPEAKER_DUMMY, kvm_userspace_memory_region};
use kvm_ioctls::{Cap, Kvm, VmFd};
use kvm_ioctls::Cap::*;
use crate::vm::vcpu::Vcpu;
use crate::vm::{Result, Error, ArchSetup};
use crate::vm::io::IoDispatcher;
const KVM_API_VERSION: i32 = 12;
type KvmResult<T> = result::Result<T, kvm_ioctls::Error>;
static REQUIRED_EXTENSIONS: &[Cap] = &[
AdjustClock,
Debugregs,
ExtCpuid,
Hlt,
Ioeventfd,
Irqchip,
MpState,
Pit2,
PitState2,
SetTssAddr,
UserMemory,
VcpuEvents,
Xcrs,
Xsave,
];
fn check_extensions_and_version(kvm: &Kvm) -> Result<()> {
if kvm.get_api_version() != KVM_API_VERSION {
return Err(Error::BadVersion);
}
for &e in REQUIRED_EXTENSIONS {
if !kvm.check_extension(e) {
return Err(Error::MissingRequiredExtension(e));
}
}
Ok(())
}
#[derive(Clone)]
pub struct KvmVm {
vm_fd: Arc<VmFd>,
supported_cpuid: Arc<CpuId>,
//supported_msrs: MsrList,
}
impl KvmVm {
pub fn open() -> Result<Self> {
let kvm = Kvm::new()
.map_err(Error::KvmOpenError)?;
check_extensions_and_version(&kvm)?;
let vm_fd = kvm.create_vm()
.map_err(Error::VmFdOpenError)?;
let supported_cpuid = kvm.get_supported_cpuid(KVM_MAX_CPUID_ENTRIES)
.map_err(Error::KvmError)?;
Ok(KvmVm {
vm_fd: Arc::new(vm_fd),
supported_cpuid : Arc::new(supported_cpuid)
})
}
pub fn vm_fd(&self) -> &VmFd {
&self.vm_fd
}
fn set_memory_region(&self, slot: u32, guest_phys_addr: u64, userspace_addr: u64, memory_size: u64) -> KvmResult<()> {
let memory_region = kvm_userspace_memory_region {
slot,
flags: 0,
guest_phys_addr,
memory_size,
userspace_addr,
};
unsafe {
self.vm_fd.set_user_memory_region(memory_region)?;
}
Ok(())
}
pub fn add_memory_region(&self, slot: u32, guest_address: u64, host_address: u64, size: usize) -> KvmResult<()> {
self.set_memory_region(slot, guest_address, host_address, size as u64)
}
pub fn remove_memory_region(&self, slot: u32) -> KvmResult<()> {
self.set_memory_region(slot, 0, 0, 0)
}
pub fn set_irq_line(&self, irq: u32, active: bool) -> KvmResult<()> {
self.vm_fd.set_irq_line(irq, active)
}
pub fn supported_cpuid(&self) -> CpuId {
(*self.supported_cpuid).clone()
}
pub fn create_irqchip(&self) -> Result<()> {
self.vm_fd.create_irq_chip()
.map_err(Error::VmSetup)?;
let pit_config = kvm_pit_config {
flags: KVM_PIT_SPEAKER_DUMMY,
..Default::default()
};
self.vm_fd.create_pit2(pit_config)
.map_err(Error::VmSetup)
}
pub fn create_vcpu<A: ArchSetup>(&self, id: u64, io: Arc<IoDispatcher>, shutdown: Arc<AtomicBool>, arch: &mut A) -> Result<Vcpu> {
let vcpu_fd = self.vm_fd.create_vcpu(id)
.map_err(Error::CreateVcpu)?;
let vcpu = Vcpu::new(vcpu_fd, io, shutdown);
arch.setup_vcpu(vcpu.vcpu_fd(), self.supported_cpuid().clone()).map_err(Error::ArchError)?;
Ok(vcpu)
}
}

View File

@ -3,15 +3,17 @@ static PHINIT: &[u8] = include_bytes!("../../ph-init/target/release/ph-init");
static SOMMELIER: &[u8] = include_bytes!("../../sommelier/build/sommelier"); static SOMMELIER: &[u8] = include_bytes!("../../sommelier/build/sommelier");
pub mod arch; pub mod arch;
mod run;
pub mod io; pub mod io;
mod setup; mod setup;
mod error; mod error;
mod kernel_cmdline; mod kernel_cmdline;
mod config; mod config;
mod kvm_vm;
mod vcpu;
pub use config::VmConfig; pub use config::VmConfig;
pub use setup::VmSetup; pub use setup::VmSetup;
pub use kvm_vm::KvmVm;
pub use self::error::{Result,Error}; pub use self::error::{Result,Error};
pub use arch::{ArchSetup,create_setup}; pub use arch::{ArchSetup,create_setup};

View File

@ -1,204 +0,0 @@
use std::sync::Arc;
use crate::kvm::KvmVcpu;
use crate::memory::Mapping;
use super::Result;
use super::io::IoDispatcher;
use std::sync::atomic::{AtomicBool, Ordering};
use crate::vm::Error;
const KVM_EXIT_UNKNOWN:u32 = 0;
const KVM_EXIT_IO:u32 = 2;
const KVM_EXIT_MMIO:u32 = 6;
const KVM_EXIT_INTR:u32 = 10;
const KVM_EXIT_SHUTDOWN:u32 = 8;
const KVM_EXIT_INTERNAL_ERROR: u32 = 17;
const KVM_EXIT_SYSTEM_EVENT:u32 = 24;
pub struct KvmRunArea {
vcpu: KvmVcpu,
io: Arc<IoDispatcher>,
mapping: Mapping,
shutdown: Arc<AtomicBool>,
}
pub struct IoExitData {
dir_out: bool,
size: usize,
port: u16,
count: usize,
offset: usize,
}
pub struct MmioExitData {
phys: u64,
size: usize,
write: bool,
}
impl KvmRunArea {
pub fn new(vcpu: KvmVcpu, shutdown: Arc<AtomicBool>, io_dispatcher: Arc<IoDispatcher>) -> Result<KvmRunArea> {
let size = vcpu.get_vcpu_mmap_size().map_err(Error::CreateVmFailed)?;
let mapping = Mapping::new_from_fd(vcpu.raw_fd(), size).map_err(Error::MappingFailed)?;
Ok(KvmRunArea{
vcpu,
io: io_dispatcher,
mapping,
shutdown,
})
}
fn r8(&self, offset: usize) -> u8 { self.mapping.read_int(offset).unwrap() }
fn r16(&self, offset: usize) -> u16 { self.mapping.read_int(offset).unwrap() }
fn r32(&self, offset: usize) -> u32 { self.mapping.read_int(offset).unwrap() }
fn r64(&self, offset: usize) -> u64 { self.mapping.read_int(offset).unwrap() }
fn w8(&self, offset: usize, val: u8) { self.mapping.write_int(offset, val).unwrap() }
fn w16(&self, offset: usize, val: u16) { self.mapping.write_int(offset, val).unwrap() }
fn w32(&self, offset: usize, val: u32) { self.mapping.write_int(offset, val).unwrap() }
fn w64(&self, offset: usize, val: u64) { self.mapping.write_int(offset, val).unwrap() }
fn exit_reason(&self) -> u32 {
self.r32(8)
}
fn suberror(&self) -> u32 {
self.r32(32)
}
fn get_io_exit(&self) -> IoExitData {
let d = self.r8(32) != 0;
let size = self.r8(33) as usize;
let port = self.r16(34);
let count = self.r32(36) as usize;
let offset = self.r64(40) as usize;
IoExitData{
dir_out: d,
size,
port,
count,
offset,
}
}
fn get_mmio_exit(&self) -> MmioExitData {
let phys = self.r64(32);
let size = self.r32(48) as usize;
assert!(size <= 8);
let write = self.r8(52) != 0;
MmioExitData {
phys, size, write
}
}
pub fn run(&mut self) {
loop {
if let Err(err) = self.vcpu.run() {
if !err.is_interrupted() {
println!("KVM_RUN returned error, bailing: {:?}", err);
return;
}
} else {
self.handle_exit();
}
if self.shutdown.load(Ordering::Relaxed) {
return;
}
}
}
fn handle_exit(&mut self) {
match self.exit_reason() {
KVM_EXIT_UNKNOWN => {println!("unknown")},
KVM_EXIT_IO => { self.handle_exit_io() },
KVM_EXIT_MMIO => { self.handle_exit_mmio() },
KVM_EXIT_INTR => { println!("intr")},
KVM_EXIT_SHUTDOWN => {
self.handle_shutdown();
},
KVM_EXIT_SYSTEM_EVENT => { println!("event")},
KVM_EXIT_INTERNAL_ERROR => {
let sub = self.suberror();
println!("internal error: {}", sub);
println!("{:?}", self.vcpu.get_regs().unwrap());
}
n => { println!("unhandled exit: {}", n);},
}
}
fn handle_shutdown(&mut self) {
self.shutdown.store(true, Ordering::Relaxed);
}
fn handle_exit_io(&mut self) {
let exit = self.get_io_exit();
if exit.dir_out {
self.handle_exit_io_out(&exit);
} else {
self.handle_exit_io_in(&exit);
}
}
fn handle_exit_io_in(&mut self, exit: &IoExitData) {
for i in 0..exit.count {
let v = self.io.emulate_io_in(exit.port, exit.size);
match exit.size {
1 => self.w8(exit.offset + i, v as u8),
2 => self.w16(exit.offset + i * 2, v as u16),
4 => self.w32(exit.offset + i * 4, v as u32),
_ => {},
}
}
}
fn handle_exit_io_out(&self, exit: &IoExitData) {
for i in 0..exit.count {
let v = match exit.size {
1 => self.r8(exit.offset + i) as u32,
2 => self.r16(exit.offset + i * 2) as u32,
4 => self.r32(exit.offset + i * 4) as u32,
_ => 0,
};
self.io.emulate_io_out(exit.port, exit.size, v);
}
}
fn handle_exit_mmio(&mut self) {
let exit = self.get_mmio_exit();
if exit.write {
self.handle_mmio_write(exit.phys, exit.size);
} else {
self.handle_mmio_read(exit.phys, exit.size);
}
}
fn handle_mmio_write(&self, address: u64, size: usize) {
if let Some(val) = self.data_to_val64(size) {
self.io.emulate_mmio_write(address, size, val)
}
}
fn handle_mmio_read(&self, address: u64, size: usize) {
if size == 1 || size == 2 || size == 4 || size == 8 {
let val = self.io.emulate_mmio_read(address, size);
match size {
1 => self.w8(40, val as u8),
2 => self.w16(40, val as u16),
4 => self.w32(40, val as u32),
8 => self.w64(40, val),
_ => (),
}
}
}
fn data_to_val64(&self, size: usize) -> Option<u64> {
match size {
1 => { Some(self.r8(40) as u64)}
2 => { Some(self.r16(40) as u64)}
4 => { Some(self.r32(40) as u64)}
8 => { Some(self.r64(40))}
_ => { None }
}
}
}

View File

@ -10,41 +10,47 @@ use crate::devices::SyntheticFS;
use std::{fs, thread}; use std::{fs, thread};
use crate::system::{Tap, NetlinkSocket}; use crate::system::{Tap, NetlinkSocket};
use crate::disk::DiskImage; use crate::disk::DiskImage;
use crate::kvm::{KvmVcpu, Kvm};
use std::sync::Arc; use std::sync::Arc;
use crate::memory::MemoryManager; use crate::memory::MemoryManager;
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use crate::vm::run::KvmRunArea; use kvm_ioctls::VmFd;
use vmm_sys_util::eventfd::EventFd;
use crate::vm::kvm_vm::KvmVm;
use crate::vm::vcpu::Vcpu;
pub struct Vm { pub struct Vm {
kvm: Kvm, kvm_vm: KvmVm,
vcpus: Vec<KvmVcpu>, vcpus: Vec<Vcpu>,
memory: MemoryManager, memory: MemoryManager,
io_dispatch: Arc<IoDispatcher>, io_dispatch: Arc<IoDispatcher>,
termios: Option<Termios>, termios: Option<Termios>,
} }
impl Vm { impl Vm {
fn create<A: ArchSetup>(arch: &mut A) -> Result<Self> { fn create<A: ArchSetup>(arch: &mut A, reset_evt: EventFd) -> Result<Self> {
let kvm = arch.open_kvm() let kvm_vm = KvmVm::open()?;
.map_err(Error::ArchError)?; kvm_vm.create_irqchip()?;
let memory = arch.create_memory(&kvm) kvm_vm.vm_fd().set_tss_address(0xfffbd000)
.map_err(Error::KvmError)?;
let memory = arch.create_memory(kvm_vm.clone())
.map_err(Error::ArchError)?; .map_err(Error::ArchError)?;
Ok(Vm { Ok(Vm {
kvm, kvm_vm,
memory, memory,
vcpus: Vec::new(), vcpus: Vec::new(),
io_dispatch: IoDispatcher::new(), io_dispatch: IoDispatcher::new(reset_evt),
termios: None, termios: None,
}) })
} }
pub fn start(&self) -> Result<()> { pub fn start(&mut self) -> Result<()> {
let shutdown = Arc::new(AtomicBool::new(false));
let mut handles = Vec::new(); let mut handles = Vec::new();
for vcpu in self.vcpus.clone() { for vcpu in self.vcpus.drain(..) {
let mut run_area = KvmRunArea::new(vcpu, shutdown.clone(), self.io_dispatch.clone())?; let h = thread::spawn(move || {
let h = thread::spawn(move || run_area.run()); vcpu.run();
});
handles.push(h); handles.push(h);
} }
@ -58,6 +64,11 @@ impl Vm {
Ok(()) Ok(())
} }
pub fn vm_fd(&self) -> &VmFd {
self.kvm_vm.vm_fd()
}
} }
pub struct VmSetup <T: ArchSetup> { pub struct VmSetup <T: ArchSetup> {
@ -77,13 +88,15 @@ impl <T: ArchSetup> VmSetup <T> {
} }
pub fn create_vm(&mut self) -> Result<Vm> { pub fn create_vm(&mut self) -> Result<Vm> {
let mut vm = Vm::create(&mut self.arch)?; let exit_evt = EventFd::new(libc::EFD_NONBLOCK)?;
let reset_evt = exit_evt.try_clone()?;
let mut vm = Vm::create(&mut self.arch, reset_evt)?;
devices::rtc::Rtc::register(vm.io_dispatch.clone()); devices::rtc::Rtc::register(vm.io_dispatch.clone());
if self.config.verbose() { if self.config.verbose() {
self.cmdline.push("earlyprintk=serial"); self.cmdline.push("earlyprintk=serial");
devices::serial::SerialDevice::register(vm.kvm.clone(),vm.io_dispatch.clone(), 0); devices::serial::SerialDevice::register(vm.kvm_vm.clone(),vm.io_dispatch.clone(), 0);
} else { } else {
self.cmdline.push("quiet"); self.cmdline.push("quiet");
} }
@ -102,7 +115,7 @@ impl <T: ArchSetup> VmSetup <T> {
.map_err(Error::TerminalTermios)?; .map_err(Error::TerminalTermios)?;
vm.termios = Some(saved); vm.termios = Some(saved);
let mut virtio = VirtioBus::new(vm.memory.clone(), vm.io_dispatch.clone(), vm.kvm.clone()); let mut virtio = VirtioBus::new(vm.memory.clone(), vm.io_dispatch.clone(), vm.kvm_vm.clone());
self.setup_synthetic_bootfs(&mut virtio)?; self.setup_synthetic_bootfs(&mut virtio)?;
self.setup_virtio(&mut virtio) self.setup_virtio(&mut virtio)
.map_err(Error::SetupVirtio)?; .map_err(Error::SetupVirtio)?;
@ -114,9 +127,9 @@ impl <T: ArchSetup> VmSetup <T> {
self.arch.setup_memory(&self.cmdline, &virtio.pci_irqs()) self.arch.setup_memory(&self.cmdline, &virtio.pci_irqs())
.map_err(Error::ArchError)?; .map_err(Error::ArchError)?;
let shutdown = Arc::new(AtomicBool::new(false));
for id in 0..self.config.ncpus() { for id in 0..self.config.ncpus() {
let vcpu = vm.kvm.new_vcpu(id).map_err(Error::CreateVmFailed)?; let vcpu = vm.kvm_vm.create_vcpu(id as u64, vm.io_dispatch.clone(), shutdown.clone(), &mut self.arch)?;
self.arch.setup_vcpu(&vcpu).map_err(Error::ArchError)?;
vm.vcpus.push(vcpu); vm.vcpus.push(vcpu);
} }
Ok(vm) Ok(vm)

100
src/vm/vcpu.rs Normal file
View File

@ -0,0 +1,100 @@
use std::convert::TryInto;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool,Ordering};
use kvm_ioctls::{VcpuExit, VcpuFd};
use vmm_sys_util::sock_ctrl_msg::IntoIovec;
use crate::vm::io::IoDispatcher;
/*
pub enum VcpuEvent {
Exit,
}
pub struct VcpuHandle {
sender: Sender<VcpuEvent>,
thread: thread::JoinHandle<()>,
}
*/
pub struct Vcpu {
vcpu_fd: VcpuFd,
io: Arc<IoDispatcher>,
shutdown: Arc<AtomicBool>,
}
impl Vcpu {
pub fn new(vcpu_fd: VcpuFd, io: Arc<IoDispatcher>, shutdown: Arc<AtomicBool>) -> Self {
Vcpu {
vcpu_fd, io, shutdown,
}
}
pub fn vcpu_fd(&self) -> &VcpuFd {
&self.vcpu_fd
}
fn data_to_int(data: &[u8]) -> u64 {
match data.len() {
1 => data[0] as u64,
2 => u16::from_le_bytes(data.try_into().unwrap()) as u64,
4 => u32::from_le_bytes(data.try_into().unwrap()) as u64,
8 => u64::from_le_bytes(data.try_into().unwrap()),
_ => 0,
}
}
fn int_to_data(n: u64, data: &mut[u8]) {
match data.len() {
1 => data[0] = n as u8,
2 => data.copy_from_slice((n as u16).to_le_bytes().as_slice()),
4 => data.copy_from_slice((n as u32).to_le_bytes().as_slice()),
8 => data.copy_from_slice((n as u64).to_le_bytes().as_slice()),
_ => {},
}
}
fn handle_io_out(&self, port: u16, data: &[u8]) {
let val = Self::data_to_int(data) as u32;
self.io.emulate_io_out(port, data.size(), val);
}
fn handle_io_in(&self, port: u16, data: &mut [u8]) {
let val = self.io.emulate_io_in(port, data.len());
Self::int_to_data(val as u64, data);
}
fn handle_mmio_read(&self, addr: u64, data: &mut [u8]) {
let val = self.io.emulate_mmio_read(addr, data.len());
Self::int_to_data(val, data);
}
fn handle_mmio_write(&self, addr: u64, data: &[u8]) {
let val = Self::data_to_int(data);
self.io.emulate_mmio_write(addr, data.size(), val);
}
fn handle_shutdown(&self) {
self.shutdown.store(true, Ordering::Relaxed);
}
pub fn run(&self) {
loop {
match self.vcpu_fd.run().expect("fail") {
VcpuExit::IoOut(port, data) => self.handle_io_out(port, data),
VcpuExit::IoIn(port, data) => self.handle_io_in(port, data),
VcpuExit::MmioRead(addr, data) => self.handle_mmio_read(addr, data),
VcpuExit::MmioWrite(addr, data) => self.handle_mmio_write(addr, data),
VcpuExit::Shutdown => self.handle_shutdown(),
exit => {
println!("unhandled exit: {:?}", exit);
}
}
if self.shutdown.load(Ordering::Relaxed) {
return;
}
}
}
}