diff --git a/src/main.rs b/src/main.rs index a82fa8c..fc88f3d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,7 +24,7 @@ struct VMMOpts { /// Stdout console file path #[clap(long)] - console: Option + console: Option, } #[derive(Debug)] @@ -47,8 +47,22 @@ fn main() -> Result<(), Error> { // * Memory size (in MB) // * Path to a Linux kernel // * Optional path to console file - vmm.configure(opts.cpus, opts.memory, &opts.kernel, opts.console) - .map_err(Error::VmmConfigure)?; + vmm.configure( + opts.cpus, + opts.memory, + &opts.kernel, + opts.console, + Option::None, + ) + .map_err(Error::VmmConfigure)?; + + // To use Writer with serial device : + // * Create mpsc channel : + // let (tx, rx) = std::sync::mpsc::channel(); + // * Create a new Writer + // let writer = Writer::new(tx); + // * Add the Writer when configuring the VMM + // * Use the rx receiver to read the data // Run the VMM vmm.run().map_err(Error::VmmRun)?; diff --git a/src/vmm/src/devices/mod.rs b/src/vmm/src/devices/mod.rs index 38db994..d4f09c7 100644 --- a/src/vmm/src/devices/mod.rs +++ b/src/vmm/src/devices/mod.rs @@ -1,3 +1,30 @@ // SPDX-License-Identifier: Apache-2.0 +use std::io::{Result, Write}; +use std::sync::mpsc; + pub(crate) mod serial; + +pub struct Writer { + tx: mpsc::Sender, +} + +impl Write for Writer { + fn write(&mut self, buf: &[u8]) -> Result { + let s = String::from_utf8_lossy(buf); + self.tx + .send(s.to_string()) + .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "Error sending data"))?; + Ok(buf.len()) + } + + fn flush(&mut self) -> Result<()> { + Ok(()) + } +} + +impl Writer { + pub fn new(tx: mpsc::Sender) -> Self { + Writer { tx } + } +} diff --git a/src/vmm/src/lib.rs b/src/vmm/src/lib.rs index 160dd2f..0edcc25 100644 --- a/src/vmm/src/lib.rs +++ b/src/vmm/src/lib.rs @@ -8,14 +8,15 @@ extern crate linux_loader; extern crate vm_memory; extern crate vm_superio; +use std::fs::File; use std::io::stdout; use std::os::unix::io::AsRawFd; use std::os::unix::prelude::RawFd; use std::sync::{Arc, Mutex}; use std::thread; use std::{io, path::PathBuf}; -use std::fs::File; +use devices::Writer; use kvm_bindings::{kvm_userspace_memory_region, KVM_MAX_CPUID_ENTRIES}; use kvm_ioctls::{Kvm, VmFd}; use linux_loader::loader::{self, KernelLoaderResult}; @@ -66,6 +67,8 @@ pub enum Error { TerminalConfigure(kvm_ioctls::Error), /// Console configuration error ConsoleError(io::Error), + /// Writer configuration error + WriterError(io::Error), } /// Dedicated [`Result`](https://doc.rust-lang.org/std/result/) type. @@ -164,10 +167,15 @@ impl VMM { Ok(()) } - pub fn configure_console( - &mut self, - console_path: Option - ) -> Result<()> { + pub fn configure_writer(&mut self, writer: Option) -> Result<()> { + if let Some(writer) = writer { + let mut serial = self.serial.lock().unwrap(); + *serial = LumperSerial::new(Box::new(writer)).map_err(Error::WriterError)?; + } + Ok(()) + } + + pub fn configure_console(&mut self, console_path: Option) -> Result<()> { if let Some(console_path) = console_path { // We create the file if it does not exist, else we open let file = File::create(&console_path).map_err(Error::ConsoleError)?; @@ -266,8 +274,16 @@ impl VMM { } } - pub fn configure(&mut self, num_vcpus: u8, mem_size_mb: u32, kernel_path: &str, console: Option) -> Result<()> { + pub fn configure( + &mut self, + num_vcpus: u8, + mem_size_mb: u32, + kernel_path: &str, + console: Option, + writer: Option, + ) -> Result<()> { self.configure_console(console)?; + self.configure_writer(writer); self.configure_memory(mem_size_mb)?; let kernel_load = kernel::kernel_setup(&self.guest_memory, PathBuf::from(kernel_path))?; self.configure_io()?;