From 539367b58c8ad24c8f455826e11781abad0ec4b4 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Thu, 28 Feb 2019 14:16:58 +0100 Subject: [PATCH] cloud-hypervisor: Initial kernel booting implementation Signed-off-by: Samuel Ortiz --- Cargo.lock | 33 ++++++ src/main.rs | 2 +- vmm/Cargo.toml | 2 + vmm/src/lib.rs | 120 ++++------------------ vmm/src/vm.rs | 271 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 327 insertions(+), 101 deletions(-) create mode 100644 vmm/src/vm.rs diff --git a/Cargo.lock b/Cargo.lock index 7d0c8d870..530fdeacf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,6 +3,22 @@ name = "ansi_term" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "arch" +version = "0.1.0" +dependencies = [ + "arch_gen 0.1.0", + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "kvm-bindings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "kvm-ioctls 0.1.0 (git+https://github.com/rust-vmm/kvm-ioctls)", + "libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)", + "vm-memory 0.1.0 (git+https://github.com/rust-vmm/vm-memory)", +] + +[[package]] +name = "arch_gen" +version = "0.1.0" + [[package]] name = "atty" version = "0.2.11" @@ -18,6 +34,11 @@ name = "bitflags" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "byteorder" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "clap" version = "2.27.1" @@ -59,6 +80,14 @@ name = "libc" version = "0.2.48" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "linux-loader" +version = "0.1.0" +source = "git+https://github.com/sameo/linux-loader#f0905480bd9f9aae8b88c63ca5dd6f3469483b65" +dependencies = [ + "vm-memory 0.1.0 (git+https://github.com/rust-vmm/vm-memory)", +] + [[package]] name = "redox_syscall" version = "0.1.51" @@ -117,8 +146,10 @@ dependencies = [ name = "vmm" version = "0.1.0" dependencies = [ + "arch 0.1.0", "kvm-bindings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "kvm-ioctls 0.1.0 (git+https://github.com/rust-vmm/kvm-ioctls)", + "linux-loader 0.1.0 (git+https://github.com/sameo/linux-loader)", "vm-memory 0.1.0 (git+https://github.com/rust-vmm/vm-memory)", ] @@ -145,10 +176,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" "checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" +"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" "checksum clap 2.27.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1b8c532887f1a292d17de05ae858a8fe50a301e196f9ef0ddb7ccd0d1d00f180" "checksum kvm-bindings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c223e8703d2eb76d990c5f58e29c85b0f6f50e24b823babde927948e7c71fc03" "checksum kvm-ioctls 0.1.0 (git+https://github.com/rust-vmm/kvm-ioctls)" = "" "checksum libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)" = "e962c7641008ac010fa60a7dfdc1712449f29c44ef2d4702394aea943ee75047" +"checksum linux-loader 0.1.0 (git+https://github.com/sameo/linux-loader)" = "" "checksum redox_syscall 0.1.51 (registry+https://github.com/rust-lang/crates.io-index)" = "423e376fffca3dfa06c9e9790a9ccd282fafb3cc6e6397d01dbf64f9bacc6b85" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" diff --git a/src/main.rs b/src/main.rs index 95d280cf2..03903ea23 100644 --- a/src/main.rs +++ b/src/main.rs @@ -32,5 +32,5 @@ fn main() { println!("Booting {:?}...", kernel_path.as_path()); - vmm::test_vm() + vmm::boot_kernel(kernel_path.as_path()).unwrap(); } diff --git a/vmm/Cargo.toml b/vmm/Cargo.toml index d501b8964..cebeaa156 100644 --- a/vmm/Cargo.toml +++ b/vmm/Cargo.toml @@ -5,8 +5,10 @@ authors = ["The Cloud Hypervisor Authors"] edition = "2018" [dependencies] +arch = { path = "../arch" } kvm-bindings = "0.1" kvm-ioctls = { git = "https://github.com/rust-vmm/kvm-ioctls" } +linux-loader = { git = "https://github.com/sameo/linux-loader" } [dependencies.vm-memory] git = "https://github.com/rust-vmm/vm-memory" diff --git a/vmm/src/lib.rs b/vmm/src/lib.rs index 7f0a1271c..b56ea5ac2 100644 --- a/vmm/src/lib.rs +++ b/vmm/src/lib.rs @@ -4,110 +4,30 @@ // extern crate kvm_ioctls; -extern crate vm_memory; -use kvm_bindings::kvm_userspace_memory_region; use kvm_ioctls::*; -use std::str; -use vm_memory::{Address, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap, GuestMemoryRegion}; +use std::path::Path; +pub mod vm; -pub fn test_vm() { - // This example based on https://lwn.net/Articles/658511/ - let code = [ - 0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */ - 0x00, 0xd8, /* add %bl, %al */ - 0x04, b'0', /* add $'0', %al */ - 0xee, /* out %al, (%dx) */ - 0xb0, b'\n', /* mov $'\n', %al */ - 0xee, /* out %al, (%dx) */ - 0xf4, /* hlt */ - ]; +use self::vm::{Result, Vm}; - let mem_size = 0x1000; - let load_addr = GuestAddress(0x1000); - let mem = GuestMemoryMmap::new(&[(load_addr, mem_size)]).unwrap(); +struct Vmm { + kvm: Kvm, +} - let kvm = Kvm::new().expect("new KVM instance creation failed"); - let vm_fd = kvm.create_vm().expect("new VM fd creation failed"); - - mem.with_regions(|index, region| { - let mem_region = kvm_userspace_memory_region { - slot: index as u32, - guest_phys_addr: region.start_addr().raw_value(), - memory_size: region.len() as u64, - userspace_addr: region.as_ptr() as u64, - flags: 0, - }; - - // Safe because the guest regions are guaranteed not to overlap. - vm_fd.set_user_memory_region(mem_region) - }) - .expect("Cannot configure guest memory"); - - mem.write_slice(&code, load_addr) - .expect("Writing code to memory failed"); - - let vcpu_fd = vm_fd.create_vcpu(0).expect("new VcpuFd failed"); - - let mut vcpu_sregs = vcpu_fd.get_sregs().expect("get sregs failed"); - vcpu_sregs.cs.base = 0; - vcpu_sregs.cs.selector = 0; - vcpu_fd.set_sregs(&vcpu_sregs).expect("set sregs failed"); - - let mut vcpu_regs = vcpu_fd.get_regs().expect("get regs failed"); - vcpu_regs.rip = 0x1000; - vcpu_regs.rax = 2; - vcpu_regs.rbx = 3; - vcpu_regs.rflags = 2; - vcpu_fd.set_regs(&vcpu_regs).expect("set regs failed"); - - loop { - match vcpu_fd.run().expect("run failed") { - VcpuExit::IoIn(addr, data) => { - println!( - "IO in -- addr: {:#x} data [{:?}]", - addr, - str::from_utf8(&data).unwrap() - ); - } - VcpuExit::IoOut(addr, data) => { - println!( - "IO out -- addr: {:#x} data [{:?}]", - addr, - str::from_utf8(&data).unwrap() - ); - } - VcpuExit::MmioRead(_addr, _data) => {} - VcpuExit::MmioWrite(_addr, _data) => {} - VcpuExit::Unknown => {} - VcpuExit::Exception => {} - VcpuExit::Hypercall => {} - VcpuExit::Debug => {} - VcpuExit::Hlt => { - println!("HLT"); - } - VcpuExit::IrqWindowOpen => {} - VcpuExit::Shutdown => {} - VcpuExit::FailEntry => {} - VcpuExit::Intr => {} - VcpuExit::SetTpr => {} - VcpuExit::TprAccess => {} - VcpuExit::S390Sieic => {} - VcpuExit::S390Reset => {} - VcpuExit::Dcr => {} - VcpuExit::Nmi => {} - VcpuExit::InternalError => {} - VcpuExit::Osi => {} - VcpuExit::PaprHcall => {} - VcpuExit::S390Ucontrol => {} - VcpuExit::Watchdog => {} - VcpuExit::S390Tsch => {} - VcpuExit::Epr => {} - VcpuExit::SystemEvent => {} - VcpuExit::S390Stsi => {} - VcpuExit::IoapicEoi => {} - VcpuExit::Hyperv => {} - } - // r => panic!("unexpected exit reason: {:?}", r), +impl Vmm { + fn new() -> Result { + let kvm = Kvm::new().expect("new KVM instance creation failed"); + Ok(Vmm { kvm }) } } + +pub fn boot_kernel(kernel: &Path) -> Result<()> { + let vmm = Vmm::new()?; + let mut vm = Vm::new(&vmm.kvm, kernel)?; + + vm.load_kernel()?; + vm.start()?; + + Ok(()) +} diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs new file mode 100644 index 000000000..ddc2f5c22 --- /dev/null +++ b/vmm/src/vm.rs @@ -0,0 +1,271 @@ +// Copyright © 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +extern crate arch; +extern crate kvm_ioctls; +extern crate linux_loader; +extern crate vm_memory; + +use kvm_bindings::kvm_userspace_memory_region; +use kvm_ioctls::*; +use linux_loader::cmdline; +use linux_loader::loader::KernelLoader; +use std::ffi::CString; +use std::fs::File; +use std::path::Path; +use std::{io, result, str, thread}; +use vm_memory::{ + Address, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap, GuestMemoryRegion, GuestUsize, + MmapError, +}; + +const DEFAULT_CMDLINE: &str = + "console=ttyS0,115200n8 init=/init tsc=reliable no_timer_check cryptomgr.notests"; +const CMDLINE_OFFSET: GuestAddress = GuestAddress(0x20000); + +/// Errors associated with the wrappers over KVM ioctls. +#[derive(Debug)] +pub enum Error { + /// Cannot open the VM file descriptor. + VmFd(io::Error), + + /// Cannot create the KVM instance + VmCreate(io::Error), + + /// Cannot set the VM up + VmSetup(io::Error), + + /// Cannot open the kernel image + KernelFile(io::Error), + + /// Mmap backed guest memory error + GuestMemory(MmapError), + + /// Cannot load the kernel in memory + KernelLoad(linux_loader::loader::Error), + + /// Cannot load the command line in memory + CmdLine, +} +pub type Result = result::Result; + +struct VmConfig<'a> { + kernel_path: &'a Path, + cmdline: Option, + cmdline_addr: GuestAddress, + + memory_size: GuestUsize, + vcpu_count: u8, +} + +impl<'a> Default for VmConfig<'a> { + fn default() -> Self { + let line = String::from(DEFAULT_CMDLINE); + let mut cmdline = cmdline::Cmdline::new(line.capacity()); + cmdline.insert_str(line); + + VmConfig { + kernel_path: Path::new(""), + cmdline: Some(cmdline), + cmdline_addr: CMDLINE_OFFSET, + memory_size: 512, + vcpu_count: 1, + } + } +} + +pub struct Vm<'a> { + fd: VmFd, + kernel: File, + memory: GuestMemoryMmap, + vcpus: Option>>, + config: VmConfig<'a>, +} + +impl<'a> Vm<'a> { + pub fn new(kvm: &Kvm, kernel_path: &'a Path) -> Result { + let vm_config = VmConfig { + kernel_path, + ..Default::default() + }; + + let kernel = File::open(kernel_path).map_err(Error::KernelFile)?; + let fd = kvm.create_vm().map_err(Error::VmCreate)?; + + // Init guest memory + let arch_mem_regions = arch::arch_memory_regions(vm_config.memory_size << 20); + let guest_memory = GuestMemoryMmap::new(&arch_mem_regions).map_err(Error::GuestMemory)?; + + guest_memory + .with_regions(|index, region| { + let mem_region = kvm_userspace_memory_region { + slot: index as u32, + guest_phys_addr: region.start_addr().raw_value(), + memory_size: region.len() as u64, + userspace_addr: region.as_ptr() as u64, + flags: 0, + }; + + println!( + "Size {:?} guest addr 0x{:x} host addr 0x{:x}", + mem_region.memory_size, mem_region.guest_phys_addr, mem_region.userspace_addr + ); + + // Safe because the guest regions are guaranteed not to overlap. + fd.set_user_memory_region(mem_region) + }) + .map_err(|_| Error::GuestMemory(MmapError::NoMemoryRegion))?; + + // Set TSS + fd.set_tss_address(arch::x86_64::layout::KVM_TSS_ADDRESS.raw_value() as usize) + .map_err(Error::VmSetup)?; + + Ok(Vm { + fd, + kernel, + memory: guest_memory, + vcpus: None, + config: vm_config, + }) + } + + pub fn load_kernel(&mut self) -> Result { + let cmdline = self.config.cmdline.clone().ok_or(Error::CmdLine)?; + + let cmdline_cstring = CString::new(cmdline).map_err(|_| Error::CmdLine)?; + + let entry_addr = linux_loader::loader::Elf::load( + &self.memory, + None, + &mut self.kernel, + Some(arch::HIMEM_START), + ) + .map_err(Error::KernelLoad)?; + + linux_loader::loader::load_cmdline( + &self.memory, + self.config.cmdline_addr, + &cmdline_cstring, + ) + .map_err(|_| Error::CmdLine)?; + + let vcpu_count = self.config.vcpu_count; + + arch::configure_system( + &self.memory, + self.config.cmdline_addr, + cmdline_cstring.to_bytes().len() + 1, + vcpu_count, + ) + .map_err(|_| Error::CmdLine)?; + + Ok(entry_addr.kernel_load) + } + + pub fn start(&mut self) -> Result<()> { + Ok(()) + } +} + +#[allow(unused)] +pub fn test_vm() { + // This example based on https://lwn.net/Articles/658511/ + let code = [ + 0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */ + 0x00, 0xd8, /* add %bl, %al */ + 0x04, b'0', /* add $'0', %al */ + 0xee, /* out %al, (%dx) */ + 0xb0, b'\n', /* mov $'\n', %al */ + 0xee, /* out %al, (%dx) */ + 0xf4, /* hlt */ + ]; + + let mem_size = 0x1000; + let load_addr = GuestAddress(0x1000); + let mem = GuestMemoryMmap::new(&[(load_addr, mem_size)]).unwrap(); + + let kvm = Kvm::new().expect("new KVM instance creation failed"); + let vm_fd = kvm.create_vm().expect("new VM fd creation failed"); + + mem.with_regions(|index, region| { + let mem_region = kvm_userspace_memory_region { + slot: index as u32, + guest_phys_addr: region.start_addr().raw_value(), + memory_size: region.len() as u64, + userspace_addr: region.as_ptr() as u64, + flags: 0, + }; + + // Safe because the guest regions are guaranteed not to overlap. + vm_fd.set_user_memory_region(mem_region) + }) + .expect("Cannot configure guest memory"); + mem.write_slice(&code, load_addr) + .expect("Writing code to memory failed"); + + let vcpu_fd = vm_fd.create_vcpu(0).expect("new VcpuFd failed"); + + let mut vcpu_sregs = vcpu_fd.get_sregs().expect("get sregs failed"); + vcpu_sregs.cs.base = 0; + vcpu_sregs.cs.selector = 0; + vcpu_fd.set_sregs(&vcpu_sregs).expect("set sregs failed"); + + let mut vcpu_regs = vcpu_fd.get_regs().expect("get regs failed"); + vcpu_regs.rip = 0x1000; + vcpu_regs.rax = 2; + vcpu_regs.rbx = 3; + vcpu_regs.rflags = 2; + vcpu_fd.set_regs(&vcpu_regs).expect("set regs failed"); + + loop { + match vcpu_fd.run().expect("run failed") { + VcpuExit::IoIn(addr, data) => { + println!( + "IO in -- addr: {:#x} data [{:?}]", + addr, + str::from_utf8(&data).unwrap() + ); + } + VcpuExit::IoOut(addr, data) => { + println!( + "IO out -- addr: {:#x} data [{:?}]", + addr, + str::from_utf8(&data).unwrap() + ); + } + VcpuExit::MmioRead(_addr, _data) => {} + VcpuExit::MmioWrite(_addr, _data) => {} + VcpuExit::Unknown => {} + VcpuExit::Exception => {} + VcpuExit::Hypercall => {} + VcpuExit::Debug => {} + VcpuExit::Hlt => { + println!("HLT"); + } + VcpuExit::IrqWindowOpen => {} + VcpuExit::Shutdown => {} + VcpuExit::FailEntry => {} + VcpuExit::Intr => {} + VcpuExit::SetTpr => {} + VcpuExit::TprAccess => {} + VcpuExit::S390Sieic => {} + VcpuExit::S390Reset => {} + VcpuExit::Dcr => {} + VcpuExit::Nmi => {} + VcpuExit::InternalError => {} + VcpuExit::Osi => {} + VcpuExit::PaprHcall => {} + VcpuExit::S390Ucontrol => {} + VcpuExit::Watchdog => {} + VcpuExit::S390Tsch => {} + VcpuExit::Epr => {} + VcpuExit::SystemEvent => {} + VcpuExit::S390Stsi => {} + VcpuExit::IoapicEoi => {} + VcpuExit::Hyperv => {} + } + // r => panic!("unexpected exit reason: {:?}", r), + } +}