Refactor to use rust-vmm crates for kvm operations
This commit is contained in:
parent
99020a5ad4
commit
41d6d10373
92
Cargo.lock
generated
92
Cargo.lock
generated
@ -149,6 +149,26 @@ dependencies = [
|
||||
"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]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@ -235,11 +255,15 @@ name = "ph"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"kvm-bindings",
|
||||
"kvm-ioctls",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"libcitadel",
|
||||
"signal-hook",
|
||||
"termios",
|
||||
"thiserror",
|
||||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -254,16 +278,16 @@ version = "0.4.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
||||
dependencies = [
|
||||
"unicode-xid 0.1.0",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.3"
|
||||
version = "1.0.50"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8"
|
||||
checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
|
||||
dependencies = [
|
||||
"unicode-xid 0.2.0",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -281,7 +305,7 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.3",
|
||||
"proc-macro2 1.0.50",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -323,9 +347,9 @@ version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.3",
|
||||
"proc-macro2 1.0.50",
|
||||
"quote 1.0.2",
|
||||
"syn 1.0.5",
|
||||
"syn 1.0.107",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -367,18 +391,18 @@ checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
|
||||
dependencies = [
|
||||
"proc-macro2 0.4.30",
|
||||
"quote 0.6.13",
|
||||
"unicode-xid 0.1.0",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.5"
|
||||
version = "1.0.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf"
|
||||
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.3",
|
||||
"proc-macro2 1.0.50",
|
||||
"quote 1.0.2",
|
||||
"unicode-xid 0.2.0",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -390,7 +414,7 @@ dependencies = [
|
||||
"proc-macro2 0.4.30",
|
||||
"quote 0.6.13",
|
||||
"syn 0.15.44",
|
||||
"unicode-xid 0.1.0",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -420,6 +444,26 @@ dependencies = [
|
||||
"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]]
|
||||
name = "toml"
|
||||
version = "0.4.10"
|
||||
@ -429,24 +473,34 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
|
@ -4,10 +4,16 @@ version = "0.1.0"
|
||||
authors = ["Bruce Leidl <bruce@subgraph.com>"]
|
||||
edition = "2018"
|
||||
|
||||
|
||||
|
||||
[dependencies]
|
||||
byteorder="1.0.0"
|
||||
libc = "*"
|
||||
termios = "0.2.2"
|
||||
lazy_static = "1.4.0"
|
||||
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" }
|
||||
|
56
ph-init/Cargo.lock
generated
56
ph-init/Cargo.lock
generated
@ -20,4 +20,60 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"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"
|
||||
|
@ -226,7 +226,6 @@ impl InitServer {
|
||||
let sommelierx = ServiceLaunch::new("sommelier-x", "/opt/ph/usr/bin/sommelier")
|
||||
.base_environment()
|
||||
.uidgid(1000,1000)
|
||||
.env("SOMMELIER_SHM_DRIVER", shm_driver)
|
||||
.arg("-X")
|
||||
.arg("--x-display=0")
|
||||
.arg("--no-exit-with-child")
|
||||
|
@ -70,7 +70,7 @@ pub fn move_mount(source: &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",
|
||||
libc::MS_NOATIME|MS_LAZYTIME,
|
||||
Some("trans=virtio,cache=loose"))
|
||||
|
@ -2,7 +2,7 @@ use std::sync::{Arc, RwLock};
|
||||
use std::io::{self, Write};
|
||||
|
||||
use crate::vm::io::{IoPortOps,IoDispatcher};
|
||||
use crate::kvm::Kvm;
|
||||
use crate::vm::KvmVm;
|
||||
|
||||
const UART_TX: u16 = 0;
|
||||
const UART_RX: u16 = 0;
|
||||
@ -67,7 +67,7 @@ impl Bits for u8 {
|
||||
|
||||
pub struct SerialDevice {
|
||||
iobase: u16,
|
||||
kvm: Kvm,
|
||||
kvm_vm: KvmVm,
|
||||
irq: u8,
|
||||
irq_state: u8,
|
||||
txcnt: usize,
|
||||
@ -134,12 +134,12 @@ impl SerialDevice {
|
||||
if iir == 0 {
|
||||
self.iir = UART_IIR_NO_INT;
|
||||
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 {
|
||||
self.iir = iir;
|
||||
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;
|
||||
@ -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) {
|
||||
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)));
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
iobase,
|
||||
kvm,
|
||||
kvm_vm,
|
||||
irq,
|
||||
irq_state: 0,
|
||||
txcnt: 0,
|
||||
|
@ -3,13 +3,14 @@ use std::sync::{RwLock, Arc};
|
||||
use std::thread;
|
||||
|
||||
use crate::{system, virtio};
|
||||
use crate::system::{EPoll,EventFd};
|
||||
use crate::system::EPoll;
|
||||
use crate::memory::{MemoryManager, DrmDescriptor};
|
||||
use crate::virtio::{VirtQueue, VirtioBus, VirtioDeviceOps, Chain};
|
||||
|
||||
use crate::devices::virtio_wl::{vfd::VfdManager, consts::*, Error, Result, VfdObject};
|
||||
use crate::system::ioctl::ioctl_with_ref;
|
||||
use std::os::raw::{c_ulong, c_uint, c_ulonglong};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
#[repr(C)]
|
||||
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> {
|
||||
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)?;
|
||||
Ok(dev)
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
256
src/kvm/ioctl.rs
256
src/kvm/ioctl.rs
@ -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))
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
160
src/kvm/mod.rs
160
src/kvm/mod.rs
@ -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, ®ion)?;
|
||||
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, ®ion)?;
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ pub mod util;
|
||||
mod vm;
|
||||
mod memory;
|
||||
mod devices;
|
||||
mod kvm;
|
||||
mod virtio;
|
||||
mod disk;
|
||||
|
||||
|
@ -3,16 +3,16 @@ use std::os::unix::io::{AsRawFd,RawFd};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use crate::memory::{GuestRam, SystemAllocator, Mapping, Error, Result};
|
||||
use crate::kvm::Kvm;
|
||||
use crate::system::FileDesc;
|
||||
use crate::util::BitSet;
|
||||
use crate::memory::drm::{DrmBufferAllocator, DrmDescriptor};
|
||||
use std::io::SeekFrom;
|
||||
use crate::memory::ram::MemoryRegion;
|
||||
use crate::vm::KvmVm;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MemoryManager {
|
||||
kvm: Kvm,
|
||||
kvm_vm: KvmVm,
|
||||
ram: GuestRam,
|
||||
device_memory: Arc<RwLock<DeviceMemory>>,
|
||||
drm_allocator: Option<DrmBufferAllocator>,
|
||||
@ -20,7 +20,7 @@ pub struct 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 drm_allocator = if use_drm {
|
||||
DrmBufferAllocator::open().ok()
|
||||
@ -28,7 +28,7 @@ impl MemoryManager {
|
||||
None
|
||||
};
|
||||
Ok(MemoryManager {
|
||||
kvm, ram, device_memory,
|
||||
kvm_vm, ram, device_memory,
|
||||
drm_allocator,
|
||||
})
|
||||
}
|
||||
@ -37,8 +37,8 @@ impl MemoryManager {
|
||||
&self.ram
|
||||
}
|
||||
|
||||
pub fn kvm(&self) -> &Kvm {
|
||||
&self.kvm
|
||||
pub fn kvm_vm(&self) -> &KvmVm {
|
||||
&self.kvm_vm
|
||||
}
|
||||
|
||||
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)> {
|
||||
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<()> {
|
||||
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 {
|
||||
@ -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)
|
||||
.map_err(Error::MappingFailed)?;
|
||||
|
||||
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);
|
||||
Err(Error::RegisterMemoryFailed(e))
|
||||
} 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) {
|
||||
kvm.remove_memory_region(slot)
|
||||
kvm_vm.remove_memory_region(slot)
|
||||
.map_err(Error::UnregisterMemoryFailed)?;
|
||||
self.free_addr_and_slot(registration.guest_addr, slot);
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@ use crate::system::errno::{Result,Error};
|
||||
pub const IOC_SIZEBITS: u64 = 14;
|
||||
pub const IOC_DIRBITS: u64 = 2;
|
||||
|
||||
pub const IOC_NONE: u64 = 0;
|
||||
pub const IOC_READ: u64 = 2;
|
||||
pub const IOC_WRITE: u64 = 1;
|
||||
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)
|
||||
}
|
||||
|
||||
macro_rules! io {
|
||||
($ty:expr, $nr:expr) => (ioc!($crate::system::ioctl::IOC_NONE, $ty, $nr, 0))
|
||||
}
|
||||
|
||||
macro_rules! iow {
|
||||
($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 {
|
||||
($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::system::ioctl::IOC_RDWR, $ty, $nr, $sz))
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
use std::sync::{Arc,RwLock};
|
||||
use crate::vm::io::IoDispatcher;
|
||||
use crate::kvm::Kvm;
|
||||
use crate::memory::{AddressRange, MemoryManager};
|
||||
use super::{VirtioDevice,VirtioDeviceOps,PciIrq};
|
||||
use super::consts::*;
|
||||
use super::pci::PciBus;
|
||||
use crate::virtio::Result;
|
||||
use std::iter;
|
||||
use crate::vm::KvmVm;
|
||||
|
||||
|
||||
pub struct VirtioBus {
|
||||
kvm: Kvm,
|
||||
kvm_vm: KvmVm,
|
||||
memory: MemoryManager,
|
||||
io_dispatcher: Arc<IoDispatcher>,
|
||||
pci_bus: Arc<RwLock<PciBus>>,
|
||||
@ -18,9 +18,9 @@ pub struct 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 {
|
||||
kvm,
|
||||
kvm_vm,
|
||||
memory,
|
||||
io_dispatcher: io_dispatcher.clone(),
|
||||
pci_bus: PciBus::new(&io_dispatcher),
|
||||
@ -41,7 +41,7 @@ pub struct VirtioDeviceConfig<'a> {
|
||||
virtio_bus: &'a mut VirtioBus,
|
||||
device_type: u16,
|
||||
irq: u8,
|
||||
kvm: Kvm,
|
||||
kvm_vm: KvmVm,
|
||||
ops: Arc<RwLock<dyn VirtioDeviceOps>>,
|
||||
mmio: AddressRange,
|
||||
queue_sizes: Vec<usize>,
|
||||
@ -53,13 +53,13 @@ pub struct VirtioDeviceConfig<'a> {
|
||||
|
||||
impl <'a> VirtioDeviceConfig<'a> {
|
||||
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);
|
||||
VirtioDeviceConfig {
|
||||
virtio_bus,
|
||||
device_type,
|
||||
irq: 0,
|
||||
kvm,
|
||||
kvm_vm,
|
||||
ops,
|
||||
mmio,
|
||||
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>> {
|
||||
self.ops.clone()
|
||||
|
@ -1,12 +1,13 @@
|
||||
use crate::memory::GuestRam;
|
||||
use std::sync::Arc;
|
||||
use kvm_ioctls::{IoEventAddress, NoDatamatch};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
use super::VirtQueue;
|
||||
use super::vring::Vring;
|
||||
use super::virtqueue::InterruptLine;
|
||||
use super::bus::VirtioDeviceConfig;
|
||||
use crate::virtio::{Result, Error};
|
||||
use crate::kvm::IoEventFd;
|
||||
use crate::virtio::{Error, Result};
|
||||
|
||||
///
|
||||
/// Manages a set of virtqueues during device intitialization.
|
||||
@ -17,7 +18,7 @@ pub struct VirtQueueConfig {
|
||||
enabled_features: u64,
|
||||
vrings: Vec<Vring>,
|
||||
interrupt: Arc<InterruptLine>,
|
||||
events: Vec<Arc<IoEventFd>>,
|
||||
events: Vec<Arc<EventFd>>,
|
||||
}
|
||||
|
||||
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 notify_base = conf.notify_mmio().base();
|
||||
|
||||
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)?;
|
||||
v.push(Arc::new(evt));
|
||||
}
|
||||
|
@ -1,29 +1,27 @@
|
||||
use std::sync::atomic::{Ordering, AtomicUsize, AtomicBool};
|
||||
use std::sync::Arc;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
use crate::memory::GuestRam;
|
||||
use crate::kvm::Kvm;
|
||||
use crate::virtio::{Result,Error};
|
||||
use crate::system::EventFd;
|
||||
use crate::kvm::IoEventFd;
|
||||
use super::consts::*;
|
||||
use super::vring::{Vring,Descriptor};
|
||||
use super::bus::VirtioDeviceConfig;
|
||||
use crate::virtio::chain::Chain;
|
||||
use crate::vm::KvmVm;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct VirtQueue {
|
||||
memory: GuestRam,
|
||||
vring: Vring,
|
||||
features: u64,
|
||||
ioeventfd: Arc<IoEventFd>,
|
||||
ioeventfd: Arc<EventFd>,
|
||||
interrupt: Arc<InterruptLine>,
|
||||
closed: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
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 {
|
||||
memory,
|
||||
vring,
|
||||
@ -120,7 +118,7 @@ impl VirtQueue {
|
||||
self.vring.load_descriptor(idx)
|
||||
}
|
||||
|
||||
pub fn ioevent(&self) -> &IoEventFd {
|
||||
pub fn ioevent(&self) -> &EventFd {
|
||||
&self.ioeventfd
|
||||
}
|
||||
}
|
||||
@ -147,12 +145,13 @@ pub struct InterruptLine {
|
||||
|
||||
impl 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>> {
|
||||
let irqfd = EventFd::new().map_err(Error::CreateEventFd)?;
|
||||
kvm.irqfd(irqfd.as_raw_fd() as u32, irq as u32)
|
||||
fn new(kvm_vm: &KvmVm, irq: u8) -> Result<Arc<InterruptLine>> {
|
||||
let irqfd = EventFd::new(0)
|
||||
.map_err(Error::CreateEventFd)?;
|
||||
kvm_vm.vm_fd().register_irqfd(&irqfd, irq as u32)
|
||||
.map_err(Error::IrqFd)?;
|
||||
Ok(Arc::new(InterruptLine{
|
||||
irqfd,
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::kvm::{KvmVcpu, Kvm};
|
||||
use kvm_bindings::CpuId;
|
||||
use kvm_ioctls::VcpuFd;
|
||||
pub use crate::vm::arch::x86::X86ArchSetup;
|
||||
use crate::memory::MemoryManager;
|
||||
|
||||
@ -7,21 +8,20 @@ mod x86;
|
||||
|
||||
pub use x86::PCI_MMIO_RESERVED_BASE;
|
||||
|
||||
pub use x86::KvmRegs;
|
||||
pub use error::{Error,Result};
|
||||
use crate::vm::kernel_cmdline::KernelCmdLine;
|
||||
use crate::vm::VmConfig;
|
||||
use crate::virtio::PciIrq;
|
||||
use crate::vm::kvm_vm::KvmVm;
|
||||
|
||||
pub fn create_setup(config: &VmConfig) -> X86ArchSetup {
|
||||
X86ArchSetup::create(config)
|
||||
}
|
||||
|
||||
pub trait ArchSetup {
|
||||
fn open_kvm(&self) -> Result<Kvm>;
|
||||
fn create_memory(&mut self, kvm: &Kvm) -> Result<MemoryManager>;
|
||||
fn create_memory(&mut self, kvm_vm: KvmVm) -> Result<MemoryManager>;
|
||||
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<()>;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
|
||||
use std::os::unix::io::RawFd;
|
||||
use crate::vm::arch::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};
|
||||
use kvm_bindings::CpuId;
|
||||
use kvm_ioctls::VcpuFd;
|
||||
use crate::vm::arch::{Error, Result};
|
||||
|
||||
const EBX_CLFLUSH_CACHELINE: u32 = 8; // Flush a cache line size.
|
||||
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_ECX: u32 = u32::from_le_bytes([b'n', b't', b'e', b'l']);
|
||||
|
||||
pub fn setup_cpuid(vcpu: &KvmVcpu) -> Result<()> {
|
||||
let mut cpuid = kvm_get_supported_cpuid(vcpu.sys_raw_fd())?;
|
||||
pub fn setup_cpuid(vcpu: &VcpuFd, cpuid: CpuId) -> Result<()> {
|
||||
let mut cpuid = cpuid;
|
||||
|
||||
let cpu_id = 0u32; // first vcpu
|
||||
|
||||
for e in &mut cpuid {
|
||||
for e in cpuid.as_mut_slice() {
|
||||
match e.function {
|
||||
0 => {
|
||||
e.ebx = INTEL_EBX;
|
||||
@ -57,69 +56,7 @@ pub fn setup_cpuid(vcpu: &KvmVcpu) -> Result<()> {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
kvm_set_cpuid2(vcpu.raw_fd(), cpuid)
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
vcpu.set_cpuid2(&cpuid)
|
||||
.map_err(Error::SetupError)?;
|
||||
Ok(())
|
||||
}
|
76
src/vm/arch/x86/gdt.rs
Normal file
76
src/vm/arch/x86/gdt.rs
Normal 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())
|
||||
}
|
||||
}
|
@ -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::x86::ioctl::{KVM_GET_LAPIC, KVM_SET_LAPIC};
|
||||
use crate::vm::arch::{Error, Result};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct KvmLapicState {
|
||||
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_MODE_EXTINT: u32 = 0x7;
|
||||
const APIC_MODE_NMI: u32 = 0x4;
|
||||
const APIC_LVT_LINT0_OFFSET: usize = 0x350;
|
||||
const APIC_LVT_LINT1_OFFSET: usize = 0x360;
|
||||
|
||||
pub fn setup_lapic(cpufd: RawFd) -> Result<()> {
|
||||
let mut lapic = kvm_get_lapic(cpufd)?;
|
||||
// delivery mode
|
||||
lapic.regs[APIC_LVT_LINT0_OFFSET + 1] &= 0xF8;
|
||||
lapic.regs[APIC_LVT_LINT0_OFFSET + 1] |= APIC_MODE_EXTINT;
|
||||
lapic.regs[APIC_LVT_LINT1_OFFSET + 1] &= 0xF8;
|
||||
lapic.regs[APIC_LVT_LINT1_OFFSET + 1] |= APIC_MODE_NMI;
|
||||
kvm_set_lapic(cpufd, &lapic)
|
||||
fn get_klapic_reg(klapic: &kvm_lapic_state, offset: usize) -> u32 {
|
||||
let mut bytes = [0u8; 4];
|
||||
for idx in 0..4 {
|
||||
bytes[idx] = klapic.regs[offset + idx] as u8;
|
||||
}
|
||||
u32::from_le_bytes(bytes)
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
use crate::kvm::Kvm;
|
||||
use crate::memory::{MemoryManager, MemoryRegion, GuestRam};
|
||||
use crate::vm::arch::{Error, Result};
|
||||
use std::cmp;
|
||||
@ -7,6 +6,7 @@ use crate::vm::arch::x86::kernel::{load_pm_kernel, KERNEL_CMDLINE_ADDRESS};
|
||||
use crate::system;
|
||||
use crate::vm::arch::x86::mptable::setup_mptable;
|
||||
use crate::virtio::PciIrq;
|
||||
use crate::vm::KvmVm;
|
||||
|
||||
pub const HIMEM_BASE: u64 = 1 << 32;
|
||||
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<()> {
|
||||
let mut regions = Vec::new();
|
||||
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 {
|
||||
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);
|
||||
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)
|
||||
.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)?;
|
||||
Ok(mr)
|
||||
}
|
||||
|
@ -1,13 +1,11 @@
|
||||
mod cpuid;
|
||||
mod gdt;
|
||||
mod interrupts;
|
||||
mod kvm;
|
||||
mod memory;
|
||||
mod mptable;
|
||||
mod registers;
|
||||
mod kernel;
|
||||
mod ioctl;
|
||||
mod setup;
|
||||
|
||||
pub use setup::X86ArchSetup;
|
||||
pub use memory::PCI_MMIO_RESERVED_BASE;
|
||||
pub use registers::KvmRegs;
|
||||
pub use memory::PCI_MMIO_RESERVED_BASE;
|
@ -1,12 +1,9 @@
|
||||
use std::fmt;
|
||||
use std::os::unix::io::RawFd;
|
||||
use kvm_bindings::{kvm_fpu, kvm_msr_entry, kvm_regs, Msrs};
|
||||
use kvm_ioctls::VcpuFd;
|
||||
|
||||
use crate::kvm::KvmVcpu;
|
||||
use crate::vm::arch::{Result, Error};
|
||||
use crate::vm::arch::{Error, Result};
|
||||
use crate::vm::arch::x86::gdt::GdtEntry;
|
||||
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_ESP: u32 = 0x00000175;
|
||||
@ -21,27 +18,38 @@ const MSR_IA32_MISC_ENABLE: u32 = 0x000001a0;
|
||||
|
||||
const MSR_IA32_MISC_ENABLE_FAST_STRING: u64 = 0x01;
|
||||
|
||||
pub fn setup_fpu(vcpu: &KvmVcpu) -> Result<()> {
|
||||
let mut fpu = KvmFpu::new();
|
||||
fpu.fcw = 0x37f;
|
||||
fpu.mxcsr = 0x1f80;
|
||||
kvm_set_fpu(vcpu.raw_fd(), &fpu)?;
|
||||
pub fn setup_fpu(vcpu: &VcpuFd) -> Result<()> {
|
||||
let fpu = kvm_fpu {
|
||||
fcw: 0x37f,
|
||||
mxcsr: 0x1f80,
|
||||
..Default::default()
|
||||
};
|
||||
vcpu.set_fpu(&fpu)
|
||||
.map_err(Error::SetupError)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn setup_msrs(vcpu: &KvmVcpu) -> Result<()> {
|
||||
let mut msrs = KvmMsrs::new();
|
||||
msrs.add(MSR_IA32_SYSENTER_CS, 0);
|
||||
msrs.add(MSR_IA32_SYSENTER_ESP, 0);
|
||||
msrs.add(MSR_IA32_SYSENTER_EIP, 0);
|
||||
msrs.add(MSR_STAR, 0);
|
||||
msrs.add(MSR_CSTAR, 0);
|
||||
msrs.add(MSR_KERNEL_GS_BASE, 0);
|
||||
msrs.add(MSR_SYSCALL_MASK, 0);
|
||||
msrs.add(MSR_LSTAR, 0);
|
||||
msrs.add(MSR_IA32_TSC, 0);
|
||||
msrs.add(MSR_IA32_MISC_ENABLE, MSR_IA32_MISC_ENABLE_FAST_STRING);
|
||||
kvm_set_msrs(vcpu.raw_fd(), &msrs)?;
|
||||
pub fn setup_msrs(vcpu: &VcpuFd) -> Result<()> {
|
||||
let msr = | index, data| kvm_msr_entry {
|
||||
index, data, ..Default::default()
|
||||
};
|
||||
let entries = vec![
|
||||
msr(MSR_IA32_SYSENTER_CS, 0),
|
||||
msr(MSR_IA32_SYSENTER_ESP, 0),
|
||||
msr(MSR_IA32_SYSENTER_EIP, 0),
|
||||
msr(MSR_STAR, 0),
|
||||
msr(MSR_CSTAR, 0),
|
||||
msr(MSR_KERNEL_GS_BASE, 0),
|
||||
msr(MSR_SYSCALL_MASK, 0),
|
||||
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(())
|
||||
}
|
||||
|
||||
@ -58,19 +66,23 @@ const X86_CR4_PAE: u64 = 0x20;
|
||||
const EFER_LME: u64 = 0x100;
|
||||
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 data = KvmSegment::new(0, 0xfffff, 2 * 8, 0xc093);
|
||||
let tss = KvmSegment::new(0, 0xfffff, 3 * 8, 0x808b);
|
||||
let code = GdtEntry::new(0xa09b, 0, 0xFFFFF)
|
||||
.kvm_segment(1);
|
||||
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.limit = 32 - 1;
|
||||
|
||||
regs.itd.base = BOOT_IDT_OFFSET as u64;
|
||||
regs.itd.limit = 8 - 1;
|
||||
regs.idt.base = BOOT_IDT_OFFSET as u64;
|
||||
regs.idt.limit = 8 - 1;
|
||||
|
||||
regs.cs = code;
|
||||
regs.ds = data;
|
||||
@ -89,226 +101,24 @@ pub fn setup_pm_sregs(vcpu: &KvmVcpu) -> Result<()> {
|
||||
regs.cr0 |= X86_CR0_PG;
|
||||
regs.efer |= EFER_LMA;
|
||||
|
||||
kvm_set_sregs(vcpu.raw_fd(), ®s)?;
|
||||
vcpu.set_sregs(®s)
|
||||
.map_err(Error::SetupError)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn setup_pm_regs(vcpu: &KvmVcpu, kernel_entry: u64) -> Result<()> {
|
||||
let mut regs = KvmRegs::new();
|
||||
regs.rflags = 0x0000000000000002;
|
||||
regs.rip = kernel_entry;
|
||||
regs.rsp = BOOT_STACK;
|
||||
regs.rbp = BOOT_STACK;
|
||||
regs.rsi = KERNEL_ZERO_PAGE;
|
||||
pub fn setup_pm_regs(vcpu: &VcpuFd, kernel_entry: u64) -> Result<()> {
|
||||
let regs = kvm_regs {
|
||||
rflags: 0x0000000000000002,
|
||||
rip: kernel_entry,
|
||||
rsp: BOOT_STACK,
|
||||
rbp: BOOT_STACK,
|
||||
rsi: KERNEL_ZERO_PAGE,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
vcpu.set_regs(®s)
|
||||
.map_err(Error::KvmError)?;
|
||||
.map_err(Error::SetupError)?;
|
||||
|
||||
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() }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,15 +1,16 @@
|
||||
use kvm_bindings::CpuId;
|
||||
use kvm_ioctls::VcpuFd;
|
||||
use crate::memory::{MemoryManager, GuestRam, SystemAllocator, AddressRange};
|
||||
use crate::vm::VmConfig;
|
||||
use crate::vm::arch::{ArchSetup, Error, Result};
|
||||
use crate::vm::kernel_cmdline::KernelCmdLine;
|
||||
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::cpuid::setup_cpuid;
|
||||
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::kernel::KVM_KERNEL_LOAD_ADDRESS;
|
||||
use crate::vm::kvm_vm::KvmVm;
|
||||
|
||||
pub struct X86ArchSetup {
|
||||
ram_size: usize,
|
||||
@ -40,16 +41,12 @@ fn get_base_dev_pfn(mem_size: u64) -> u64 {
|
||||
}
|
||||
|
||||
impl ArchSetup for X86ArchSetup {
|
||||
fn open_kvm(&self) -> Result<Kvm> {
|
||||
x86_open_kvm()
|
||||
}
|
||||
|
||||
fn create_memory(&mut self, kvm: &Kvm) -> Result<MemoryManager> {
|
||||
fn create_memory(&mut self, kvm_vm: KvmVm) -> Result<MemoryManager> {
|
||||
let ram = GuestRam::new(self.ram_size);
|
||||
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 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)?;
|
||||
x86_setup_memory_regions(&mut mm, self.ram_size)?;
|
||||
self.memory = Some(mm.clone());
|
||||
@ -62,12 +59,15 @@ impl ArchSetup for X86ArchSetup {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_vcpu(&self, vcpu: &KvmVcpu) -> Result<()> {
|
||||
setup_cpuid(vcpu)?;
|
||||
setup_pm_sregs(vcpu)?;
|
||||
setup_pm_regs(&vcpu, KVM_KERNEL_LOAD_ADDRESS)?;
|
||||
setup_fpu(vcpu)?;
|
||||
setup_msrs(vcpu)?;
|
||||
setup_lapic(vcpu.raw_fd())
|
||||
fn setup_vcpu(&self, vcpu_fd: &VcpuFd, cpuid: CpuId) -> Result<()> {
|
||||
setup_cpuid(vcpu_fd, cpuid)?;
|
||||
setup_pm_sregs(vcpu_fd)?;
|
||||
setup_pm_regs(&vcpu_fd, KVM_KERNEL_LOAD_ADDRESS)?;
|
||||
setup_fpu(vcpu_fd)?;
|
||||
setup_msrs(vcpu_fd)?;
|
||||
setup_lapic(vcpu_fd)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -124,7 +124,7 @@ impl VmConfig {
|
||||
}
|
||||
}
|
||||
let mut setup = self.setup();
|
||||
let vm = match setup.create_vm() {
|
||||
let mut vm = match setup.create_vm() {
|
||||
Ok(vm) => vm,
|
||||
Err(err) => {
|
||||
warn!("Failed to create VM: {}", err);
|
||||
|
18
src/vm/io.rs
18
src/vm/io.rs
@ -1,4 +1,5 @@
|
||||
use std::sync::{Arc,RwLock,RwLockWriteGuard};
|
||||
use vmm_sys_util::eventfd::EventFd;
|
||||
use crate::memory::AddressRange;
|
||||
|
||||
pub trait IoPortOps: Send+Sync {
|
||||
@ -32,7 +33,7 @@ impl IoPortOps for IoPortPS2Control {
|
||||
fn io_in(&mut self, _port: u16, _size: usize) -> u32 { 0x02 }
|
||||
}
|
||||
|
||||
struct IoPortFakeI8042(bool);
|
||||
struct IoPortFakeI8042(bool, EventFd);
|
||||
|
||||
impl IoPortOps for IoPortFakeI8042 {
|
||||
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) {
|
||||
if port == 0x64 && val == 0xfe && !self.0 {
|
||||
self.0 = true;
|
||||
if let Err(err) = self.1.write(1) {
|
||||
warn!("Error triggering reset event: {}", err);
|
||||
}
|
||||
println!("Reset signal!");
|
||||
}
|
||||
}
|
||||
@ -107,9 +111,9 @@ pub struct IoDispatcher {
|
||||
}
|
||||
|
||||
impl IoDispatcher {
|
||||
pub fn new() -> Arc<IoDispatcher> {
|
||||
pub fn new(reset_evt: EventFd) -> Arc<IoDispatcher> {
|
||||
Arc::new(IoDispatcher{
|
||||
state: RwLock::new(IoDispatcherState::new()),
|
||||
state: RwLock::new(IoDispatcherState::new(reset_evt)),
|
||||
})
|
||||
}
|
||||
|
||||
@ -149,13 +153,13 @@ struct IoDispatcherState {
|
||||
}
|
||||
|
||||
impl IoDispatcherState {
|
||||
pub fn new() -> IoDispatcherState {
|
||||
pub fn new(reset_evt: EventFd) -> IoDispatcherState {
|
||||
let mut st = IoDispatcherState {
|
||||
last_unhandled_port: 0,
|
||||
ioport_entries: Vec::new(),
|
||||
mmio_entries: Vec::new(),
|
||||
};
|
||||
st.setup_ioports();
|
||||
st.setup_ioports(reset_evt);
|
||||
st
|
||||
}
|
||||
|
||||
@ -227,13 +231,13 @@ impl IoDispatcherState {
|
||||
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 */
|
||||
self.register_dummy(0x0000, 32);
|
||||
/* 0020 - 003F - 8259A PIC 1 */
|
||||
self.register_dummy(0x0020, 2);
|
||||
/* 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) */
|
||||
self.register_dummy(0x0040, 4);
|
||||
/* 0092 - PS/2 system control port A */
|
||||
|
@ -15,10 +15,11 @@ fn add_defaults(cmdline: &mut KernelCmdLine) {
|
||||
.push("init_on_free=0")
|
||||
.push_set_val("console", "hvc0")
|
||||
|
||||
.push_set_true("i8042.direct")
|
||||
.push_set_true("i8042.dumbkbd")
|
||||
.push_set_true("i8042.nopnp")
|
||||
.push_set_true("i8042.noaux")
|
||||
.push("i8042.direct")
|
||||
.push("i8042.dumbkbd")
|
||||
.push("i8042.nopnp")
|
||||
.push("i8042.noaux")
|
||||
.push("i8042.nomux")
|
||||
// .push("initcall_debug")
|
||||
.push_set_val("iommu", "off")
|
||||
.push("cryptomgr.notests")
|
||||
|
125
src/vm/kvm_vm.rs
Normal file
125
src/vm/kvm_vm.rs
Normal 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)
|
||||
}
|
||||
}
|
@ -3,15 +3,17 @@ static PHINIT: &[u8] = include_bytes!("../../ph-init/target/release/ph-init");
|
||||
static SOMMELIER: &[u8] = include_bytes!("../../sommelier/build/sommelier");
|
||||
|
||||
pub mod arch;
|
||||
mod run;
|
||||
pub mod io;
|
||||
mod setup;
|
||||
mod error;
|
||||
mod kernel_cmdline;
|
||||
mod config;
|
||||
mod kvm_vm;
|
||||
mod vcpu;
|
||||
|
||||
pub use config::VmConfig;
|
||||
pub use setup::VmSetup;
|
||||
pub use kvm_vm::KvmVm;
|
||||
|
||||
pub use self::error::{Result,Error};
|
||||
pub use arch::{ArchSetup,create_setup};
|
||||
|
204
src/vm/run.rs
204
src/vm/run.rs
@ -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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,41 +10,47 @@ use crate::devices::SyntheticFS;
|
||||
use std::{fs, thread};
|
||||
use crate::system::{Tap, NetlinkSocket};
|
||||
use crate::disk::DiskImage;
|
||||
use crate::kvm::{KvmVcpu, Kvm};
|
||||
use std::sync::Arc;
|
||||
use crate::memory::MemoryManager;
|
||||
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 {
|
||||
kvm: Kvm,
|
||||
vcpus: Vec<KvmVcpu>,
|
||||
kvm_vm: KvmVm,
|
||||
vcpus: Vec<Vcpu>,
|
||||
memory: MemoryManager,
|
||||
io_dispatch: Arc<IoDispatcher>,
|
||||
termios: Option<Termios>,
|
||||
}
|
||||
|
||||
impl Vm {
|
||||
fn create<A: ArchSetup>(arch: &mut A) -> Result<Self> {
|
||||
let kvm = arch.open_kvm()
|
||||
.map_err(Error::ArchError)?;
|
||||
let memory = arch.create_memory(&kvm)
|
||||
fn create<A: ArchSetup>(arch: &mut A, reset_evt: EventFd) -> Result<Self> {
|
||||
let kvm_vm = KvmVm::open()?;
|
||||
kvm_vm.create_irqchip()?;
|
||||
kvm_vm.vm_fd().set_tss_address(0xfffbd000)
|
||||
.map_err(Error::KvmError)?;
|
||||
|
||||
let memory = arch.create_memory(kvm_vm.clone())
|
||||
.map_err(Error::ArchError)?;
|
||||
|
||||
Ok(Vm {
|
||||
kvm,
|
||||
kvm_vm,
|
||||
memory,
|
||||
vcpus: Vec::new(),
|
||||
io_dispatch: IoDispatcher::new(),
|
||||
io_dispatch: IoDispatcher::new(reset_evt),
|
||||
termios: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn start(&self) -> Result<()> {
|
||||
let shutdown = Arc::new(AtomicBool::new(false));
|
||||
pub fn start(&mut self) -> Result<()> {
|
||||
let mut handles = Vec::new();
|
||||
for vcpu in self.vcpus.clone() {
|
||||
let mut run_area = KvmRunArea::new(vcpu, shutdown.clone(), self.io_dispatch.clone())?;
|
||||
let h = thread::spawn(move || run_area.run());
|
||||
for vcpu in self.vcpus.drain(..) {
|
||||
let h = thread::spawn(move || {
|
||||
vcpu.run();
|
||||
});
|
||||
handles.push(h);
|
||||
}
|
||||
|
||||
@ -58,6 +64,11 @@ impl Vm {
|
||||
Ok(())
|
||||
|
||||
}
|
||||
|
||||
pub fn vm_fd(&self) -> &VmFd {
|
||||
self.kvm_vm.vm_fd()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub struct VmSetup <T: ArchSetup> {
|
||||
@ -77,13 +88,15 @@ impl <T: ArchSetup> VmSetup <T> {
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
if self.config.verbose() {
|
||||
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 {
|
||||
self.cmdline.push("quiet");
|
||||
}
|
||||
@ -102,7 +115,7 @@ impl <T: ArchSetup> VmSetup <T> {
|
||||
.map_err(Error::TerminalTermios)?;
|
||||
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_virtio(&mut virtio)
|
||||
.map_err(Error::SetupVirtio)?;
|
||||
@ -114,9 +127,9 @@ impl <T: ArchSetup> VmSetup <T> {
|
||||
self.arch.setup_memory(&self.cmdline, &virtio.pci_irqs())
|
||||
.map_err(Error::ArchError)?;
|
||||
|
||||
let shutdown = Arc::new(AtomicBool::new(false));
|
||||
for id in 0..self.config.ncpus() {
|
||||
let vcpu = vm.kvm.new_vcpu(id).map_err(Error::CreateVmFailed)?;
|
||||
self.arch.setup_vcpu(&vcpu).map_err(Error::ArchError)?;
|
||||
let vcpu = vm.kvm_vm.create_vcpu(id as u64, vm.io_dispatch.clone(), shutdown.clone(), &mut self.arch)?;
|
||||
vm.vcpus.push(vcpu);
|
||||
}
|
||||
Ok(vm)
|
||||
|
100
src/vm/vcpu.rs
Normal file
100
src/vm/vcpu.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user