diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ac42316d..b0620192 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -57,5 +57,9 @@ jobs: - name: Download prebuilt user image run: cd user && make sfsimg ARCH=${{ matrix.arch }} PREBUILT=1 && cd .. + - name: Build kernel run: cd kernel && make build ARCH=${{ matrix.arch }} && cd .. + - name: Build kernel with hypervisor + if: runner.os == 'Linux' && matrix.arch == 'x86_64' + run: cd kernel && make build ARCH=${{ matrix.arch }} HYPERVISOR=on && cd .. diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index ee529942..eb3f3631 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -26,9 +26,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.10" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" +checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" dependencies = [ "memchr", ] @@ -48,16 +48,16 @@ name = "apic" version = "0.1.0" source = "git+https://github.com/rcore-os/apic-rs?rev=fb86bd7#fb86bd7c798608a18cbb48755637d97d4266eb89" dependencies = [ - "bit_field 0.10.0", + "bit_field 0.10.1", "bitflags", "x86", ] [[package]] name = "autocfg" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "bare-metal" @@ -78,6 +78,21 @@ dependencies = [ "volatile", ] +[[package]] +name = "bit-set" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0dc55f2d8a1a85650ac47858bb001b4c0dd73d79e3c455a842925e68d29cd3" + [[package]] name = "bit_field" version = "0.9.0" @@ -86,9 +101,9 @@ checksum = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" [[package]] name = "bit_field" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" +checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" [[package]] name = "bitflags" @@ -96,6 +111,14 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "bitmap-allocator" +version = "0.1.0" +source = "git+https://github.com/rcore-os/bitmap-allocator#03bd9909d0dc85e99f5559b97a163ab81073df83" +dependencies = [ + "bit_field 0.9.0", +] + [[package]] name = "bitmap-allocator" version = "0.1.0" @@ -141,9 +164,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.54" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311" +checksum = "ef611cc68ff783f18535d77ddd080185275713d852c4f5cbb6122c462a7a825c" [[package]] name = "cfg-if" @@ -170,9 +193,9 @@ source = "git+https://github.com/rcore-os/device_tree-rs?rev=eee2c23#eee2c23d50a [[package]] name = "either" -version = "1.5.3" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "embedded-graphics" @@ -206,9 +229,9 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "affc17579b132fc2461adf7c575cc6e8b134ebca52c51f5411388965227dc695" +checksum = "3ed85775dcc68644b5c950ac06a2b23768d3bc9390464151aaf27136998dcf9e" dependencies = [ "cfg-if", "libc", @@ -239,15 +262,15 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.71" +version = "0.2.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49" +checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" [[package]] name = "log" -version = "0.4.8" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" dependencies = [ "cfg-if", ] @@ -269,7 +292,7 @@ name = "mips" version = "0.3.0" source = "git+https://github.com/Harry-Chen/rust-mips?rev=3b828a2#3b828a2afed97f2769a66cf9cd8239a285804dc0" dependencies = [ - "bit_field 0.10.0", + "bit_field 0.10.1", "bitflags", ] @@ -303,9 +326,9 @@ dependencies = [ [[package]] name = "num-derive" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8b15b261814f992e33760b1fca9fe8b693d8a65299f20c9901688636cfb746" +checksum = "6f09b9841adb6b5e1f89ef7087ea636e0fd94b2851f887c1e3eb5d5f8228fab3" dependencies = [ "proc-macro2", "quote", @@ -354,10 +377,16 @@ dependencies = [ ] [[package]] -name = "paste" -version = "0.1.16" +name = "numeric-enum-macro" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d508492eeb1e5c38ee696371bf7b9fc33c83d46a7d451606b96458fbbbdc2dec" +checksum = "300e4bdb6b46b592948e700ea1ef24a4296491f6a0ee722b258040abd15a3714" + +[[package]] +name = "paste" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880" dependencies = [ "paste-impl", "proc-macro-hack", @@ -365,21 +394,18 @@ dependencies = [ [[package]] name = "paste-impl" -version = "0.1.16" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84f328a6a63192b333fce5fbb4be79db6758a4d518dfac6d54412f1492f72d32" +checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6" dependencies = [ "proc-macro-hack", - "proc-macro2", - "quote", - "syn", ] [[package]] name = "pc-keyboard" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c48392db76c4e9a69e0b3be356c5f97ebb7b14413c5e4fd0af4755dbf86e2fce" +checksum = "5c6f2d937e3b8d63449b01401e2bae4041bc9dd1129c2e3e0d239407cf6635ac" [[package]] name = "pci" @@ -391,15 +417,15 @@ dependencies = [ [[package]] name = "proc-macro-hack" -version = "0.5.16" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" +checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598" [[package]] name = "proc-macro2" -version = "1.0.18" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" +checksum = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c" dependencies = [ "unicode-xid", ] @@ -450,9 +476,9 @@ dependencies = [ "aml", "apic", "bcm2837", - "bit_field 0.10.0", + "bit_field 0.10.1", "bitflags", - "bitmap-allocator", + "bitmap-allocator 0.1.0 (git+https://github.com/rcore-os/bitmap-allocator?rev=03bd9909)", "bitvec", "buddy_system_allocator", "compression", @@ -480,6 +506,7 @@ dependencies = [ "rcore-memory", "riscv", "rlibc", + "rvm", "smoltcp", "spin", "trapframe", @@ -563,9 +590,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "regex" @@ -600,7 +627,7 @@ version = "0.5.6" source = "git+https://github.com/rcore-os/riscv?rev=38f3786#38f3786966ba15cc76977375162e3f3622a30f4b" dependencies = [ "bare-metal", - "bit_field 0.10.0", + "bit_field 0.10.1", "bitflags", "log", "riscv-target", @@ -633,15 +660,43 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bba175698996010c4f6dce5e7f173b6eb781fce25d2cfc45e27091ce0b79f6" +checksum = "b9bdc5e856e51e685846fb6c13a1f5e5432946c2c90501bdc76a1319f19e29da" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "rvm" +version = "1.0.1" +source = "git+https://github.com/rcore-os/RVM?rev=939eb0a#939eb0aafd100e9944e4f2fe90eebfa8149d2b85" +dependencies = [ + "bit-set", + "bit_field 0.10.1", + "bitflags", + "bitmap-allocator 0.1.0 (git+https://github.com/rcore-os/bitmap-allocator)", + "lazy_static", + "log", + "numeric-enum-macro", + "raw-cpuid", + "rvm_macros", + "spin", + "x86", + "x86_64", +] + +[[package]] +name = "rvm_macros" +version = "0.1.0" +source = "git+https://github.com/rcore-os/RVM?rev=939eb0a#939eb0aafd100e9944e4f2fe90eebfa8149d2b85" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "semver" version = "0.9.0" @@ -682,9 +737,9 @@ checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3" [[package]] name = "syn" -version = "1.0.31" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" +checksum = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b" dependencies = [ "proc-macro2", "quote", @@ -737,7 +792,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85061f4e43545a613c0da6b87725bf23f8da8613cf2473719c4f71a270c4ce8a" dependencies = [ - "bit_field 0.10.0", + "bit_field 0.10.1", ] [[package]] @@ -754,9 +809,9 @@ dependencies = [ [[package]] name = "uefi-macros" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a69fa8dd920e84d783769c44560484ade81f6c765cde2e1cc46c754ddf95947" +checksum = "3dcca10ca861f34a320d178f3fdb29ffbf05087fc2c70d2a99860e3329bee1a8" dependencies = [ "proc-macro2", "quote", @@ -765,9 +820,9 @@ dependencies = [ [[package]] name = "unicode-xid" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "usize_conversions" @@ -799,9 +854,9 @@ dependencies = [ [[package]] name = "volatile" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6af0edf5b4faacc31fc51159244d78d65ec580f021afcef7bd53c04aeabc7f29" +checksum = "f6b06ad3ed06fef1713569d547cdbdb439eafed76341820fb0e0344f29a41945" [[package]] name = "vte" @@ -814,9 +869,9 @@ dependencies = [ [[package]] name = "winapi" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", @@ -846,16 +901,16 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2786ac694ed572ab5d2bbcd9e188805dba26b3501973dd69718914fb3d4a5a69" dependencies = [ - "bit_field 0.10.0", + "bit_field 0.10.1", "bitflags", "raw-cpuid", ] [[package]] name = "x86_64" -version = "0.11.0" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "365de37eb7c6da582cbb510dd0f3f1235d24ff6309a8a96e8a9909cc9bfd608f" +checksum = "aa580d2cf2a6a8c55f6283d6d06271b1ccab4d93cb3741edab290d5408d848c4" dependencies = [ "bit_field 0.9.0", "bitflags", diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 642e456d..c97cf643 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -36,6 +36,8 @@ link_user = [] run_cmdline = [] # Add performance profiling profile = [] +# Rcore Virtual machine +hypervisor = ["rvm"] [profile.dev] # MUST >= 2 : Enable RVO to avoid stack overflow @@ -74,6 +76,7 @@ virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "df volatile = "0.2" woke = "0.0.2" xmas-elf = "0.7" +rvm = { git = "https://github.com/rcore-os/RVM", rev = "939eb0a", optional = true } [target.'cfg(target_arch = "x86_64")'.dependencies] apic = { git = "https://github.com/rcore-os/apic-rs", rev = "fb86bd7" } @@ -93,4 +96,4 @@ bcm2837 = { git = "https://github.com/rcore-os/bcm2837", version = "2.5.1", opti [target.'cfg(target_arch = "mips")'.dependencies] mips = { git = "https://github.com/Harry-Chen/rust-mips", rev = "3b828a2" } -paste = "0.1" \ No newline at end of file +paste = "0.1" diff --git a/kernel/Makefile b/kernel/Makefile index ecb17c73..38213696 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -26,6 +26,8 @@ # PCI_PASSTHRU = 0000:00:00.1 [ x86_64 only] Passthrough the specified PCI device # INIT = /bin/ls [riscv64 only] Run specified program instead of user shell # EXTRA_NIC = on | off [ x86_64 only] Add an additional e1000 nic +# ACCEL = on | off [ x86_64 only] Enable/disable kvm/hvf acceleration +# HYPERVISOR = on | off [ x86_64 only] Enable/disable the RVM hypervisor, and set ACCEL to on # FEATURES = profile | ... Add additional features ARCH ?= riscv64 @@ -36,6 +38,8 @@ SMP ?= 4 PCI_PASSTHRU ?= INIT ?= EXTRA_NIC ?= off +ACCEL ?= off +HYPERVISOR ?= off qemu := qemu-system-$(ARCH) target := $(ARCH) @@ -93,10 +97,6 @@ qemu_net_opts += \ qemu_ui_opts += \ -vga std endif -ifeq ($(shell uname), Darwin) -qemu_opts += \ - -machine accel=hvf -endif ifeq ($(EXTRA_NIC), on) qemu_net_opts += \ -netdev type=tap,id=net1,script=no,downscript=no \ @@ -171,6 +171,19 @@ qemu_opts += $(qemu_net_opts) qemu := sudo $(qemu) endif +ifeq ($(HYPERVISOR), on) +FEATURES += hypervisor +ACCEL = on +endif + +ifeq ($(ACCEL), on) +ifeq ($(shell uname), Darwin) +qemu_opts += -accel hvf +else +qemu_opts += -accel kvm -cpu host +endif +endif + ### build args ### ifeq ($(GRAPHIC), off) FEATURES += nographic diff --git a/kernel/src/fs/mod.rs b/kernel/src/fs/mod.rs index df174e10..ef9af81f 100644 --- a/kernel/src/fs/mod.rs +++ b/kernel/src/fs/mod.rs @@ -83,6 +83,9 @@ lazy_static! { devfs.add("fb0", Arc::new(Fbdev::default())).expect("failed to mknod /dev/fb0"); devfs.add("shm", Arc::new(ShmINode::default())).expect("failed to mkdir shm"); + #[cfg(feature = "hypervisor")] + devfs.add("rvm", Arc::new(crate::rvm::RvmINode::new())).expect("failed to mknod /dev/rvm"); + // mount DevFS at /dev let dev = root.find(true, "dev").unwrap_or_else(|_| { root.create("dev", FileType::Dir, 0o666).expect("failed to mkdir /dev") diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 18e32782..06f57dfe 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -21,8 +21,6 @@ #![allow(unused_mut)] #![allow(unused_variables)] #![allow(unused_imports)] -#![allow(unreachable_patterns)] -#![allow(unused_assignments)] #![no_std] // just keep it ... @@ -54,6 +52,8 @@ pub mod lkm; pub mod memory; pub mod net; pub mod process; +#[cfg(feature = "hypervisor")] +pub mod rvm; pub mod shell; pub mod signal; pub mod sync; diff --git a/kernel/src/rvm/inode.rs b/kernel/src/rvm/inode.rs new file mode 100644 index 00000000..b5955965 --- /dev/null +++ b/kernel/src/rvm/inode.rs @@ -0,0 +1,367 @@ +//! Implement INode for Rcore Virtual Machine + +use alloc::collections::BTreeMap; +use core::any::Any; +use core::convert::{TryFrom, TryInto}; +use core::mem::size_of; +use spin::RwLock; + +use rcore_fs::vfs::*; +use rvm::{RvmError, RvmExitPacket, RvmResult, TrapKind, VcpuIo, VcpuReadWriteKind, VcpuState}; + +use super::into_fs_error; +use super::structs::{Guest, Vcpu}; +use crate::syscall::{UserInOutPtr, UserInPtr, UserOutPtr}; + +const MAX_GUEST_NUM: usize = 64; +const MAX_VCPU_NUM: usize = 64; + +const RVM_IO: u32 = 0xAE00; +const RVM_GUEST_CREATE: u32 = RVM_IO + 0x01; +const RVM_GUEST_ADD_MEMORY_REGION: u32 = RVM_IO + 0x02; +const RVM_GUEST_SET_TRAP: u32 = RVM_IO + 0x03; +const RVM_VCPU_CREATE: u32 = RVM_IO + 0x11; +const RVM_VCPU_RESUME: u32 = RVM_IO + 0x12; +const RVM_VCPU_READ_STATE: u32 = RVM_IO + 0x13; +const RVM_VCPU_WRITE_STATE: u32 = RVM_IO + 0x14; +const RVM_VCPU_INTERRUPT: u32 = RVM_IO + 0x15; + +pub struct RvmINode { + guests: RwLock>, + vcpus: RwLock>, +} + +#[repr(C)] +#[derive(Debug)] +struct RvmVcpuCreateArgs { + vmid: u16, + entry: u64, +} + +#[repr(C)] +#[derive(Debug)] +struct RvmGuestAddMemoryRegionArgs { + vmid: u16, + guest_start_paddr: u64, + memory_size: u64, +} + +#[repr(C)] +#[derive(Debug)] +struct RvmGuestSetTrapArgs { + vmid: u16, + kind: u32, + addr: u64, + size: u64, + key: u64, +} + +#[repr(C)] +#[derive(Debug)] +struct RvmVcpuResumeArgs { + vcpu_id: u16, + packet: RvmExitPacket, +} + +#[repr(C)] +#[derive(Debug)] +struct RvmVcpuStateArgs { + vcpu_id: u16, + kind: u32, + user_buf_ptr: u64, + buf_size: u64, +} + +#[repr(C)] +#[derive(Debug)] +struct RvmVcpuInterruptArgs { + vcpu_id: u16, + vector: u32, +} + +impl INode for RvmINode { + fn read_at(&self, _offset: usize, _buf: &mut [u8]) -> Result { + Err(FsError::NotSupported) + } + fn write_at(&self, _offset: usize, _buf: &[u8]) -> Result { + Err(FsError::NotSupported) + } + fn poll(&self) -> Result { + Ok(PollStatus { + read: false, + write: false, + error: false, + }) + } + fn metadata(&self) -> Result { + Ok(Metadata { + dev: 0, + inode: 0, + size: 0, + blk_size: 0, + blocks: 0, + atime: Timespec { sec: 0, nsec: 0 }, + mtime: Timespec { sec: 0, nsec: 0 }, + ctime: Timespec { sec: 0, nsec: 0 }, + type_: FileType::CharDevice, + mode: 0o660, + nlinks: 1, + uid: 0, + gid: 0, + rdev: make_rdev(10, 232), // misc major, kvm minor + }) + } + fn io_control(&self, cmd: u32, data: usize) -> Result { + match cmd { + RVM_GUEST_CREATE => { + info!("[RVM] ioctl RVM_GUEST_CREATE"); + self.guest_create().map_err(into_fs_error) + } + RVM_GUEST_ADD_MEMORY_REGION => { + let args = UserInPtr::::from(data) + .read() + .or(Err(FsError::InvalidParam))?; + info!("[RVM] ioctl RVM_GUEST_ADD_MEMORY_REGION {:x?}", args); + self.guest_add_memory_region( + args.vmid as usize, + args.guest_start_paddr as usize, + args.memory_size as usize, + ) + .map_err(into_fs_error) + } + RVM_GUEST_SET_TRAP => { + let args = UserInPtr::::from(data) + .read() + .or(Err(FsError::InvalidParam))?; + info!("[RVM] ioctl RVM_GUEST_SET_TRAP {:x?}", args); + self.guest_set_trap( + args.vmid as usize, + args.kind.try_into().map_err(into_fs_error)?, + args.addr as usize, + args.size as usize, + args.key, + ) + .map_err(into_fs_error)?; + Ok(0) + } + RVM_VCPU_CREATE => { + let args = UserInPtr::::from(data) + .read() + .or(Err(FsError::InvalidParam))?; + info!("[RVM] ioctl RVM_VCPU_CREATE {:x?}", args); + self.vcpu_create(args.vmid as usize, args.entry) + .map_err(into_fs_error) + } + RVM_VCPU_RESUME => { + let mut ptr = UserInOutPtr::::from(data); + let mut args = ptr.read().or(Err(FsError::InvalidParam))?; + info!("[RVM] ioctl RVM_VCPU_RESUME {:#x}", args.vcpu_id); + args.packet = self + .vcpu_resume(args.vcpu_id as usize) + .map_err(into_fs_error)?; + ptr.write(args).or(Err(FsError::DeviceError))?; + Ok(0) + } + RVM_VCPU_READ_STATE => { + let args = UserInPtr::::from(data) + .read() + .or(Err(FsError::InvalidParam))?; + info!("[RVM] ioctl RVM_VCPU_READ_STATE {:#x?}", args); + self.vcpu_read_state( + args.vcpu_id as usize, + args.kind, + args.user_buf_ptr as usize, + args.buf_size as usize, + ) + .map_err(into_fs_error)?; + Ok(0) + } + RVM_VCPU_WRITE_STATE => { + let args = UserInPtr::::from(data) + .read() + .or(Err(FsError::InvalidParam))?; + info!("[RVM] ioctl RVM_VCPU_WRITE_STATE {:#x?}", args); + self.vcpu_write_state( + args.vcpu_id as usize, + args.kind, + args.user_buf_ptr as usize, + args.buf_size as usize, + ) + .map_err(into_fs_error)?; + Ok(0) + } + RVM_VCPU_INTERRUPT => { + let args = UserInPtr::::from(data) + .read() + .or(Err(FsError::InvalidParam))?; + info!("[RVM] ioctl RVM_VCPU_INTERRUPT {:#x?}", args); + self.vcpu_interrupt(args.vcpu_id as usize, args.vector) + .map_err(into_fs_error)?; + Ok(0) + } + _ => { + warn!("[RVM] invalid ioctl number {:#x}", cmd); + Err(FsError::InvalidParam) + } + } + } + fn mmap(&self, area: MMapArea) -> Result<()> { + info!("[RVM] mmap {:x?}", area); + Err(FsError::NotSupported) + } + fn as_any_ref(&self) -> &dyn Any { + self + } +} + +impl RvmINode { + pub fn new() -> Self { + Self { + guests: RwLock::new(BTreeMap::new()), + vcpus: RwLock::new(BTreeMap::new()), + } + } + + fn get_free_vmid(&self) -> usize { + (1..).find(|i| !self.guests.read().contains_key(i)).unwrap() + } + + fn add_guest(&self, guest: Guest, vmid_option: Option) -> usize { + let vmid = vmid_option.unwrap_or_else(|| self.get_free_vmid()); + self.guests.write().insert(vmid, guest); + vmid + } + + fn get_free_vcpu_id(&self) -> usize { + (1..).find(|i| !self.vcpus.read().contains_key(i)).unwrap() + } + + fn add_vcpu(&self, vcpu: Vcpu, vcpu_id_option: Option) -> usize { + let vcpu_id = vcpu_id_option.unwrap_or_else(|| self.get_free_vcpu_id()); + self.vcpus.write().insert(vcpu_id, vcpu); + vcpu_id + } + + fn guest_create(&self) -> RvmResult { + if rvm::check_hypervisor_feature() { + let vmid = self.get_free_vmid(); + if vmid >= MAX_GUEST_NUM { + warn!("[RVM] too many guests ({})", MAX_GUEST_NUM); + return Err(RvmError::NoMemory); + } + self.add_guest(Guest::new()?, Some(vmid)); + Ok(vmid) + } else { + warn!("[RVM] no hardware support"); + Err(RvmError::NotSupported) + } + } + + fn guest_add_memory_region(&self, vmid: usize, gpaddr: usize, size: usize) -> RvmResult { + if let Some(guest) = self.guests.read().get(&vmid) { + Ok(guest.add_memory_region(gpaddr, size)?) + } else { + Err(RvmError::InvalidParam) + } + } + + fn guest_set_trap( + &self, + vmid: usize, + kind: TrapKind, + addr: usize, + size: usize, + key: u64, + ) -> RvmResult<()> { + if let Some(guest) = self.guests.read().get(&vmid) { + guest.inner.set_trap(kind, addr, size, None, key) + } else { + Err(RvmError::InvalidParam) + } + } + + fn vcpu_create(&self, vmid: usize, entry: u64) -> RvmResult { + if let Some(guest) = self.guests.read().get(&vmid) { + let vcpu_id = self.get_free_vcpu_id(); + if vcpu_id >= MAX_VCPU_NUM { + warn!("[RVM] too many vcpus ({})", MAX_VCPU_NUM); + return Err(RvmError::NoMemory); + } + let vcpu = Vcpu::new(entry, guest.inner.clone())?; + self.add_vcpu(vcpu, Some(vcpu_id)); + Ok(vcpu_id) + } else { + Err(RvmError::InvalidParam) + } + } + + fn vcpu_resume(&self, vcpu_id: usize) -> RvmResult { + if let Some(vcpu) = self.vcpus.write().get_mut(&vcpu_id) { + Ok(vcpu.inner.lock().resume()?) + } else { + Err(RvmError::InvalidParam) + } + } + + fn vcpu_read_state( + &self, + vcpu_id: usize, + kind: u32, + user_buf_ptr: usize, + buf_size: usize, + ) -> RvmResult<()> { + if kind != VcpuReadWriteKind::VcpuState as u32 || buf_size != size_of::() { + return Err(RvmError::InvalidParam); + } + if let Some(vcpu) = self.vcpus.read().get(&vcpu_id) { + let mut ptr = UserOutPtr::::from(user_buf_ptr); + let state = vcpu.inner.lock().read_state()?; + ptr.write(state).or(Err(RvmError::InvalidParam))?; + Ok(()) + } else { + Err(RvmError::InvalidParam) + } + } + + fn vcpu_write_state( + &self, + vcpu_id: usize, + kind: u32, + user_buf_ptr: usize, + buf_size: usize, + ) -> RvmResult<()> { + if let Some(vcpu) = self.vcpus.write().get_mut(&vcpu_id) { + match VcpuReadWriteKind::try_from(kind) { + Ok(VcpuReadWriteKind::VcpuState) => { + if buf_size != size_of::() { + return Err(RvmError::InvalidParam); + } + let ptr = UserInPtr::::from(user_buf_ptr); + let state = ptr.read().or(Err(RvmError::InvalidParam))?; + vcpu.inner.lock().write_state(&state) + } + Ok(VcpuReadWriteKind::VcpuIo) => { + if buf_size != size_of::() { + return Err(RvmError::InvalidParam); + } + let ptr = UserInPtr::::from(user_buf_ptr); + let state = ptr.read().or(Err(RvmError::InvalidParam))?; + vcpu.inner.lock().write_io_state(&state) + } + Err(_) => return Err(RvmError::InvalidParam), + } + } else { + Err(RvmError::InvalidParam) + } + } + + fn vcpu_interrupt(&self, vcpu_id: usize, vector: u32) -> RvmResult<()> { + if let Some(vcpu) = self.vcpus.write().get_mut(&vcpu_id) { + vcpu.inner.lock().virtual_interrupt(vector) + } else { + Err(RvmError::InvalidParam) + } + } + + // TODO: remove guest & vcpu +} diff --git a/kernel/src/rvm/memory.rs b/kernel/src/rvm/memory.rs new file mode 100644 index 00000000..2e80cb7e --- /dev/null +++ b/kernel/src/rvm/memory.rs @@ -0,0 +1,97 @@ +//! Used for delay mapping host's virtual memory to guest's physical memory + +use alloc::{boxed::Box, sync::Arc}; + +use rvm::RvmPageTable; +use rvm::{DefaultGuestPhysMemorySet, GuestMemoryAttr, GuestPhysAddr, HostVirtAddr}; + +use rcore_memory::memory_set::handler::{FrameAllocator, MemoryHandler}; +use rcore_memory::memory_set::MemoryAttr; +use rcore_memory::paging::PageTable; + +#[derive(Debug, Clone)] +pub struct RvmPageTableHandlerDelay { + guest_start_paddr: GuestPhysAddr, + host_start_vaddr: HostVirtAddr, + gpm: Arc, + allocator: T, +} + +impl RvmPageTableHandlerDelay { + pub fn new( + guest_start_paddr: GuestPhysAddr, + host_start_vaddr: HostVirtAddr, + gpm: Arc, + allocator: T, + ) -> Self { + Self { + guest_start_paddr, + host_start_vaddr, + gpm, + allocator, + } + } +} + +impl MemoryHandler for RvmPageTableHandlerDelay { + fn box_clone(&self) -> Box { + Box::new(self.clone()) + } + + fn map(&self, pt: &mut dyn PageTable, addr: HostVirtAddr, attr: &MemoryAttr) { + let entry = pt.map(addr, 0); + entry.set_present(false); + attr.apply(entry); + } + + fn unmap(&self, pt: &mut dyn PageTable, addr: HostVirtAddr) { + let entry = pt.get_entry(addr).expect("failed to get entry"); + // PageTable::unmap requires page to be present + entry.set_present(true); + pt.unmap(addr); + } + + fn clone_map( + &self, + pt: &mut dyn PageTable, + src_pt: &mut dyn PageTable, + addr: HostVirtAddr, + attr: &MemoryAttr, + ) { + let entry = src_pt.get_entry(addr).expect("failed to get entry"); + if entry.present() { + // eager map and copy data + let data = src_pt.get_page_slice_mut(addr); + let target = self.allocator.alloc().expect("failed to alloc frame"); + let entry = pt.map(addr, target); + attr.apply(entry); + pt.get_page_slice_mut(addr).copy_from_slice(data); + } else { + // delay map + self.map(pt, addr, attr); + } + } + + fn handle_page_fault(&self, pt: &mut dyn PageTable, addr: HostVirtAddr) -> bool { + let entry = pt.get_entry(addr).expect("failed to get entry"); + if entry.present() { + // not a delay case + return false; + } + + let guest_paddr = addr - self.host_start_vaddr + self.guest_start_paddr; + let mut rvm_pt = self.gpm.rvm_page_table.lock(); + let mut target = rvm_pt.query(guest_paddr).unwrap_or(0); + if target == 0 { + target = self.allocator.alloc().expect("failed to alloc frame"); + } + rvm_pt + .map(guest_paddr, target, GuestMemoryAttr::default()) + .expect("failed to create GPA -> HPA mapping"); + + entry.set_target(target); + entry.set_present(true); + entry.update(); + true + } +} diff --git a/kernel/src/rvm/mod.rs b/kernel/src/rvm/mod.rs new file mode 100644 index 00000000..8bb03eb9 --- /dev/null +++ b/kernel/src/rvm/mod.rs @@ -0,0 +1,55 @@ +//! Implement hypervisor using rvm crate + +#![deny(non_upper_case_globals)] +#![deny(dead_code)] +#![deny(unused_mut)] +#![deny(unused_variables)] +#![deny(unused_imports)] + +use rcore_fs::vfs::FsError; +use rvm::RvmError; + +mod inode; +mod memory; +mod structs; + +pub use inode::RvmINode; + +fn into_fs_error(e: RvmError) -> FsError { + match e { + RvmError::Internal => FsError::DeviceError, + RvmError::NotSupported => FsError::NotSupported, + RvmError::NoMemory => FsError::NoDeviceSpace, + RvmError::InvalidParam => FsError::InvalidParam, + RvmError::OutOfRange => FsError::InvalidParam, + RvmError::BadState => FsError::DeviceError, + RvmError::NotFound => FsError::InvalidParam, + } +} + +mod rvm_extern_fn { + use crate::memory::{alloc_frame, dealloc_frame, phys_to_virt}; + #[rvm::extern_fn(alloc_frame)] + fn rvm_alloc_frame() -> Option { + alloc_frame() + } + + #[rvm::extern_fn(dealloc_frame)] + fn rvm_dealloc_frame(paddr: usize) { + dealloc_frame(paddr) + } + + #[rvm::extern_fn(phys_to_virt)] + fn rvm_phys_to_virt(paddr: usize) -> usize { + phys_to_virt(paddr) + } + + #[cfg(target_arch = "x86_64")] + #[rvm::extern_fn(x86_all_traps_handler_addr)] + unsafe fn rvm_x86_all_traps_handler_addr() -> usize { + extern "C" { + fn __alltraps(); + } + __alltraps as usize + } +} diff --git a/kernel/src/rvm/structs.rs b/kernel/src/rvm/structs.rs new file mode 100644 index 00000000..7934b8ba --- /dev/null +++ b/kernel/src/rvm/structs.rs @@ -0,0 +1,54 @@ +//! Wrappers of rvm::Guest and rvm::Vcpu + +use alloc::sync::Arc; +use spin::Mutex; + +use rcore_memory::{memory_set::MemoryAttr, PAGE_SIZE}; +use rvm::{DefaultGuestPhysMemorySet, GuestPhysAddr, HostVirtAddr, RvmResult}; +use rvm::{Guest as GuestInner, Vcpu as VcpuInner}; + +use super::memory::RvmPageTableHandlerDelay; +use crate::memory::GlobalFrameAlloc; + +pub(super) struct Guest { + gpm: Arc, + pub(super) inner: Arc, +} + +pub(super) struct Vcpu { + pub(super) inner: Mutex, +} + +impl Guest { + pub fn new() -> RvmResult { + let gpm = DefaultGuestPhysMemorySet::new(); + Ok(Self { + inner: GuestInner::new(gpm.clone())?, + gpm, + }) + } + + pub fn add_memory_region(&self, gpaddr: GuestPhysAddr, size: usize) -> RvmResult { + self.inner.add_memory_region(gpaddr, size, None)?; + let thread = crate::process::current_thread().unwrap(); + let hvaddr = thread.vm.lock().find_free_area(PAGE_SIZE, size); + let handler = + RvmPageTableHandlerDelay::new(gpaddr, hvaddr, self.gpm.clone(), GlobalFrameAlloc); + thread.vm.lock().push( + hvaddr, + hvaddr + size, + MemoryAttr::default().user().writable(), + handler, + "rvm_guest_physical", + ); + Ok(hvaddr) + } +} + +impl Vcpu { + pub fn new(entry: u64, guest: Arc) -> RvmResult { + Ok(Self { + inner: Mutex::new(VcpuInner::new(entry, guest)?), + }) + } +} diff --git a/user b/user index 63342746..94620bd6 160000 --- a/user +++ b/user @@ -1 +1 @@ -Subproject commit 63342746297b9694676c82a716601d736ebab1a1 +Subproject commit 94620bd696ac61b40129d845671cde83001fdf61