diff --git a/README.ch.md b/README.ch.md new file mode 100644 index 0000000000000000000000000000000000000000..f7f4b5dbcf5a17ab1db71e4a519154e0d706dbef --- /dev/null +++ b/README.ch.md @@ -0,0 +1,57 @@ +# StratoVirt: +StratoVirt是计算产业中面向云数据中心的企业级虚拟化平台,实现了一套架构统一支持虚拟机、容器、Serverless三种场景。StratoVirt在轻量低噪、软硬协同、Rust语言级安全等方面具备关键技术竞争优势。 + +StratoVirt预留了接口和设计来支持更多特性,未来甚至向标准虚拟化演进。 + +## 如何开始 + +### 环境准备 +在编译StratoVirt前,请确保Rust语言环境和Cargo软件已经安装成功。如果没有安装,请参考以下链接的指导进行安装: + +https://www.rust-lang.org/tools/install + +### 编译软件 +为了编译StratoVirt,需要先克隆代码工程,然后执行编译命令,如下: +```sh +$ git clone https://gitee.com/openeuler/stratovirt.git +$ cd stratovirt +$ cargo build --release +``` +可以在`target/release/stratovirt`路径下找到生成的二进制文件 + +### 运行软件 +为了快速上手StratoVirt,需要准备 +* PE格式的Linux内核镜像 +* EXT4格式的rootfs镜像 + +```shell +# 如果-api-channel的socket文件已经存在,请先删除它 +$ ./target/release/stratovirt \ + -kernel /path/to/kernel \ + -append console=ttyS0 root=/dev/vda reboot=k panic=1 \ + -drive file=/path/to/rootfs,id=rootfs,readonly=off \ + -api-channel unix:/path/to/socket \ + -serial stdio +``` + +关于制作rootfs镜像、编译内核镜像以及编译StratoVirt的详细指导,请参考[StratoVirt Quickstart](./docs/quickstart.md)。 + +StratoVirt所支持更多特性,详细指导请参考[Configuration Guidebook](docs/config_guidebook.md)。 + +## 设计 +想获取更多的StratoVirt核心架构设计信息,请参考[StratoVirt design](./docs/design.md)。 + +## 如何贡献 +我们非常欢迎新贡献者的加入,并且非常乐意为新的贡献者提供指导和帮助。 +StratoVirt遵循Rust语言编程规范,请参考以下链接: + +https://github.com/rust-dev-tools/fmt-rfcs/tree/master/guide + +https://github.com/rust-lang/rust-clippy + +如果你想获取更多关于StratoVirt的信息,请参考以下链接: + +https://gitee.com/openeuler/stratovirt/wikis + +## 许可 +StratoVirt使用Mulan PSL v2开源协议许可 diff --git a/README.md b/README.md index 7ac244519ea6889ab0a571fab9343c2237d45cfb..8301d6214cc004e1c3912eed54ad8e1ad4cbee6a 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,5 @@ # StratoVirt -StratoVirt is an opensource VMM(Virtual Machine Manager) which aims to perform -next generation virtualization. - -Based on Rust programming language, StratoVirt is lightweight, efficient and safe. -StratoVirt reduces memory resource consumption and improves VM startup speed while -retains isolation capability and security capability of traditional virtualization. - -StratoVirt supports communicating with external systems using OCI compatible Interface, -and can be applied to microservices or serverless scenarios. +StratoVirt is an enterprise-level virtualization platform for cloud data centers in the computing industry. It implements a set of architecture that supports three scenarios: virtual machines, containers, and serverless. StratoVirt has key technological competitive advantages in light weight and low noise, software and hardware coordination, and Rust language-level security. StratoVirt reserves interface and design for importing more features, even standard virtualization. diff --git a/address_space/src/listener.rs b/address_space/src/listener.rs index 3810d1dbb0171a6a31f1bd80a9ac665c8cf83946..66a37317f0cde8b80a964a7769d1a9ba6d208160 100644 --- a/address_space/src/listener.rs +++ b/address_space/src/listener.rs @@ -518,11 +518,6 @@ mod test { } } - fn create_vm() -> VmFd { - let kvm = Kvm::new().expect("create kvm failed"); - kvm.create_vm().expect("create vm failed") - } - fn create_ram_range(addr: u64, size: u64, offset_in_region: u64) -> FlatRange { let mem_mapping = Arc::new(HostMemMapping::new(GuestAddress(addr), size, false).unwrap()); FlatRange { @@ -537,7 +532,11 @@ mod test { #[test] fn test_alloc_slot() { - let kml = KvmMemoryListener::new(34, Arc::new(create_vm())); + let kml = match Kvm::new().and_then(|kvm| kvm.create_vm()) { + Ok(vm_fd) => KvmMemoryListener::new(34, Arc::new(vm_fd)), + Err(_) => return, + }; + let host_addr = 0u64; assert_eq!(kml.get_free_slot(0, 100, host_addr).unwrap(), 0); assert_eq!(kml.get_free_slot(200, 100, host_addr).unwrap(), 1); @@ -553,21 +552,23 @@ mod test { #[test] fn test_add_del_ram_region() { - let vm = Arc::new(create_vm()); - let kml = KvmMemoryListener::new(34, vm.clone()); + let kml = match Kvm::new().and_then(|kvm| kvm.create_vm()) { + Ok(vm_fd) => KvmMemoryListener::new(34, Arc::new(vm_fd)), + Err(_) => return, + }; let ram_size = page_size(); let ram_fr1 = create_ram_range(0, ram_size, 0); kml.handle_request(Some(&ram_fr1), None, ListenerReqType::AddRegion) .unwrap(); - //flat-range already added, adding again should make an error + // flat-range already added, adding again should make an error assert!(kml .handle_request(Some(&ram_fr1), None, ListenerReqType::AddRegion) .is_err()); assert!(kml .handle_request(Some(&ram_fr1), None, ListenerReqType::DeleteRegion) .is_ok()); - //flat-range already deleted, deleting again should make an error + // flat-range already deleted, deleting again should make an error assert!(kml .handle_request(Some(&ram_fr1), None, ListenerReqType::DeleteRegion) .is_err()); @@ -575,8 +576,10 @@ mod test { #[test] fn test_add_region_align() { - let vm = Arc::new(create_vm()); - let kml = KvmMemoryListener::new(34, vm.clone()); + let kml = match Kvm::new().and_then(|kvm| kvm.create_vm()) { + Ok(vm_fd) => KvmMemoryListener::new(34, Arc::new(vm_fd)), + Err(_) => return, + }; // flat-range not aligned let page_size = page_size(); @@ -594,8 +597,10 @@ mod test { #[test] fn test_add_del_ioeventfd() { - let vm = Arc::new(create_vm()); - let kml = KvmMemoryListener::new(34, vm.clone()); + let kml = match Kvm::new().and_then(|kvm| kvm.create_vm()) { + Ok(vm_fd) => KvmMemoryListener::new(34, Arc::new(vm_fd)), + Err(_) => return, + }; let evtfd = generate_region_ioeventfd(4, None); assert!(kml @@ -619,7 +624,7 @@ mod test { .handle_request(None, Some(&evtfd), ListenerReqType::AddIoeventfd) .is_ok()); // deleting this ioeventfd returns an error, for the reason that - // function `unregister_ioevent` in kvm-ioctls package don't have an `data_match` argument + // function `unregister_ioevent` in kvm-ioctls package doesn't support `data_match` argument yet. assert!(kml .handle_request(None, Some(&evtfd), ListenerReqType::DeleteIoeventfd) .is_err()); @@ -628,8 +633,10 @@ mod test { #[test] #[cfg(target_arch = "x86_64")] fn test_kvm_io_listener() { - let vm = Arc::new(create_vm()); - let iol = KvmIoListener::new(vm.clone()); + let iol = match Kvm::new().and_then(|kvm| kvm.create_vm()) { + Ok(vm_fd) => KvmIoListener::new(Arc::new(vm_fd)), + Err(_) => return, + }; let evtfd = generate_region_ioeventfd(4, None); assert!(iol diff --git a/device_model/src/cpu/x86_64/mod.rs b/device_model/src/cpu/x86_64/mod.rs index 94ea3671573bfe35f9a120daf69cbc7d84b33bdc..9dece119309fc6f3066d83c7a3686d4ed6e18663 100644 --- a/device_model/src/cpu/x86_64/mod.rs +++ b/device_model/src/cpu/x86_64/mod.rs @@ -384,8 +384,8 @@ mod test { use super::*; use kvm_bindings::kvm_segment; use std::sync::Arc; - #[test] + #[test] fn test_x86_64_cpu() { let code_seg = kvm_segment { base: 0, @@ -429,10 +429,15 @@ mod test { idt_size: 8, pml4_start: 0x0000_9000, }; - let kvm = Kvm::new().unwrap(); - let vm = Arc::new(kvm.create_vm().unwrap()); - /* For `get_lapic` in realize function to work, - you need to create a irq_chip for VM before creating the VCPU. */ + + let vm = if let Ok(vm_fd) = Kvm::new().and_then(|kvm| kvm.create_vm()) { + Arc::new(vm_fd) + } else { + return; + }; + + // For `get_lapic` in realize function to work, + // you need to create a irq_chip for VM before creating the VCPU. vm.create_irq_chip().unwrap(); let vcpu = Arc::new(vm.create_vcpu(0).unwrap()); let mut x86_cpu = X86CPU::new(&vm, 0, 1); diff --git a/device_model/src/interrupt_controller/aarch64/mod.rs b/device_model/src/interrupt_controller/aarch64/mod.rs index 5ae43fbf13cef6e85a47d91747499e1dc1ed2aaa..90867b59957f5171d8108354d8c613bef2bf51bf 100644 --- a/device_model/src/interrupt_controller/aarch64/mod.rs +++ b/device_model/src/interrupt_controller/aarch64/mod.rs @@ -137,8 +137,11 @@ mod tests { use super::*; use kvm_ioctls::Kvm; - let kvm = Kvm::new().unwrap(); - let vm = Arc::new(kvm.create_vm().unwrap()); + let vm = if let Ok(vm_fd) = Kvm::new().and_then(|kvm| kvm.create_vm()) { + Arc::new(vm_fd) + } else { + return; + }; let gic_conf = GICConfig { version: kvm_bindings::kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3.into(), diff --git a/device_model/src/micro_vm/micro_syscall.rs b/device_model/src/micro_vm/micro_syscall.rs index 2ea20a5bac77c91962ab70d99b8a8b7ce19d87aa..ee8b81656c21108a6205890d9e246af6bb6eb767 100644 --- a/device_model/src/micro_vm/micro_syscall.rs +++ b/device_model/src/micro_vm/micro_syscall.rs @@ -52,10 +52,10 @@ const KVM_SET_DEVICE_ATTR: u32 = 0x4018_aee1; /// /// # Notes /// This allowlist limit syscall with: -/// * x86_64-unknown-gnu: 34 syscalls -/// * x86_64-unknown-musl: 33 syscalls -/// * aarch64-unknown-gnu: 33 syscalls -/// * aarch64-unknown-musl: 32 syscalls +/// * x86_64-unknown-gnu: 35 syscalls +/// * x86_64-unknown-musl: 34 syscalls +/// * aarch64-unknown-gnu: 34 syscalls +/// * aarch64-unknown-musl: 33 syscalls /// To reduce performance losses, the syscall rules is ordered by frequency. fn syscall_allow_list() -> Vec { vec![ @@ -76,6 +76,7 @@ fn syscall_allow_list() -> Vec { BpfRule::new(libc::SYS_recvmsg), BpfRule::new(libc::SYS_sendmsg), BpfRule::new(libc::SYS_recvfrom), + BpfRule::new(libc::SYS_mremap), BpfRule::new(libc::SYS_io_setup), BpfRule::new(libc::SYS_brk), BpfRule::new(libc::SYS_fcntl) diff --git a/device_model/src/micro_vm/mod.rs b/device_model/src/micro_vm/mod.rs index 8b5ddf5b2bc43d9a6d9572a0bf9a1440094536f5..0f0cbad115fba7ced1ede7f2cfb3ea39d21ef585 100644 --- a/device_model/src/micro_vm/mod.rs +++ b/device_model/src/micro_vm/mod.rs @@ -204,8 +204,11 @@ impl LightMachine { /// /// * `vm_config` - Represents the configuration for VM. pub fn new(vm_config: VmConfig) -> Result> { - let kvm = Kvm::new()?; - let vm_fd = Arc::new(kvm.create_vm()?); + let kvm = Kvm::new().chain_err(|| "Failed to open /dev/kvm.")?; + let vm_fd = Arc::new( + kvm.create_vm() + .chain_err(|| "KVM: failed to create VM fd failed")?, + ); let sys_mem = AddressSpace::new(Region::init_container_region(u64::max_value()))?; let nr_slots = kvm.get_nr_memslots(); diff --git a/device_model/src/virtio/net.rs b/device_model/src/virtio/net.rs index 05b41b50af64b55fc0f2a71020755c9c3a2008af..3d6dcc3faac4b1aba01d746fde75d8ef76d5e5b8 100644 --- a/device_model/src/virtio/net.rs +++ b/device_model/src/virtio/net.rs @@ -704,3 +704,68 @@ impl VirtioDevice for Net { Ok(()) } } + +#[cfg(test)] +mod tests { + pub use super::super::*; + pub use super::*; + + #[test] + fn test_net_init() { + // test net new method + let mut net = Net::new(); + assert_eq!(net.device_features, 0); + assert_eq!(net.driver_features, 0); + + assert_eq!(net.tap.is_none(), true); + assert_eq!(net.sender.is_none(), true); + assert_eq!(net.net_cfg.mac.is_none(), true); + assert_eq!(net.net_cfg.tap_fd.is_none(), true); + assert_eq!(net.net_cfg.vhost_type.is_none(), true); + assert_eq!(net.net_cfg.vhost_fd.is_none(), true); + + // test net realize method + net.realize().unwrap(); + assert_eq!(net.device_type(), 1); + assert_eq!(net.queue_num(), 2); + assert_eq!(net.queue_size(), 256); + + // test read_config and write_config method + let wriet_data: Vec = vec![7; 4]; + let mut random_data: Vec = vec![0; 4]; + let mut origin_data: Vec = vec![0; 4]; + net.read_config(0x00, &mut origin_data).unwrap(); + + net.write_config(0x00, &wriet_data).unwrap(); + net.read_config(0x00, &mut random_data).unwrap(); + assert_eq!(random_data, wriet_data); + + net.write_config(0x00, &origin_data).unwrap(); + + // test boundary condition of offset and data parameters + let device_config = net.device_config.as_bytes(); + let len = device_config.len() as u64; + + let mut data: Vec = vec![0; 10]; + let offset: u64 = len + 1; + assert_eq!(net.read_config(offset, &mut data).is_ok(), false); + + let offset: u64 = len; + assert_eq!(net.read_config(offset, &mut data).is_ok(), false); + + let offset: u64 = 0; + assert_eq!(net.read_config(offset, &mut data).is_ok(), true); + + let offset: u64 = len; + let mut data: Vec = vec![0; 1]; + assert_eq!(net.write_config(offset, &mut data).is_ok(), false); + + let offset: u64 = len - 1; + let mut data: Vec = vec![0; 1]; + assert_eq!(net.write_config(offset, &mut data).is_ok(), true); + + let offset: u64 = 0; + let mut data: Vec = vec![0; len as usize]; + assert_eq!(net.write_config(offset, &mut data).is_ok(), true); + } +} diff --git a/docs/config_guidebook.md b/docs/config_guidebook.md index 00641e8c0b7d9dea0bcc1464ac3fdaab97e88829..837969a7281d0141f7bc98b4bbf0adb84a221016 100644 --- a/docs/config_guidebook.md +++ b/docs/config_guidebook.md @@ -469,7 +469,7 @@ And you can also restore StratoVirt's **pid number** to a file by: ### 4.2 Seccomp StratoVirt use [prctl(2)](https://man7.org/linux/man-pages/man2/prctl.2.html) to limit the syscalls -in StratoVirt process by default. StratoVirt use only 32 syscalls in aarch64 (33 syscalls in x86_64) after running. +in StratoVirt process by default. StratoVirt use only 33 syscalls in aarch64 (34 syscalls in x86_64) after running. It will make a slight influence on performance to StratoVirt. If you want to disable seccomp, you can run StratoVirt with `-disable-seccomp`. diff --git a/docs/design.md b/docs/design.md index ae2bb12a322d97247445b2238bde033df73a0d21..b686a4bec0c79f5aa3846c5be92d1c5960386c59 100644 --- a/docs/design.md +++ b/docs/design.md @@ -13,7 +13,7 @@ StratoVirt reserves interface and design for importing more features, even stand - Fast cold boot: Benefit from the minimalist design, StratoVirt could boot a microVM in 50ms. - Low memory overhead: StratoVirt works with a memory footprint at 3MB. - IO enhancement: StratoVirt offers normal IO ability with minimalist IO device emulation. -- OCI compatibility: StratoVirt offers OCI-compatible interface,which connects to Kubernetes ecosystem perfectly. +- OCI compatibility: StratoVirt works with isula and kata container, and can be integrated in Kubernetes ecosystem perfectly. - Multi-platform support: Fully support for Intel and Arm platform. - Expansibility: StratoVirt reserves interface and design for importing more features, even expand to standard virtualization support. diff --git a/src/main.rs b/src/main.rs index 9fb043d5946d14c349af0912ac9f38d630aa23be..fc8078d3098af584710b542be7bafb759aba67fc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,14 +45,13 @@ error_chain! { quick_main!(run); -#[allow(clippy::cast_ptr_alignment)] fn run() -> Result<()> { let cmd_args = create_args_parser().get_matches()?; if let Some(logfile_path) = cmd_args.value_of("display log") { if logfile_path.is_empty() { logger::init_logger_with_env(Some(Box::new(std::io::stdout()))) - .expect("logger init failed!"); + .chain_err(|| "Failed to init logger.")?; } else { let logfile = std::fs::OpenOptions::new() .read(false) @@ -60,11 +59,11 @@ fn run() -> Result<()> { .append(true) .create(true) .mode(0o640) - .open(logfile_path)?; - logger::init_logger_with_env(Some(Box::new(logfile))).expect("logger init failed!"); + .open(logfile_path) + .chain_err(|| "Failed to open log file")?; + logger::init_logger_with_env(Some(Box::new(logfile))) + .chain_err(|| "Failed to init logger.")?; } - } else { - logger::init_logger_with_env(None).expect("logger init failed!"); } std::panic::set_hook(Box::new(|panic_msg| { @@ -128,7 +127,8 @@ fn real_main(cmd_args: &arg_parser::ArgMatches) -> Result<()> { MainLoop::update_event(EventNotifierHelper::internal_notifiers(Arc::new( Mutex::new(api_socket), - )))?; + ))) + .chain_err(|| "Failed to add api event to MainLoop")?; vm.realize()?; vm.vm_start( @@ -141,7 +141,7 @@ fn real_main(cmd_args: &arg_parser::ArgMatches) -> Result<()> { } loop { - if !MainLoop::run()? { + if !MainLoop::run().chain_err(|| "MainLoop exits unexpectedly: error occurs")? { break; } }