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",
|
"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"
|
||||||
|
@ -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
56
ph-init/Cargo.lock
generated
@ -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"
|
||||||
|
@ -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")
|
||||||
|
@ -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"))
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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 vm;
|
||||||
mod memory;
|
mod memory;
|
||||||
mod devices;
|
mod devices;
|
||||||
mod kvm;
|
|
||||||
mod virtio;
|
mod virtio;
|
||||||
mod disk;
|
mod disk;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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_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))
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
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::{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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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::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)
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
@ -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(), ®s)?;
|
vcpu.set_sregs(®s)
|
||||||
|
.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(®s)
|
vcpu.set_regs(®s)
|
||||||
.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() }
|
|
||||||
}
|
|
||||||
}
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
18
src/vm/io.rs
18
src/vm/io.rs
@ -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 */
|
||||||
|
@ -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
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");
|
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};
|
||||||
|
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 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
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