From 5a1c62d473a829016258a0653c0fda236596eb5b Mon Sep 17 00:00:00 2001 From: Bruce Leidl Date: Fri, 20 Sep 2019 16:26:28 -0400 Subject: [PATCH] extensive changes to how VMs are configured and launched --- rust/src/devices/virtio_9p/synthetic.rs | 1 + rust/src/devices/virtio_block.rs | 7 +- rust/src/disk/mod.rs | 9 +- rust/src/disk/raw.rs | 89 +++++---- rust/src/disk/realmfs.rs | 15 +- rust/src/main.rs | 4 +- rust/src/vm/config.rs | 244 ++++++++++++++++-------- rust/src/vm/error.rs | 13 +- rust/src/vm/kernel_cmdline.rs | 12 +- rust/src/vm/mod.rs | 108 ++++++----- rust/src/vm/setup/kernel.rs | 18 +- 11 files changed, 313 insertions(+), 207 deletions(-) diff --git a/rust/src/devices/virtio_9p/synthetic.rs b/rust/src/devices/virtio_9p/synthetic.rs index 90540de..0c40004 100644 --- a/rust/src/devices/virtio_9p/synthetic.rs +++ b/rust/src/devices/virtio_9p/synthetic.rs @@ -378,6 +378,7 @@ impl SyntheticFS { Ok(()) } + #[allow(dead_code)] pub fn add_executable, Q: AsRef>(&mut self, dirpath: P, filename: &str, realpath: Q) -> io::Result<()> { let realpath = realpath.as_ref(); self.add_library_dependencies(realpath)?; diff --git a/rust/src/devices/virtio_block.rs b/rust/src/devices/virtio_block.rs index 2185649..5b3af68 100644 --- a/rust/src/devices/virtio_block.rs +++ b/rust/src/devices/virtio_block.rs @@ -66,7 +66,7 @@ pub struct VirtioBlock { const VIRTIO_ID_BLOCK: u16 = 2; impl VirtioBlock { - fn new(disk_image: D) -> Self { + pub fn new(disk_image: D) -> Self { let mut config = DeviceConfigArea::new(8); config.write_u64(0, disk_image.sector_count()); VirtioBlock { @@ -192,7 +192,6 @@ impl <'a, D: DiskImage> MessageHandler<'a, D> { } } - fn sector_round(sz: usize) -> usize { (sz / SECTOR_SIZE) * SECTOR_SIZE } @@ -204,7 +203,7 @@ impl <'a, D: DiskImage> MessageHandler<'a, D> { self.disk.read_sectors(self.sector, buffer) .map_err(Error::DiskRead)?; - self.chain.inc_offset(len); + self.chain.inc_offset(len, true); Ok(()) } @@ -215,7 +214,7 @@ impl <'a, D: DiskImage> MessageHandler<'a, D> { self.disk.write_sectors(self.sector, buffer) .map_err(Error::DiskWrite)?; - self.chain.inc_offset(len); + self.chain.inc_offset(len, false); Ok(()) } diff --git a/rust/src/disk/mod.rs b/rust/src/disk/mod.rs index 1f3b4e5..6191c39 100644 --- a/rust/src/disk/mod.rs +++ b/rust/src/disk/mod.rs @@ -25,14 +25,15 @@ pub enum OpenType { pub trait DiskImage: Sync+Send { fn read_only(&self) -> bool; fn sector_count(&self) -> u64; - fn disk_file(&self) -> &File; + fn disk_file(&mut self) -> Result<&mut File>; - fn seek_to_sector(&self, sector: u64) -> Result<()> { + fn seek_to_sector(&mut self, sector: u64) -> Result<()> { if sector > self.sector_count() { return Err(Error::BadSectorOffset(sector)); } let offset = SeekFrom::Start(sector * SECTOR_SIZE as u64); - self.disk_file().seek(offset) + let file = self.disk_file()?; + file.seek(offset) .map_err(Error::DiskSeek)?; Ok(()) } @@ -67,6 +68,7 @@ pub enum Error { DiskSeek(io::Error), BadSectorOffset(u64), MemoryOverlayCreate(system::Error), + NotOpen, } impl error::Error for Error {} @@ -83,6 +85,7 @@ impl fmt::Display for Error { DiskSeek(err) => write!(f, "error seeking to offset on disk image: {}", err), BadSectorOffset(sector) => write!(f, "attempt to access invalid sector offset {}", sector), MemoryOverlayCreate(err) => write!(f, "failed to create memory overlay: {}", err), + NotOpen => write!(f, "disk not open"), } } } \ No newline at end of file diff --git a/rust/src/disk/raw.rs b/rust/src/disk/raw.rs index 3ba6597..dff956f 100644 --- a/rust/src/disk/raw.rs +++ b/rust/src/disk/raw.rs @@ -3,81 +3,84 @@ use std::fs::{File, OpenOptions}; use std::io::{Write, Read, SeekFrom, Seek}; use crate::disk::Error::DiskRead; use crate::disk::memory::MemoryOverlay; -use std::path::Path; +use std::path::PathBuf; + pub struct RawDiskImage { - file: File, + path: PathBuf, + open_type: OpenType, + file: Option, offset: usize, nsectors: u64, - read_only: bool, disk_image_id: Vec, overlay: Option, } impl RawDiskImage { - #[allow(dead_code)] - pub fn open>(path: P, open_type: OpenType) -> Result { - Self::open_with_offset(path, open_type, 0) + pub fn new>(path: P, open_type: OpenType) -> Self { + Self::new_with_offset(path, open_type, 0) } - pub fn open_with_offset>(path: P, open_type: OpenType, offset: usize) -> Result { - let path = path.as_ref(); - let meta = path.metadata() - .map_err(|e| Error::DiskOpen(path.into(), e))?; + pub fn new_with_offset>(path: P, open_type: OpenType, offset: usize) -> Self { + let path = path.into(); + RawDiskImage { + path, + open_type, + file: None, + offset, + nsectors: 0, + disk_image_id: Vec::new(), + overlay: None, + } + } - if meta.len() < offset as u64 { - return Err(Error::DiskOpenTooShort(path.into())) + pub fn open(&mut self) -> Result<()> { + let meta = self.path.metadata() + .map_err(|e| Error::DiskOpen(self.path.clone(), e))?; + + if meta.len() < self.offset as u64 { + return Err(Error::DiskOpenTooShort(self.path.clone())) } - let nsectors = (meta.len() - offset as u64) / SECTOR_SIZE as u64; + self.nsectors = (meta.len() - self.offset as u64) / SECTOR_SIZE as u64; let file = OpenOptions::new() .read(true) - .write(open_type == OpenType::ReadWrite) - .open(path) - .map_err(|e| Error::DiskOpen(path.into(), e))?; + .write(self.open_type == OpenType::ReadWrite) + .open(&self.path) + .map_err(|e| Error::DiskOpen(self.path.clone(), e))?; + self.disk_image_id = generate_disk_image_id(&file); + self.file = Some(file); - let disk = match open_type { - OpenType::MemoryOverlay => { - let overlay = MemoryOverlay::new()?; - Self::new(file, nsectors, offset, false, Some(overlay)) - } - OpenType::ReadOnly => { - Self::new(file, nsectors, offset, true, None) - } - OpenType::ReadWrite => { - Self::new(file, nsectors, offset, false, None) - } - }; - Ok(disk) - } - - pub fn new(file: File, nsectors: u64, offset: usize, read_only: bool, overlay: Option) -> Self { - let disk_image_id = generate_disk_image_id(&file); - RawDiskImage { file, nsectors, read_only, offset, disk_image_id, overlay } + if self.open_type == OpenType::MemoryOverlay { + let overlay = MemoryOverlay::new()?; + self.overlay = Some(overlay); + } + Ok(()) } } impl DiskImage for RawDiskImage { fn read_only(&self) -> bool { - self.read_only + self.open_type == OpenType::ReadOnly } fn sector_count(&self) -> u64 { self.nsectors } - fn disk_file(&self) -> &File { - &self.file + fn disk_file(&mut self) -> Result<&mut File> { + self.file.as_mut().ok_or(Error::NotOpen) } - fn seek_to_sector(&self, sector: u64) -> Result<()> { + fn seek_to_sector(&mut self, sector: u64) -> Result<()> { if sector > self.sector_count() { return Err(Error::BadSectorOffset(sector)); } let offset = SeekFrom::Start(sector * SECTOR_SIZE as u64 + self.offset as u64); - self.disk_file().seek(offset) + let disk = self.disk_file()?; + disk.seek(offset) .map_err(Error::DiskSeek)?; Ok(()) } @@ -86,12 +89,13 @@ impl DiskImage for RawDiskImage { if let Some(ref mut overlay) = self.overlay { return overlay.write_sectors(start_sector, buffer); } - if self.read_only { + if self.read_only() { return Err(Error::ReadOnly) } self.seek_to_sector(start_sector)?; let len = (buffer.len() / SECTOR_SIZE) * SECTOR_SIZE; - self.file.write_all(&buffer[..len]) + let file = self.disk_file()?; + file.write_all(&buffer[..len]) .map_err(Error::DiskWrite)?; Ok(()) } @@ -105,7 +109,8 @@ impl DiskImage for RawDiskImage { self.seek_to_sector(start_sector)?; let len = (buffer.len() / SECTOR_SIZE) * SECTOR_SIZE; - self.file.read_exact(&mut buffer[..len]) + let file = self.disk_file()?; + file.read_exact(&mut buffer[..len]) .map_err(DiskRead)?; Ok(()) } diff --git a/rust/src/disk/realmfs.rs b/rust/src/disk/realmfs.rs index 615afaa..896c35d 100644 --- a/rust/src/disk/realmfs.rs +++ b/rust/src/disk/realmfs.rs @@ -1,6 +1,6 @@ use crate::disk::{Result, DiskImage, SECTOR_SIZE, RawDiskImage, OpenType}; use std::fs::File; -use std::path::Path; +use std::path::PathBuf; // skip 4096 byte realmfs header const HEADER_SECTOR_COUNT: usize = 8; @@ -11,10 +11,13 @@ pub struct RealmFSImage { // Just pass everything through to raw image for now impl RealmFSImage { - pub fn open>(path: P, read_only: bool) -> Result { - let open_type = if read_only { OpenType::ReadOnly } else { OpenType::MemoryOverlay }; - let raw = RawDiskImage::open_with_offset(path, open_type, HEADER_SECTOR_COUNT * SECTOR_SIZE)?; - Ok(RealmFSImage { raw }) + pub fn new>(path: P, open_type: OpenType) -> Self { + let offset = HEADER_SECTOR_COUNT * SECTOR_SIZE; + let raw = RawDiskImage::new_with_offset(path, open_type, offset); + RealmFSImage { raw } + } + pub fn open(&mut self) -> Result<()> { + self.raw.open() } } @@ -27,7 +30,7 @@ impl DiskImage for RealmFSImage { self.raw.sector_count() } - fn disk_file(&self) -> &File { + fn disk_file(&mut self) -> Result<&mut File> { self.raw.disk_file() } diff --git a/rust/src/main.rs b/rust/src/main.rs index b9f9f83..301232b 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -13,11 +13,9 @@ mod virtio; mod disk; pub use log::{Logger,LogLevel}; -use std::env; fn main() { - vm::VmConfig::new(env::args()) + vm::VmConfig::new() .ram_size_megs(1024) - .use_realmfs("/home/user/Shared/main-realmfs.img") .boot(); } diff --git a/rust/src/vm/config.rs b/rust/src/vm/config.rs index 2b2d7ea..6f99e07 100644 --- a/rust/src/vm/config.rs +++ b/rust/src/vm/config.rs @@ -1,39 +1,47 @@ use std::path::{PathBuf, Path}; -use crate::vm::{Vm, Result, ErrorKind}; +use crate::vm::Vm; use std::{env, process}; - -pub enum RootFS { - SelfRoot, - RealmFSImage(PathBuf), - RawImage(PathBuf), - RawOffset(PathBuf, usize), -} +use crate::devices::SyntheticFS; +use crate::disk::{RawDiskImage, RealmFSImage, OpenType}; +use libcitadel::Realms; +use libcitadel::terminal::{TerminalPalette, AnsiTerminal}; pub struct VmConfig { ram_size: usize, ncpus: usize, verbose: bool, + rootshell: bool, + home: String, launch_systemd: bool, kernel_path: Option, init_path: Option, init_cmd: Option, - rootfs: RootFS, + raw_disks: Vec, + + realmfs_images: Vec, + realm_name: Option, + synthetic: Option, } #[allow(dead_code)] impl VmConfig { - pub fn new(args: env::Args) -> VmConfig { + pub fn new() -> VmConfig { let mut config = VmConfig { ram_size: 256 * 1024 * 1024, ncpus: 1, verbose: false, + rootshell: false, + home: String::from("/home/user"), launch_systemd: false, kernel_path: None, init_path: None, init_cmd: None, - rootfs: RootFS::SelfRoot, + realm_name: None, + raw_disks: Vec::new(), + realmfs_images: Vec::new(), + synthetic: None, }; - config.parse_args(args); + config.parse_args(); config } @@ -42,6 +50,21 @@ impl VmConfig { self } + pub fn raw_disk_image>(mut self, path: P, open_type: OpenType) -> Self { + self.raw_disks.push(RawDiskImage::new(path, open_type)); + self + } + + pub fn raw_disk_image_with_offset>(mut self, path: P, open_type: OpenType, offset: usize) -> Self { + self.raw_disks.push(RawDiskImage::new_with_offset(path, open_type, offset)); + self + } + + pub fn realmfs_image>(mut self, path: P) -> Self { + self.realmfs_images.push(RealmFSImage::new(path, OpenType::MemoryOverlay)); + self + } + pub fn num_cpus(mut self, ncpus: usize) -> Self { self.ncpus = ncpus; self @@ -62,27 +85,20 @@ impl VmConfig { self } - pub fn use_realmfs>(mut self, path: P) -> Self { - self.rootfs = RootFS::RealmFSImage(path.into()); - self - } - - pub fn use_rawdisk>(mut self, path: P) -> Self { - self.rootfs = RootFS::RawImage(path.into()); - self - } - - pub fn use_rawdisk_with_offset>(mut self, path: P, offset: usize) -> Self { - self.rootfs = RootFS::RawOffset(path.into(), offset); - self - } - pub fn use_systemd(mut self) -> Self { self.launch_systemd = true; self } + pub fn synthetic_fs(mut self, sfs: SyntheticFS) -> Self { + self.synthetic = Some(sfs); + self + } + pub fn boot(self) { + + let _terminal_restore = TerminalRestore::save(); + match Vm::open(self) { Ok(vm) => if let Err(err) = vm.start() { notify!("Error starting VM: {}", err); @@ -103,83 +119,155 @@ impl VmConfig { self.verbose } + pub fn rootshell(&self) -> bool { + self.rootshell + } + + pub fn homedir(&self) -> &str { + &self.home + } + pub fn launch_systemd(&self) -> bool { self.launch_systemd } - pub fn get_kernel_path(&self) -> Result { - match self.kernel_path { - Some(ref path) if path.exists() => return Ok(path.to_path_buf()), - None => if let Some(path) = Self::search_kernel() { - return Ok(path) - } - _ => {}, - } - Err(ErrorKind::KernelNotFound.into()) + pub fn has_block_image(&self) -> bool { + !(self.realmfs_images.is_empty() && self.raw_disks.is_empty()) } - pub fn get_init_path(&self) -> Result { - match self.init_path { - Some(ref path) if path.exists() => return Ok(path.to_path_buf()), - None => if let Some(path) = Self::search_init() { - return Ok(path) - } - _ => {}, - } - Err(ErrorKind::InitNotFound.into()) + pub fn get_realmfs_images(&mut self) -> Vec { + self.realmfs_images.drain(..).collect() + } + + pub fn get_raw_disk_images(&mut self) -> Vec { + self.raw_disks.drain(..).collect() + } + + pub fn get_synthetic_fs(&self) -> Option { + self.synthetic.clone() } pub fn get_init_cmdline(&self) -> Option<&str> { self.init_cmd.as_ref().map(|s| s.as_str()) } - pub fn rootfs(&self) -> &RootFS { - &self.rootfs + pub fn realm_name(&self) -> Option<&str> { + self.realm_name.as_ref().map(|s| s.as_str()) } - fn search_init() -> Option { - Self::search_binary("ph-init", &[ - "rust/target/release", "rust/target/debug", - "target/debug", "target/release" - ]) + fn add_realmfs_by_name(&mut self, realmfs: &str) { + let path = Path::new("/realms/realmfs-images") + .join(format!("{}-realmfs.img", realmfs)); + if !path.exists() { + eprintln!("Realmfs image does not exist at {}", path.display()); + process::exit(1); + } + self.realmfs_images.push(RealmFSImage::new(path, OpenType::MemoryOverlay)); } - fn search_kernel() -> Option { - Self::search_binary("ph_linux", &["kernel", "../kernel"]) + fn add_realm_by_name(&mut self, realm: &str) { + let realms = Realms::load().unwrap(); + if let Some(realm) = realms.by_name(realm) { + let config = realm.config(); + let realmfs = config.realmfs(); + self.add_realmfs_by_name(realmfs); + self.home = realm.base_path().join("home").display().to_string(); + self.realm_name = Some(realm.name().to_string()) + } } - fn search_binary(name: &str, paths: &[&str]) -> Option { - let cwd = match env::current_dir() { - Ok(cwd) => cwd, - _ => return None, - }; + fn parse_args(&mut self) { + let args = ProgramArgs::new(); + if args.has_arg("-v") { + self.verbose = true; + } + if args.has_arg("--root") { + self.rootshell = true; + } + if let Some(home) = args.arg_with_value("--home") { + self.home = home.to_string(); + } + if let Some(realmfs) = args.arg_with_value("--realmfs") { + self.add_realmfs_by_name(realmfs); + } + if let Some(realm) = args.arg_with_value("--realm") { + self.add_realm_by_name(realm); + } + } +} - for p in paths { - let p = Path::new(p).join(name); - let current = if p.is_absolute() { - p - } else { - cwd.join(p) - }; - if current.exists() { - return Some(current); +struct ProgramArgs { + args: Vec, +} + +impl ProgramArgs { + fn new() -> Self { + ProgramArgs { + args: env::args().skip(1).collect(), + } + } + + fn has_arg(&self, name: &str) -> bool { + self.args.iter().any(|arg| arg.as_str() == name) + } + + fn arg_with_value(&self, name: &str) -> Option<&str> { + let mut iter = self.args.iter(); + while let Some(arg) = iter.next() { + if arg.as_str() == name { + match iter.next() { + Some(val) => return Some(val.as_str()), + None => { + eprintln!("Expected value for {} argument", name); + process::exit(1); + } + } } } None } +} - fn parse_args(&mut self, args: env::Args) { - for arg in args.skip(1) { - self.parse_one_arg(&arg); +pub struct TerminalRestore { + saved: Option, +} + +impl TerminalRestore { + pub fn save() -> Self { + let mut term = match AnsiTerminal::new() { + Ok(term) => term, + Err(e) => { + warn!("failed to open terminal: {}", e); + return TerminalRestore { saved: None } + } + }; + + let mut palette = TerminalPalette::default(); + if let Err(e) = palette.load(&mut term) { + warn!("failed to load palette: {}", e); + return TerminalRestore { saved: None } + } + if let Err(e) = term.clear_screen() { + warn!("failed to clear screen: {}", e); + return TerminalRestore { saved: None } + } + TerminalRestore { saved: Some(palette) } + } + + fn restore(&self) { + if let Some(p) = self.saved.as_ref() { + let mut term = match AnsiTerminal::new() { + Ok(term) => term, + _ => return, + }; + let _ = p.apply(&mut term); } } - fn parse_one_arg(&mut self, arg: &str) { - if arg == "-v" { - self.verbose = true; - } else { - eprintln!("Unrecognized command line argument: {}", arg); - process::exit(1); - } +} + +impl Drop for TerminalRestore { + fn drop(&mut self) { + self.restore(); } } diff --git a/rust/src/vm/error.rs b/rust/src/vm/error.rs index 41e781a..f301097 100644 --- a/rust/src/vm/error.rs +++ b/rust/src/vm/error.rs @@ -10,8 +10,6 @@ pub type Result = result::Result; #[derive(Debug)] pub enum ErrorKind { - KernelNotFound, - InitNotFound, InvalidAddress(u64), InvalidMappingOffset(usize), RegisterMemoryFailed, @@ -26,13 +24,12 @@ pub enum ErrorKind { EventFdError, DiskImageOpen(disk::Error), TerminalTermios(io::Error), + IoError(io::Error), } impl ErrorKind { fn as_str(&self) -> &'static str { match *self { - ErrorKind::KernelNotFound => "Could not find kernel image", - ErrorKind::InitNotFound => "Could not find init image", ErrorKind::InvalidAddress(..) => "Invalid guest memory address", ErrorKind::InvalidMappingOffset(..) => "Invalid memory mapping offset", ErrorKind::RegisterMemoryFailed => "Failed to register memory region", @@ -47,6 +44,7 @@ impl ErrorKind { ErrorKind::EventFdError => "eventfd error", ErrorKind::DiskImageOpen(_) => "failed to open disk image", ErrorKind::TerminalTermios(_) => "failed termios", + ErrorKind::IoError(_) => "i/o error", } } } @@ -59,11 +57,18 @@ impl fmt::Display for ErrorKind { ErrorKind::IoctlFailed(name) => write!(f, "Ioctl {} failed", name), ErrorKind::DiskImageOpen(ref e) => write!(f, "failed to open disk image: {}", e), ErrorKind::TerminalTermios(ref e) => write!(f, "error reading/restoring terminal state: {}", e), + ErrorKind::IoError(ref e) => write!(f, "i/o error: {}", e), _ => write!(f, "{}", self.as_str()), } } } +impl From for Error { + fn from(err: io::Error) -> Error { + ErrorKind::IoError(err).into() + } + +} impl From for Error { fn from(kind: ErrorKind) -> Error { Error { repr: Repr::Simple(kind) } diff --git a/rust/src/vm/kernel_cmdline.rs b/rust/src/vm/kernel_cmdline.rs index a2f8e9d..69d8a97 100644 --- a/rust/src/vm/kernel_cmdline.rs +++ b/rust/src/vm/kernel_cmdline.rs @@ -12,20 +12,16 @@ fn add_defaults(cmdline: &mut KernelCmdLine) { // keyboard reboot .push("reboot=k") .push_set_true("panic") - .push_set_val("tsc", "reliable") - .push("no_timer_check") - // faster rcu updates - .push_set_true("rcuupdate.rcu_expedited") - // then restore to normal after booting - .push_set_true("rcuupdate.rcu_normal_after_boot") + + .push("init_on_alloc=0") + .push("init_on_free=0") .push_set_val("console", "hvc0") .push_set_true("i8042.direct") .push_set_true("i8042.dumbkbd") .push_set_true("i8042.nopnp") .push_set_true("i8042.noaux") - .push("noreplace-smp") - //.push("initcall_debug") + // .push("initcall_debug") .push_set_val("iommu", "off") .push("cryptomgr.notests") diff --git a/rust/src/vm/mod.rs b/rust/src/vm/mod.rs index f48202e..44c07ae 100644 --- a/rust/src/vm/mod.rs +++ b/rust/src/vm/mod.rs @@ -1,14 +1,17 @@ -use std::thread; +use std::{thread, fs}; use self::io::IoDispatcher; use crate::virtio::VirtioBus; use crate::devices; -use crate::disk; use crate::memory::{GuestRam, KVM_KERNEL_LOAD_ADDRESS, MemoryManager, SystemAllocator, AddressRange}; use crate::kvm::*; +static KERNEL: &[u8] = include_bytes!("../../kernel/ph_linux"); +static PHINIT: &[u8] = include_bytes!("../../target/release/ph-init"); +static SOMMELIER: &[u8] = include_bytes!("../../sommelier/sommelier"); + mod run; pub mod io; mod setup; @@ -25,11 +28,8 @@ use self::run::KvmRunArea; use self::kernel_cmdline::KernelCmdLine; use std::sync::Arc; use std::sync::atomic::AtomicBool; -use crate::disk::OpenType; use termios::Termios; -use crate::vm::config::RootFS; -use std::path::Path; - +use crate::devices::SyntheticFS; pub struct Vm { _config: VmConfig, @@ -76,61 +76,66 @@ impl Vm { Ok(MemoryManager::new(kvm, ram, allocator)) } - fn setup_virtio(config: &VmConfig, cmdline: &mut KernelCmdLine, virtio: &mut VirtioBus) -> Result<()> { + fn setup_virtio(config: &mut VmConfig, cmdline: &mut KernelCmdLine, virtio: &mut VirtioBus) -> Result<()> { devices::VirtioSerial::create(virtio)?; devices::VirtioRandom::create(virtio)?; devices::VirtioWayland::create(virtio)?; - let init_path = config.get_init_path()?; - devices::VirtioP9::create(virtio, "home", "/home/user", &init_path)?; + devices::VirtioP9::create(virtio, "home", config.homedir(), false, false)?; - Self::setup_rootfs(config, cmdline, virtio, &init_path) + let mut block_root = false; + + for mut disk in config.get_realmfs_images() { + disk.open().map_err(ErrorKind::DiskImageOpen)?; + devices::VirtioBlock::create(virtio, disk)?; + block_root = true; + } + for mut disk in config.get_raw_disk_images() { + disk.open().map_err(ErrorKind::DiskImageOpen)?; + devices::VirtioBlock::create(virtio, disk)?; + block_root = true; + } + + if block_root { + cmdline.push("phinit.root=/dev/vda"); + cmdline.push("phinit.rootfstype=ext4"); + } else { + devices::VirtioP9::create(virtio, "9proot", "/", true, false)?; + cmdline.push_set_val("phinit.root", "9proot"); + cmdline.push_set_val("phinit.rootfstype", "9p"); + cmdline.push_set_val("phinit.rootflags", "trans=virtio"); + } + + Self::setup_synthetic_bootfs(cmdline, virtio) } - fn setup_rootfs(config: &VmConfig, cmdline: &mut KernelCmdLine, virtio: &mut VirtioBus, init_path: &Path) -> Result<()> { - match config.rootfs() { - RootFS::SelfRoot => { - devices::VirtioP9::create(virtio, "/dev/root", "/", &init_path)?; - notify!("9p root"); - cmdline.push_set_val("root", "/dev/root"); - cmdline.push("ro"); - cmdline.push_set_val("rootfstype", "9p"); - cmdline.push_set_val("rootflags", "trans=virtio,version=9p2000.L,cache=loose"); - cmdline.push_set_val("init", "/phinit"); - }, - RootFS::RealmFSImage(ref path) => { - let disk = disk::RealmFSImage::open(path, false) - .map_err(ErrorKind::DiskImageOpen)?; - devices::VirtioBlock::create(virtio, disk)?; - cmdline.push_set_val("root", "/dev/vda"); - cmdline.push("rw"); - cmdline.push_set_val("init", "/usr/bin/ph-init"); - }, - RootFS::RawImage(ref path) => { - let disk = disk::RawDiskImage::open(path, OpenType::MemoryOverlay).map_err(ErrorKind::DiskImageOpen)?; - devices::VirtioBlock::create(virtio, disk)?; - cmdline.push_set_val("root", "/dev/vda"); - cmdline.push("rw"); - }, - RootFS::RawOffset(path, offset) => { - let disk = disk::RawDiskImage::open_with_offset(path, OpenType::ReadWrite, *offset) - .map_err(ErrorKind::DiskImageOpen)?; - devices::VirtioBlock::create(virtio, disk)?; - cmdline.push_set_val("root", "/dev/vda"); - cmdline.push("rw"); - cmdline.push_set_val("init", "/usr/bin/ph-init"); - } - }; + fn setup_synthetic_bootfs(cmdline: &mut KernelCmdLine, virtio: &mut VirtioBus) -> Result<()> { + let mut s = SyntheticFS::new(); + s.mkdirs(&["/tmp", "/proc", "/sys", "/dev", "/home/user", "/bin", "/etc"]); + + fs::write("/tmp/ph-init", PHINIT)?; + s.add_library_dependencies("/tmp/ph-init")?; + fs::remove_file("/tmp/ph-init")?; + + s.add_memory_file("/usr/bin", "ph-init", 0o755, PHINIT)?; + s.add_memory_file("/usr/bin", "sommelier", 0o755, SOMMELIER)?; + + s.add_file("/etc", "ld.so.cache", 0o644, "/etc/ld.so.cache"); + devices::VirtioP9::create_with_filesystem(s, virtio, "/dev/root", "/", false)?; + cmdline.push_set_val("init", "/usr/bin/ph-init"); + cmdline.push_set_val("root", "/dev/root"); + cmdline.push("ro"); + cmdline.push_set_val("rootfstype", "9p"); + cmdline.push_set_val("rootflags", "trans=virtio"); Ok(()) } - pub fn open(config: VmConfig) -> Result { - let kernel_path = config.get_kernel_path()?; + pub fn open(mut config: VmConfig) -> Result { let mut memory = Self::create_memory_manager(config.ram_size())?; let mut cmdline = KernelCmdLine::new_default(); - setup::kernel::load_pm_kernel(memory.guest_ram(), &kernel_path, cmdline.address(), cmdline.size())?; + setup::kernel::load_pm_kernel(memory.guest_ram(), cmdline.address(), cmdline.size())?; let io_dispatch = IoDispatcher::new(); @@ -144,13 +149,19 @@ impl Vm { } else { cmdline.push("quiet"); } + if config.rootshell() { + cmdline.push("phinit.rootshell"); + } + if let Some(realm) = config.realm_name() { + cmdline.push_set_val("phinit.realm", realm); + } let saved= Termios::from_fd(0) .map_err(ErrorKind::TerminalTermios)?; let termios = Some(saved); let mut virtio = VirtioBus::new(memory.clone(), io_dispatch.clone(), memory.kvm().clone()); - Self::setup_virtio(&config, &mut cmdline, &mut virtio)?; + Self::setup_virtio(&mut config, &mut cmdline, &mut virtio)?; if config.launch_systemd() { cmdline.push("phinit.run_systemd"); @@ -191,6 +202,5 @@ impl Vm { } Ok(()) } - } diff --git a/rust/src/vm/setup/kernel.rs b/rust/src/vm/setup/kernel.rs index d5b66c5..db3bfe6 100644 --- a/rust/src/vm/setup/kernel.rs +++ b/rust/src/vm/setup/kernel.rs @@ -1,11 +1,9 @@ -use std::path::Path; -use std::fs::{File}; -use std::io::{self, Read,SeekFrom,Seek}; +use std::io::{self, Read, SeekFrom, Seek, Cursor}; use byteorder::{LittleEndian,ReadBytesExt}; use crate::memory::{self,GuestRam,KERNEL_ZERO_PAGE}; -use crate::vm::{Result,Error,ErrorKind}; +use crate::vm::{Result, Error, ErrorKind, KERNEL}; // Documentation/x86/boot.txt @@ -64,13 +62,13 @@ fn setup_zero_page(memory: &GuestRam, cmdline_addr: u64, cmdline_size: usize) -> setup_e820(memory, base) } -pub fn load_pm_kernel(memory: &GuestRam, path: &Path, cmdline_addr: u64, cmdline_size: usize) -> Result<()> { - load_elf_kernel(memory, path).map_err(|_| Error::from(ErrorKind::ReadKernelFailed))?; +pub fn load_pm_kernel(memory: &GuestRam, cmdline_addr: u64, cmdline_size: usize) -> Result<()> { + load_elf_kernel(memory).map_err(|_| Error::from(ErrorKind::ReadKernelFailed))?; setup_zero_page(memory, cmdline_addr, cmdline_size) } -pub fn load_elf_kernel(memory: &GuestRam, path: &Path) -> io::Result<()> { - let mut f = File::open(&path)?; +pub fn load_elf_kernel(memory: &GuestRam) -> io::Result<()> { + let mut f = Cursor::new(KERNEL); f.seek(SeekFrom::Start(32))?; let phoff = f.read_u64::()?; f.seek(SeekFrom::Current(16))?; @@ -78,7 +76,7 @@ pub fn load_elf_kernel(memory: &GuestRam, path: &Path) -> io::Result<()> { f.seek(SeekFrom::Start(phoff))?; let mut v = Vec::new(); for _ in 0..phnum { - let hdr = load_phdr(&f)?; + let hdr = load_phdr(&mut f)?; if hdr.p_type == 1 { v.push(hdr); } @@ -92,7 +90,7 @@ pub fn load_elf_kernel(memory: &GuestRam, path: &Path) -> io::Result<()> { Ok(()) } -fn load_phdr(mut r: R) -> io::Result { +fn load_phdr(r: &mut R) -> io::Result { let mut phdr: ElfPhdr = Default::default(); phdr.p_type = r.read_u32::()?; phdr.p_flags = r.read_u32::()?;