Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions alioth-cli/src/boot/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use std::mem;
use std::path::{Path, PathBuf};

use alioth::board::{BoardSpec, CpuSpec};
use alioth::device::console::ConsoleSpec;
#[cfg(target_arch = "x86_64")]
use alioth::device::fw_cfg::FwCfgItemSpec;
use alioth::errors::{DebugTrace, trace_error};
Expand Down Expand Up @@ -156,6 +157,11 @@ pub struct BootArgs {
))]
vsock: Option<String>,

#[arg(long, help(
help_text::<ConsoleSpec>("Configure guest console.")
))]
console: Option<Box<str>>,

#[cfg(target_os = "linux")]
#[arg(long, help(help_text::<VfioCdevSpec>(
"Assign a host PCI device to the guest using IOMMUFD API."
Expand Down Expand Up @@ -322,6 +328,11 @@ fn parse_args(mut args: BootArgs, objects: HashMap<&str, &str>) -> Result<VmSpec
spec.vsock = Some(param);
}

if let Some(arg) = args.console {
let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?;
spec.console = param;
}

if let Some(arg) = args.balloon {
let param = serde_aco::from_args(&arg, &objects).context(error::ParseArg { arg })?;
spec.balloon = Some(param);
Expand Down Expand Up @@ -355,9 +366,9 @@ fn create<H: Hypervisor>(hypervisor: &H, spec: VmSpec) -> Result<Machine<H>, ali
let vm = Machine::new(hypervisor, spec.board)?;

#[cfg(target_arch = "x86_64")]
vm.add_com1()?;
vm.add_com1(&spec.console)?;
#[cfg(target_arch = "aarch64")]
vm.add_pl011()?;
vm.add_pl011(&spec.console)?;
#[cfg(target_arch = "aarch64")]
vm.add_pl031();

Expand Down
1 change: 1 addition & 0 deletions alioth-cli/src/boot/boot_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ fn test_parse_args() {
]);
let spec = parse_args(args, objects).unwrap();
let want = VmSpec {
console: Default::default(),
board: BoardSpec {
cpu: CpuSpec {
count: 16,
Expand Down
2 changes: 2 additions & 0 deletions alioth-cli/src/boot/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use std::path::Path;

use alioth::board::BoardSpec;
use alioth::device::console::ConsoleSpec;
#[cfg(target_arch = "x86_64")]
use alioth::device::fw_cfg::FwCfgItemSpec;
use alioth::loader::PayloadSpec;
Expand Down Expand Up @@ -98,6 +99,7 @@ pub struct VmSpec {

pub payload: PayloadSpec,

pub console: ConsoleSpec,
pub net: Vec<NetSpec>,
pub blk: Vec<BlkSpec>,
pub fs: Vec<FsSpec>,
Expand Down
70 changes: 70 additions & 0 deletions alioth/src/device/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
// limitations under the License.

use std::fmt::Debug;
use std::fs::{File, OpenOptions};
use std::io::{self, ErrorKind, Read, Write};
use std::mem::MaybeUninit;
use std::path::Path;
use std::sync::Arc;
use std::thread::JoinHandle;

Expand All @@ -24,6 +26,8 @@ use libc::{
};
use mio::unix::SourceFd;
use mio::{Events, Interest, Poll, Registry, Token};
use serde::Deserialize;
use serde_aco::Help;

use crate::device::Result;
use crate::ffi;
Expand All @@ -35,6 +39,61 @@ pub trait Console: Debug + Send + Sync + 'static {
fn deactivate(&self, registry: &Registry) -> io::Result<()>;
}

#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Help)]
pub struct LogConsoleSpec {
/// Path to the log file.
pub path: Box<Path>,
/// Append logs to the file if it already exits.
#[serde(default)]
pub append: bool,
}

#[derive(Debug)]
pub struct LogConsole {
file: File,
}

impl LogConsole {
pub fn new(spec: &LogConsoleSpec) -> Result<Self> {
let file = OpenOptions::new()
.create(true)
.write(true)
.append(spec.append)
.open(&spec.path)?;
Ok(LogConsole { file })
}
}

impl Console for LogConsole {
const TOKEN_INPUT: Token = Token(0);

fn activate(&self, _registry: &Registry) -> io::Result<()> {
Ok(())
}

fn deactivate(&self, _registry: &Registry) -> io::Result<()> {
Ok(())
}
}

impl Read for &LogConsole {
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
Ok(0)
}
}

impl Write for &LogConsole {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let mut file = &self.file;
file.write(buf)
}

fn flush(&mut self) -> io::Result<()> {
let mut file = &self.file;
file.flush()
}
}

#[derive(Debug)]
struct StdinBackup {
termios: Option<termios>,
Expand Down Expand Up @@ -131,6 +190,17 @@ impl Console for StdioConsole {
}
}

#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Help)]
pub enum ConsoleSpec {
/// Redirect guest console to host stdio (requires TTY).
#[default]
#[serde(alias = "stdio")]
Stdio,
/// Redirect guest console to a log file.
#[serde(alias = "log")]
Log(LogConsoleSpec),
}

pub trait UartRecv: Send + 'static {
fn receive(&self, bytes: &[u8]);
}
Expand Down
35 changes: 26 additions & 9 deletions alioth/src/vm/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ use crate::arch::layout::{PL011_START, PL031_START};
use crate::arch::layout::{PORT_CMOS_REG, PORT_COM1, PORT_FW_CFG_SELECTOR, PORT_FWDBG};
use crate::board::{Board, BoardSpec};
use crate::cpu::{Context, State, VcpuHandle, stop_vcpus, vcpu_thread};
use crate::device::MmioDev;
use crate::device::clock::SystemClock;
#[cfg(target_arch = "x86_64")]
use crate::device::cmos::Cmos;
use crate::device::console::StdioConsole;
use crate::device::console::{ConsoleSpec, LogConsole, StdioConsole};
#[cfg(target_arch = "x86_64")]
use crate::device::fw_cfg::{FwCfg, FwCfgItemSpec};
#[cfg(target_arch = "x86_64")]
Expand Down Expand Up @@ -174,12 +175,20 @@ where
}

#[cfg(target_arch = "x86_64")]
pub fn add_com1(&self) -> Result<(), Error> {
pub fn add_com1(&self, spec: &ConsoleSpec) -> Result<(), Error> {
let io_apic = self.ctx.board.arch.io_apic.clone();
let console = StdioConsole::new().context(error::CreateConsole)?;
let com1 = Serial::new(PORT_COM1, io_apic, 4, console).context(error::CreateConsole)?;
let com1: Arc<dyn MmioDev> = match spec {
ConsoleSpec::Stdio => {
let console = StdioConsole::new().context(error::CreateConsole)?;
Arc::new(Serial::new(PORT_COM1, io_apic, 4, console).context(error::CreateConsole)?)
}
ConsoleSpec::Log(spec) => {
let console = LogConsole::new(spec).context(error::CreateConsole)?;
Arc::new(Serial::new(PORT_COM1, io_apic, 4, console).context(error::CreateConsole)?)
}
};
let mut io_devs = self.ctx.board.io_devs.write();
io_devs.push((PORT_COM1, Arc::new(com1)));
io_devs.push((PORT_COM1, com1));
Ok(())
}

Expand All @@ -198,12 +207,20 @@ where
}

#[cfg(target_arch = "aarch64")]
pub fn add_pl011(&self) -> Result<(), Error> {
pub fn add_pl011(&self, spec: &ConsoleSpec) -> Result<(), Error> {
let irq_line = self.ctx.board.vm.create_irq_sender(1)?;
let console = StdioConsole::new().context(error::CreateConsole)?;
let pl011_dev = Pl011::new(PL011_START, irq_line, console).context(error::CreateConsole)?;
let pl011_dev: Arc<dyn MmioDev> = match spec {
ConsoleSpec::Stdio => {
let console = StdioConsole::new().context(error::CreateConsole)?;
Arc::new(Pl011::new(PL011_START, irq_line, console).context(error::CreateConsole)?)
}
ConsoleSpec::Log(spec) => {
let console = LogConsole::new(spec).context(error::CreateConsole)?;
Arc::new(Pl011::new(PL011_START, irq_line, console).context(error::CreateConsole)?)
}
};
let mut mmio_devs = self.ctx.board.mmio_devs.write();
mmio_devs.push((PL011_START, Arc::new(pl011_dev)));
mmio_devs.push((PL011_START, pl011_dev));
Ok(())
}

Expand Down