mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-11-24 20:48:55 +00:00
crosvm: add cmdline flags for configuring serial outputs in guest machine
This change allows an output to be set for each serial device for a guest machine (stdout, syslog, or sink). BUG=chromium:953983 TEST=FEATURES=test emerge-sarien crosvm; cd sys_util; cargo test; ./build_test; manual testing on x86_64 and aarch_64 Change-Id: I9e7fcb0b296c0f8a5aa8d54b1a74ae801f6badc8 Reviewed-on: https://chromium-review.googlesource.com/1572813 Commit-Ready: Trent Begin <tbegin@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com> Tested-by: Trent Begin <tbegin@chromium.org> Reviewed-by: Dylan Reid <dgreid@chromium.org>
This commit is contained in:
parent
6868c0a72f
commit
17ccaadc24
9 changed files with 572 additions and 146 deletions
|
@ -28,8 +28,8 @@ use crate::AARCH64_RTC_SIZE;
|
|||
use devices::pl030::PL030_AMBA_ID;
|
||||
|
||||
// These are serial device related constants.
|
||||
use crate::AARCH64_SERIAL_1_3_IRQ;
|
||||
use crate::AARCH64_SERIAL_ADDR;
|
||||
use crate::AARCH64_SERIAL_IRQ;
|
||||
use crate::AARCH64_SERIAL_SIZE;
|
||||
use crate::AARCH64_SERIAL_SPEED;
|
||||
|
||||
|
@ -135,7 +135,7 @@ fn create_serial_node(fdt: &mut Vec<u8>) -> Result<()> {
|
|||
let serial_reg_prop = generate_prop64(&[AARCH64_SERIAL_ADDR, AARCH64_SERIAL_SIZE]);
|
||||
let irq = generate_prop32(&[
|
||||
GIC_FDT_IRQ_TYPE_SPI,
|
||||
AARCH64_SERIAL_IRQ,
|
||||
AARCH64_SERIAL_1_3_IRQ,
|
||||
IRQ_TYPE_EDGE_RISING,
|
||||
]);
|
||||
|
||||
|
|
|
@ -2,16 +2,20 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::error::Error as StdError;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fmt::{self, Display};
|
||||
use std::fs::File;
|
||||
use std::io::{self, stdout};
|
||||
use std::io;
|
||||
use std::os::unix::io::FromRawFd;
|
||||
use std::sync::Arc;
|
||||
|
||||
use arch::{RunnableLinuxVm, VmComponents};
|
||||
use devices::{Bus, BusError, PciConfigMmio, PciDevice, PciInterruptPin};
|
||||
use devices::{
|
||||
get_serial_tty_string, Bus, BusError, PciConfigMmio, PciDevice, PciInterruptPin,
|
||||
SerialParameters,
|
||||
};
|
||||
use io_jail::Minijail;
|
||||
use remain::sorted;
|
||||
use resources::SystemAllocator;
|
||||
|
@ -84,7 +88,8 @@ const AARCH64_SERIAL_SIZE: u64 = 0x8;
|
|||
const AARCH64_SERIAL_SPEED: u32 = 1843200;
|
||||
// The serial device gets the first interrupt line
|
||||
// Which gets mapped to the first SPI interrupt (physical 32).
|
||||
const AARCH64_SERIAL_IRQ: u32 = 0;
|
||||
const AARCH64_SERIAL_1_3_IRQ: u32 = 0;
|
||||
const AARCH64_SERIAL_2_4_IRQ: u32 = 2;
|
||||
|
||||
// Place the RTC device at page 2
|
||||
const AARCH64_RTC_ADDR: u64 = 0x2000;
|
||||
|
@ -101,8 +106,8 @@ const AARCH64_PCI_CFG_SIZE: u64 = 0x1000000;
|
|||
const AARCH64_MMIO_BASE: u64 = 0x1010000;
|
||||
// Size of the whole MMIO region.
|
||||
const AARCH64_MMIO_SIZE: u64 = 0x100000;
|
||||
// Virtio devices start at SPI interrupt number 2
|
||||
const AARCH64_IRQ_BASE: u32 = 2;
|
||||
// Virtio devices start at SPI interrupt number 3
|
||||
const AARCH64_IRQ_BASE: u32 = 3;
|
||||
|
||||
#[sorted]
|
||||
#[derive(Debug)]
|
||||
|
@ -115,6 +120,7 @@ pub enum Error {
|
|||
CreateGICFailure(sys_util::Error),
|
||||
CreateKvm(sys_util::Error),
|
||||
CreatePciRoot(arch::DeviceRegistrationError),
|
||||
CreateSerialDevices(arch::DeviceRegistrationError),
|
||||
CreateSocket(io::Error),
|
||||
CreateVcpu(sys_util::Error),
|
||||
CreateVm(sys_util::Error),
|
||||
|
@ -145,6 +151,7 @@ impl Display for Error {
|
|||
CreateGICFailure(e) => write!(f, "failed to create GIC: {}", e),
|
||||
CreateKvm(e) => write!(f, "failed to open /dev/kvm: {}", e),
|
||||
CreatePciRoot(e) => write!(f, "failed to create a PCI root hub: {}", e),
|
||||
CreateSerialDevices(e) => write!(f, "unable to create serial devices: {}", e),
|
||||
CreateSocket(e) => write!(f, "failed to create socket: {}", e),
|
||||
CreateVcpu(e) => write!(f, "failed to create VCPU: {}", e),
|
||||
CreateVm(e) => write!(f, "failed to create vm: {}", e),
|
||||
|
@ -187,6 +194,7 @@ impl arch::LinuxArch for AArch64 {
|
|||
fn build_vm<F, E>(
|
||||
mut components: VmComponents,
|
||||
_split_irqchip: bool,
|
||||
serial_parameters: &BTreeMap<u8, SerialParameters>,
|
||||
create_devices: F,
|
||||
) -> Result<RunnableLinuxVm>
|
||||
where
|
||||
|
@ -220,7 +228,6 @@ impl arch::LinuxArch for AArch64 {
|
|||
let vcpu_affinity = components.vcpu_affinity;
|
||||
|
||||
let irq_chip = Self::create_irq_chip(&vm)?;
|
||||
let mut cmdline = Self::get_base_linux_cmdline();
|
||||
|
||||
let mut mmio_bus = devices::Bus::new();
|
||||
|
||||
|
@ -236,7 +243,22 @@ impl arch::LinuxArch for AArch64 {
|
|||
// ARM doesn't really use the io bus like x86, so just create an empty bus.
|
||||
let io_bus = devices::Bus::new();
|
||||
|
||||
let stdio_serial = Self::add_arch_devs(&mut vm, &mut mmio_bus)?;
|
||||
Self::add_arch_devs(&mut vm, &mut mmio_bus)?;
|
||||
|
||||
let com_evt_1_3 = EventFd::new().map_err(Error::CreateEventFd)?;
|
||||
let com_evt_2_4 = EventFd::new().map_err(Error::CreateEventFd)?;
|
||||
let (stdio_serial_num, stdio_serial) = arch::add_serial_devices(
|
||||
&mut mmio_bus,
|
||||
&com_evt_1_3,
|
||||
&com_evt_2_4,
|
||||
&serial_parameters,
|
||||
)
|
||||
.map_err(Error::CreateSerialDevices)?;
|
||||
|
||||
vm.register_irqfd(&com_evt_1_3, AARCH64_SERIAL_1_3_IRQ)
|
||||
.map_err(Error::RegisterIrqfd)?;
|
||||
vm.register_irqfd(&com_evt_2_4, AARCH64_SERIAL_2_4_IRQ)
|
||||
.map_err(Error::RegisterIrqfd)?;
|
||||
|
||||
mmio_bus
|
||||
.insert(
|
||||
|
@ -247,6 +269,7 @@ impl arch::LinuxArch for AArch64 {
|
|||
)
|
||||
.map_err(Error::RegisterPci)?;
|
||||
|
||||
let mut cmdline = Self::get_base_linux_cmdline(stdio_serial_num);
|
||||
for param in components.extra_kernel_params {
|
||||
cmdline.insert_str(¶m).map_err(Error::Cmdline)?;
|
||||
}
|
||||
|
@ -343,9 +366,13 @@ impl AArch64 {
|
|||
}
|
||||
|
||||
/// This returns a base part of the kernel command for this architecture
|
||||
fn get_base_linux_cmdline() -> kernel_cmdline::Cmdline {
|
||||
fn get_base_linux_cmdline(stdio_serial_num: Option<u8>) -> kernel_cmdline::Cmdline {
|
||||
let mut cmdline = kernel_cmdline::Cmdline::new(sys_util::pagesize());
|
||||
cmdline.insert_str("console=ttyS0 panic=-1").unwrap();
|
||||
if stdio_serial_num.is_some() {
|
||||
let tty_string = get_serial_tty_string(stdio_serial_num.unwrap());
|
||||
cmdline.insert("console", &tty_string).unwrap();
|
||||
}
|
||||
cmdline.insert_str("panic=-1").unwrap();
|
||||
cmdline
|
||||
}
|
||||
|
||||
|
@ -365,30 +392,16 @@ impl AArch64 {
|
|||
///
|
||||
/// * `vm` - The vm to add irqs to.
|
||||
/// * `bus` - The bus to add devices to.
|
||||
fn add_arch_devs(vm: &mut Vm, bus: &mut Bus) -> Result<Arc<Mutex<devices::Serial>>> {
|
||||
fn add_arch_devs(vm: &mut Vm, bus: &mut Bus) -> Result<()> {
|
||||
let rtc_evt = EventFd::new().map_err(Error::CreateEventFd)?;
|
||||
vm.register_irqfd(&rtc_evt, AARCH64_RTC_IRQ)
|
||||
.map_err(Error::RegisterIrqfd)?;
|
||||
|
||||
let com_evt_1_3 = EventFd::new().map_err(Error::CreateEventFd)?;
|
||||
vm.register_irqfd(&com_evt_1_3, AARCH64_SERIAL_IRQ)
|
||||
.map_err(Error::RegisterIrqfd)?;
|
||||
let serial = Arc::new(Mutex::new(devices::Serial::new_out(
|
||||
com_evt_1_3.try_clone().map_err(Error::CloneEventFd)?,
|
||||
Box::new(stdout()),
|
||||
)));
|
||||
bus.insert(
|
||||
serial.clone(),
|
||||
AARCH64_SERIAL_ADDR,
|
||||
AARCH64_SERIAL_SIZE,
|
||||
false,
|
||||
)
|
||||
.expect("failed to add serial device");
|
||||
|
||||
let rtc = Arc::new(Mutex::new(devices::pl030::Pl030::new(rtc_evt)));
|
||||
bus.insert(rtc, AARCH64_RTC_ADDR, AARCH64_RTC_SIZE, false)
|
||||
.expect("failed to add rtc device");
|
||||
Ok(serial)
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The creates the interrupt controller device and optionally returns the fd for it.
|
||||
|
|
|
@ -16,7 +16,7 @@ use std::sync::Arc;
|
|||
use devices::virtio::VirtioDevice;
|
||||
use devices::{
|
||||
Bus, BusDevice, BusError, PciDevice, PciDeviceError, PciInterruptPin, PciRoot, ProxyDevice,
|
||||
Serial,
|
||||
Serial, SerialParameters, DEFAULT_SERIAL_PARAMS, SERIAL_ADDR,
|
||||
};
|
||||
use io_jail::Minijail;
|
||||
use kvm::{IoeventAddress, Kvm, Vcpu, Vm};
|
||||
|
@ -42,7 +42,7 @@ pub struct RunnableLinuxVm {
|
|||
pub vm: Vm,
|
||||
pub kvm: Kvm,
|
||||
pub resources: SystemAllocator,
|
||||
pub stdio_serial: Arc<Mutex<Serial>>,
|
||||
pub stdio_serial: Option<Arc<Mutex<Serial>>>,
|
||||
pub exit_evt: EventFd,
|
||||
pub vcpus: Vec<Vcpu>,
|
||||
pub vcpu_affinity: Vec<usize>,
|
||||
|
@ -69,10 +69,12 @@ pub trait LinuxArch {
|
|||
///
|
||||
/// * `components` - Parts to use to build the VM.
|
||||
/// * `split_irqchip` - whether to use a split IRQ chip (i.e. userspace PIT/PIC/IOAPIC)
|
||||
/// * `serial_parameters` - definitions for how the serial devices should be configured.
|
||||
/// * `create_devices` - Function to generate a list of devices.
|
||||
fn build_vm<F, E>(
|
||||
components: VmComponents,
|
||||
split_irqchip: bool,
|
||||
serial_parameters: &BTreeMap<u8, SerialParameters>,
|
||||
create_devices: F,
|
||||
) -> Result<RunnableLinuxVm, Self::Error>
|
||||
where
|
||||
|
@ -91,6 +93,8 @@ pub enum DeviceRegistrationError {
|
|||
AllocateIrq,
|
||||
/// Could not create the mmio device to wrap a VirtioDevice.
|
||||
CreateMmioDevice(sys_util::Error),
|
||||
// Unable to create serial device from serial parameters
|
||||
CreateSerialDevice(devices::SerialError),
|
||||
/// Could not create an event fd.
|
||||
EventFdCreate(sys_util::Error),
|
||||
/// Could not add a device to the mmio bus.
|
||||
|
@ -120,6 +124,7 @@ impl Display for DeviceRegistrationError {
|
|||
AllocateDeviceAddrs(e) => write!(f, "Allocating device addresses: {}", e),
|
||||
AllocateIrq => write!(f, "Allocating IRQ number"),
|
||||
CreateMmioDevice(e) => write!(f, "failed to create mmio device: {}", e),
|
||||
CreateSerialDevice(e) => write!(f, "failed to create serial device: {}", e),
|
||||
Cmdline(e) => write!(f, "unable to add device to kernel command line: {}", e),
|
||||
EventFdCreate(e) => write!(f, "failed to create eventfd: {}", e),
|
||||
MmioInsert(e) => write!(f, "failed to add to mmio bus: {}", e),
|
||||
|
@ -212,6 +217,57 @@ pub fn generate_pci_root(
|
|||
Ok((root, pci_irqs, pid_labels))
|
||||
}
|
||||
|
||||
/// Adds serial devices to the provided bus based on the serial parameters given. Returns the serial
|
||||
/// port number and serial device to be used for stdout if defined.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `io_bus` - Bus to add the devices to
|
||||
/// * `com_evt_1_3` - eventfd for com1 and com3
|
||||
/// * `com_evt_1_4` - eventfd for com2 and com4
|
||||
/// * `io_bus` - Bus to add the devices to
|
||||
/// * `serial_parameters` - definitions of serial parameter configuationis. If a setting is not
|
||||
/// provided for a port, then it will use the default configuation.
|
||||
pub fn add_serial_devices(
|
||||
io_bus: &mut Bus,
|
||||
com_evt_1_3: &EventFd,
|
||||
com_evt_2_4: &EventFd,
|
||||
serial_parameters: &BTreeMap<u8, SerialParameters>,
|
||||
) -> Result<(Option<u8>, Option<Arc<Mutex<Serial>>>), DeviceRegistrationError> {
|
||||
let mut stdio_serial_num = None;
|
||||
let mut stdio_serial = None;
|
||||
|
||||
for x in 0..3 {
|
||||
let com_evt = match x {
|
||||
0 => com_evt_1_3,
|
||||
1 => com_evt_2_4,
|
||||
2 => com_evt_1_3,
|
||||
3 => com_evt_2_4,
|
||||
_ => com_evt_1_3,
|
||||
};
|
||||
|
||||
let param = serial_parameters
|
||||
.get(&(x + 1))
|
||||
.unwrap_or(&DEFAULT_SERIAL_PARAMS[x as usize]);
|
||||
|
||||
let com = Arc::new(Mutex::new(
|
||||
param
|
||||
.create_serial_device(&com_evt)
|
||||
.map_err(DeviceRegistrationError::CreateSerialDevice)?,
|
||||
));
|
||||
io_bus
|
||||
.insert(com.clone(), SERIAL_ADDR[x as usize], 0x8, false)
|
||||
.unwrap();
|
||||
|
||||
if param.console {
|
||||
stdio_serial_num = Some(x + 1);
|
||||
stdio_serial = Some(com.clone());
|
||||
}
|
||||
}
|
||||
|
||||
Ok((stdio_serial_num, stdio_serial))
|
||||
}
|
||||
|
||||
/// Errors for image loading.
|
||||
#[derive(Debug)]
|
||||
pub enum LoadImageError {
|
||||
|
|
|
@ -34,7 +34,10 @@ pub use self::pit::{Pit, PitError};
|
|||
pub use self::pl030::Pl030;
|
||||
pub use self::proxy::Error as ProxyError;
|
||||
pub use self::proxy::ProxyDevice;
|
||||
pub use self::serial::Serial;
|
||||
pub use self::serial::Error as SerialError;
|
||||
pub use self::serial::{
|
||||
get_serial_tty_string, Serial, SerialParameters, SerialType, DEFAULT_SERIAL_PARAMS, SERIAL_ADDR,
|
||||
};
|
||||
pub use self::usb::host_backend::host_backend_device_provider::HostBackendDeviceProvider;
|
||||
pub use self::usb::xhci::xhci_controller::XhciController;
|
||||
pub use self::virtio::VirtioPciDevice;
|
||||
|
|
|
@ -3,9 +3,12 @@
|
|||
// found in the LICENSE file.
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::io;
|
||||
use std::fmt::{self, Display};
|
||||
use std::io::{self, stdout};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use sys_util::{error, EventFd, Result};
|
||||
use sys_util::{error, syslog, EventFd, Result};
|
||||
|
||||
use crate::BusDevice;
|
||||
|
||||
|
@ -44,6 +47,147 @@ const DEFAULT_MODEM_CONTROL: u8 = 0x8; // Auxiliary output 2
|
|||
const DEFAULT_MODEM_STATUS: u8 = 0x20 | 0x10 | 0x80; // data ready, clear to send, carrier detect
|
||||
const DEFAULT_BAUD_DIVISOR: u16 = 12; // 9600 bps
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
CloneEventFd(sys_util::Error),
|
||||
InvalidSerialType(String),
|
||||
Unimplemented(SerialType),
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
#[remain::check]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::Error::*;
|
||||
|
||||
#[sorted]
|
||||
match self {
|
||||
CloneEventFd(e) => write!(f, "unable to clone an EventFd: {}", e),
|
||||
InvalidSerialType(e) => write!(f, "invalid serial type: {}", e),
|
||||
Unimplemented(e) => write!(f, "serial device type {} not implemented", e.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Enum for possible type of serial devices
|
||||
#[derive(Debug)]
|
||||
pub enum SerialType {
|
||||
File, // NOT IMPLEMENTED
|
||||
Stdout,
|
||||
Sink,
|
||||
Syslog,
|
||||
UnixSocket, // NOT IMPLEMENTED
|
||||
}
|
||||
|
||||
impl Display for SerialType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let s = match &self {
|
||||
SerialType::File => "File".to_string(),
|
||||
SerialType::Stdout => "Stdout".to_string(),
|
||||
SerialType::Sink => "Sink".to_string(),
|
||||
SerialType::Syslog => "Syslog".to_string(),
|
||||
SerialType::UnixSocket => "UnixSocket".to_string(),
|
||||
};
|
||||
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for SerialType {
|
||||
type Err = Error;
|
||||
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
|
||||
match s {
|
||||
"file" | "File" => return Ok(SerialType::File),
|
||||
"stdout" | "Stdout" => return Ok(SerialType::Stdout),
|
||||
"sink" | "Sink" => return Ok(SerialType::Sink),
|
||||
"syslog" | "Syslog" => return Ok(SerialType::Syslog),
|
||||
"unix" | "UnixSocket" => return Ok(SerialType::UnixSocket),
|
||||
_ => Err(Error::InvalidSerialType(s.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds the parameters for a serial device
|
||||
#[derive(Debug)]
|
||||
pub struct SerialParameters {
|
||||
pub type_: SerialType,
|
||||
pub path: Option<PathBuf>,
|
||||
pub num: u8,
|
||||
pub console: bool,
|
||||
}
|
||||
|
||||
impl SerialParameters {
|
||||
/// Helper function to create a serial device from the defined parameters.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `evt_fd` - eventfd used for interrupt events
|
||||
pub fn create_serial_device(&self, evt_fd: &EventFd) -> std::result::Result<Serial, Error> {
|
||||
match self.type_ {
|
||||
SerialType::Stdout => Ok(Serial::new_out(
|
||||
evt_fd.try_clone().map_err(Error::CloneEventFd)?,
|
||||
Box::new(stdout()),
|
||||
)),
|
||||
SerialType::Sink => Ok(Serial::new_sink(
|
||||
evt_fd.try_clone().map_err(Error::CloneEventFd)?,
|
||||
)),
|
||||
SerialType::Syslog => Ok(Serial::new_out(
|
||||
evt_fd.try_clone().map_err(Error::CloneEventFd)?,
|
||||
Box::new(syslog::Syslogger::new(
|
||||
syslog::Priority::Info,
|
||||
syslog::Facility::Daemon,
|
||||
)),
|
||||
)),
|
||||
SerialType::File => Err(Error::Unimplemented(SerialType::File)),
|
||||
SerialType::UnixSocket => Err(Error::Unimplemented(SerialType::UnixSocket)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Structure for holding the default configuration of the serial devices.
|
||||
pub const DEFAULT_SERIAL_PARAMS: [SerialParameters; 4] = [
|
||||
SerialParameters {
|
||||
type_: SerialType::Stdout,
|
||||
path: None,
|
||||
num: 1,
|
||||
console: true,
|
||||
},
|
||||
SerialParameters {
|
||||
type_: SerialType::Sink,
|
||||
path: None,
|
||||
num: 2,
|
||||
console: false,
|
||||
},
|
||||
SerialParameters {
|
||||
type_: SerialType::Sink,
|
||||
path: None,
|
||||
num: 3,
|
||||
console: false,
|
||||
},
|
||||
SerialParameters {
|
||||
type_: SerialType::Sink,
|
||||
path: None,
|
||||
num: 4,
|
||||
console: false,
|
||||
},
|
||||
];
|
||||
|
||||
/// Address for Serial ports in x86
|
||||
pub const SERIAL_ADDR: [u64; 4] = [0x3f8, 0x2f8, 0x3e8, 0x2e8];
|
||||
|
||||
/// String representations of serial devices
|
||||
pub const SERIAL_TTY_STRINGS: [&str; 4] = ["ttyS0", "ttyS1", "ttyS2", "ttyS3"];
|
||||
|
||||
/// Helper function to get the tty string of a serial device based on the port number. Will default
|
||||
/// to ttyS0 if an invalid number is given.
|
||||
pub fn get_serial_tty_string(stdio_serial_num: u8) -> String {
|
||||
match stdio_serial_num {
|
||||
1 => SERIAL_TTY_STRINGS[0].to_string(),
|
||||
2 => SERIAL_TTY_STRINGS[1].to_string(),
|
||||
3 => SERIAL_TTY_STRINGS[2].to_string(),
|
||||
4 => SERIAL_TTY_STRINGS[3].to_string(),
|
||||
_ => SERIAL_TTY_STRINGS[0].to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Emulates serial COM ports commonly seen on x86 I/O ports 0x3f8/0x2f8/0x3e8/0x2e8.
|
||||
///
|
||||
/// This can optionally write the guest's output to a Write trait object. To send input to the
|
||||
|
|
45
src/linux.rs
45
src/linux.rs
|
@ -789,7 +789,7 @@ fn create_virtio_devices(
|
|||
}
|
||||
|
||||
fn create_devices(
|
||||
cfg: Config,
|
||||
cfg: &Config,
|
||||
mem: &GuestMemory,
|
||||
exit_evt: &EventFd,
|
||||
wayland_device_socket: WlControlRequestSocket,
|
||||
|
@ -1180,17 +1180,22 @@ pub fn run_config(cfg: Config) -> Result<()> {
|
|||
}
|
||||
|
||||
let sandbox = cfg.sandbox;
|
||||
let linux = Arch::build_vm(components, cfg.split_irqchip, |m, e| {
|
||||
create_devices(
|
||||
cfg,
|
||||
m,
|
||||
e,
|
||||
wayland_device_socket,
|
||||
balloon_device_socket,
|
||||
&mut disk_device_sockets,
|
||||
usb_provider,
|
||||
)
|
||||
})
|
||||
let linux = Arch::build_vm(
|
||||
components,
|
||||
cfg.split_irqchip,
|
||||
&cfg.serial_parameters,
|
||||
|m, e| {
|
||||
create_devices(
|
||||
&cfg,
|
||||
m,
|
||||
e,
|
||||
wayland_device_socket,
|
||||
balloon_device_socket,
|
||||
&mut disk_device_sockets,
|
||||
usb_provider,
|
||||
)
|
||||
},
|
||||
)
|
||||
.map_err(Error::BuildVm)?;
|
||||
|
||||
let _render_node_host = ();
|
||||
|
@ -1401,13 +1406,15 @@ fn run_control(
|
|||
warn!("error while reading stdin: {}", e);
|
||||
let _ = poll_ctx.delete(&stdin_handle);
|
||||
}
|
||||
Ok(count) => {
|
||||
linux
|
||||
.stdio_serial
|
||||
.lock()
|
||||
.queue_input_bytes(&out[..count])
|
||||
.expect("failed to queue bytes into serial port");
|
||||
}
|
||||
Ok(count) => match linux.stdio_serial {
|
||||
Some(ref stdio_serial) => {
|
||||
stdio_serial
|
||||
.lock()
|
||||
.queue_input_bytes(&out[..count])
|
||||
.expect("failed to queue bytes into serial port");
|
||||
}
|
||||
None => {}
|
||||
},
|
||||
}
|
||||
}
|
||||
Token::ChildSignal => {
|
||||
|
|
129
src/main.rs
129
src/main.rs
|
@ -10,6 +10,7 @@ pub mod panic_hook;
|
|||
#[cfg(feature = "plugin")]
|
||||
pub mod plugin;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::net;
|
||||
|
@ -20,6 +21,7 @@ use std::string::String;
|
|||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
|
||||
use devices::{SerialParameters, SerialType};
|
||||
use msg_socket::{MsgReceiver, MsgSender, MsgSocket};
|
||||
use qcow::QcowFile;
|
||||
use sys_util::{
|
||||
|
@ -102,6 +104,8 @@ pub struct Config {
|
|||
software_tpm: bool,
|
||||
cras_audio: bool,
|
||||
null_audio: bool,
|
||||
serial_parameters: BTreeMap<u8, SerialParameters>,
|
||||
syslog_tag: Option<String>,
|
||||
virtio_single_touch: Option<TouchDeviceOption>,
|
||||
virtio_trackpad: Option<TouchDeviceOption>,
|
||||
virtio_mouse: Option<PathBuf>,
|
||||
|
@ -141,6 +145,8 @@ impl Default for Config {
|
|||
seccomp_policy_dir: PathBuf::from(SECCOMP_POLICY_DIR),
|
||||
cras_audio: false,
|
||||
null_audio: false,
|
||||
serial_parameters: BTreeMap::new(),
|
||||
syslog_tag: None,
|
||||
virtio_single_touch: None,
|
||||
virtio_trackpad: None,
|
||||
virtio_mouse: None,
|
||||
|
@ -221,6 +227,58 @@ fn parse_cpu_set(s: &str) -> argument::Result<Vec<usize>> {
|
|||
Ok(cpuset)
|
||||
}
|
||||
|
||||
fn parse_serial_options(s: &str) -> argument::Result<SerialParameters> {
|
||||
let mut serial_setting = SerialParameters {
|
||||
type_: SerialType::Sink,
|
||||
path: None,
|
||||
num: 0,
|
||||
console: false,
|
||||
};
|
||||
|
||||
let opts = s
|
||||
.split(",")
|
||||
.map(|frag| frag.split("="))
|
||||
.map(|mut kv| (kv.next().unwrap_or(""), kv.next().unwrap_or("")));
|
||||
|
||||
for (k, v) in opts {
|
||||
match k {
|
||||
"type" => {
|
||||
serial_setting.type_ = v
|
||||
.parse::<SerialType>()
|
||||
.map_err(|e| argument::Error::UnknownArgument(format!("{}", e)))?
|
||||
}
|
||||
"num" => {
|
||||
let num = v.parse::<u8>().map_err(|e| {
|
||||
argument::Error::Syntax(format!("serial device number is not parsable: {}", e))
|
||||
})?;
|
||||
if num < 1 || num > 4 {
|
||||
return Err(argument::Error::InvalidValue {
|
||||
value: num.to_string(),
|
||||
expected: "Serial port num must be between 1 - 4",
|
||||
});
|
||||
}
|
||||
serial_setting.num = num;
|
||||
}
|
||||
"console" => {
|
||||
serial_setting.console = v.parse::<bool>().map_err(|e| {
|
||||
argument::Error::Syntax(format!(
|
||||
"serial device console is not parseable: {}",
|
||||
e
|
||||
))
|
||||
})?
|
||||
}
|
||||
_ => {
|
||||
return Err(argument::Error::UnknownArgument(format!(
|
||||
"serial parameter {}",
|
||||
k
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(serial_setting)
|
||||
}
|
||||
|
||||
fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> {
|
||||
match name {
|
||||
"" => {
|
||||
|
@ -312,6 +370,38 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
|
|||
"null-audio" => {
|
||||
cfg.null_audio = true;
|
||||
}
|
||||
"serial" => {
|
||||
let serial_params = parse_serial_options(value.unwrap())?;
|
||||
let num = serial_params.num;
|
||||
if cfg.serial_parameters.contains_key(&num) {
|
||||
return Err(argument::Error::TooManyArguments(format!(
|
||||
"serial num {}",
|
||||
num
|
||||
)));
|
||||
}
|
||||
|
||||
if serial_params.console {
|
||||
for (_, params) in &cfg.serial_parameters {
|
||||
if params.console {
|
||||
return Err(argument::Error::TooManyArguments(format!(
|
||||
"serial device {} already set as console",
|
||||
params.num
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cfg.serial_parameters.insert(num, serial_params);
|
||||
}
|
||||
"syslog-tag" => {
|
||||
if cfg.syslog_tag.is_some() {
|
||||
return Err(argument::Error::TooManyArguments(
|
||||
"`syslog-tag` already given".to_owned(),
|
||||
));
|
||||
}
|
||||
syslog::set_proc_name(value.unwrap());
|
||||
cfg.syslog_tag = Some(value.unwrap().to_owned());
|
||||
}
|
||||
"root" | "disk" | "rwdisk" | "qcow" | "rwqcow" => {
|
||||
let disk_path = PathBuf::from(value.unwrap());
|
||||
if !disk_path.exists() {
|
||||
|
@ -702,6 +792,15 @@ fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
|
|||
Argument::value("mac", "MAC", "MAC address for VM."),
|
||||
Argument::flag("cras-audio", "Add an audio device to the VM that plays samples through CRAS server"),
|
||||
Argument::flag("null-audio", "Add an audio device to the VM that plays samples to /dev/null"),
|
||||
Argument::value("serial",
|
||||
"type=TYPE,[path=PATH,num=NUM,console]",
|
||||
"Comma seperated key=value pairs for setting up serial devices. Can be given more than once.
|
||||
Possible key values:
|
||||
type=(stdout,syslog,sink) - Where to route the serial device
|
||||
num=(1,2,3,4) - Serial Device Number
|
||||
console - Use this serial device as the guest console. Can only be given once. Will default to first serial port if not provided.
|
||||
"),
|
||||
Argument::value("syslog-tag", "TAG", "When logging to syslog, use the provided tag."),
|
||||
Argument::value("wayland-sock", "PATH", "Path to the Wayland socket to use."),
|
||||
#[cfg(feature = "wl-dmabuf")]
|
||||
Argument::flag("wayland-dmabuf", "Enable support for DMABufs in Wayland device."),
|
||||
|
@ -1271,4 +1370,34 @@ mod tests {
|
|||
fn parse_cpu_set_extra_comma() {
|
||||
parse_cpu_set("0,1,2,").expect_err("parse should have failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_serial_vaild() {
|
||||
parse_serial_options("type=syslog,num=1,console=true").expect("parse should have succeded");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_serial_invalid_type() {
|
||||
parse_serial_options("type=wormhole,num=1").expect_err("parse should have failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_serial_invalid_num_upper() {
|
||||
parse_serial_options("type=syslog,num=5").expect_err("parse should have failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_serial_invalid_num_lower() {
|
||||
parse_serial_options("type=syslog,num=0").expect_err("parse should have failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_serial_invalid_num_string() {
|
||||
parse_serial_options("type=syslog,num=number3").expect_err("parse should have failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_serial_invalid_option() {
|
||||
parse_serial_options("type=syslog,speed=lightspeed").expect_err("parse should have failed");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,6 +83,7 @@ impl Display for Priority {
|
|||
/// The facility of a syslog message.
|
||||
///
|
||||
/// See syslog man pages for information on their semantics.
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Facility {
|
||||
Kernel = 0,
|
||||
User = 1 << 3,
|
||||
|
@ -387,8 +388,8 @@ fn get_localtime() -> tm {
|
|||
/// # Arguments
|
||||
/// * `pri` - The `Priority` (i.e. severity) of the log message.
|
||||
/// * `fac` - The `Facility` of the log message. Usually `Facility::User` should be used.
|
||||
/// * `file_name` - Name of the file that generated the log.
|
||||
/// * `line` - Line number within `file_name` that generated the log.
|
||||
/// * `file_line` - Optional tuple of the name of the file that generated the
|
||||
/// log and the line number within that file.
|
||||
/// * `args` - The log's message to record, in the form of `format_args!()` return value
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -402,12 +403,11 @@ fn get_localtime() -> tm {
|
|||
/// # }
|
||||
/// syslog::log(syslog::Priority::Error,
|
||||
/// syslog::Facility::User,
|
||||
/// file!(),
|
||||
/// line!(),
|
||||
/// Some((file!(), line!())),
|
||||
/// format_args!("hello syslog"));
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn log(pri: Priority, fac: Facility, file_name: &str, line: u32, args: fmt::Arguments) {
|
||||
pub fn log(pri: Priority, fac: Facility, file_line: Option<(&str, u32)>, args: fmt::Arguments) {
|
||||
const MONTHS: [&str; 12] = [
|
||||
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
|
||||
];
|
||||
|
@ -417,46 +417,52 @@ pub fn log(pri: Priority, fac: Facility, file_name: &str, line: u32, args: fmt::
|
|||
if let Some(socket) = &state.socket {
|
||||
let tm = get_localtime();
|
||||
let prifac = (pri as u8) | (fac as u8);
|
||||
let (res, len) = {
|
||||
let res = {
|
||||
let mut buf_cursor = Cursor::new(&mut buf[..]);
|
||||
(
|
||||
write!(
|
||||
&mut buf_cursor,
|
||||
"<{}>{} {:02} {:02}:{:02}:{:02} {}[{}]: [{}:{}] {}",
|
||||
prifac,
|
||||
MONTHS[tm.tm_mon as usize],
|
||||
tm.tm_mday,
|
||||
tm.tm_hour,
|
||||
tm.tm_min,
|
||||
tm.tm_sec,
|
||||
state.proc_name.as_ref().map(|s| s.as_ref()).unwrap_or("-"),
|
||||
getpid(),
|
||||
file_name,
|
||||
line,
|
||||
args
|
||||
),
|
||||
buf_cursor.position() as usize,
|
||||
write!(
|
||||
&mut buf_cursor,
|
||||
"<{}>{} {:02} {:02}:{:02}:{:02} {}[{}]: ",
|
||||
prifac,
|
||||
MONTHS[tm.tm_mon as usize],
|
||||
tm.tm_mday,
|
||||
tm.tm_hour,
|
||||
tm.tm_min,
|
||||
tm.tm_sec,
|
||||
state.proc_name.as_ref().map(|s| s.as_ref()).unwrap_or("-"),
|
||||
getpid()
|
||||
)
|
||||
.and_then(|()| {
|
||||
if let Some((file_name, line)) = &file_line {
|
||||
write!(&mut buf_cursor, " [{}:{}] ", file_name, line)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
.and_then(|()| write!(&mut buf_cursor, "{}", args))
|
||||
.and_then(|()| Ok(buf_cursor.position() as usize))
|
||||
};
|
||||
|
||||
if res.is_ok() {
|
||||
send_buf(&socket, &buf[..len]);
|
||||
if let Ok(len) = &res {
|
||||
send_buf(&socket, &buf[..*len])
|
||||
}
|
||||
}
|
||||
|
||||
let (res, len) = {
|
||||
let res = {
|
||||
let mut buf_cursor = Cursor::new(&mut buf[..]);
|
||||
(
|
||||
writeln!(&mut buf_cursor, "[{}:{}:{}] {}", pri, file_name, line, args),
|
||||
buf_cursor.position() as usize,
|
||||
)
|
||||
if let Some((file_name, line)) = &file_line {
|
||||
write!(&mut buf_cursor, "[{}:{}:{}] ", pri, file_name, line)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
.and_then(|()| writeln!(&mut buf_cursor, "{}", args))
|
||||
.and_then(|()| Ok(buf_cursor.position() as usize))
|
||||
};
|
||||
if res.is_ok() {
|
||||
if let Ok(len) = &res {
|
||||
if let Some(file) = &mut state.file {
|
||||
let _ = file.write_all(&buf[..len]);
|
||||
let _ = file.write_all(&buf[..*len]);
|
||||
}
|
||||
if state.stderr {
|
||||
let _ = stderr().write_all(&buf[..len]);
|
||||
let _ = stderr().write_all(&buf[..*len]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -467,7 +473,7 @@ pub fn log(pri: Priority, fac: Facility, file_name: &str, line: u32, args: fmt::
|
|||
#[macro_export]
|
||||
macro_rules! log {
|
||||
($pri:expr, $($args:tt)+) => ({
|
||||
$crate::syslog::log($pri, $crate::syslog::Facility::User, file!(), line!(), format_args!($($args)+))
|
||||
$crate::syslog::log($pri, $crate::syslog::Facility::User, Some((file!(), line!())), format_args!($($args)+))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -503,6 +509,44 @@ macro_rules! debug {
|
|||
($($args:tt)+) => ($crate::log!($crate::syslog::Priority::Debug, $($args)*))
|
||||
}
|
||||
|
||||
// Struct that implements io::Write to be used for writing directly to the syslog
|
||||
pub struct Syslogger {
|
||||
buf: String,
|
||||
priority: Priority,
|
||||
facility: Facility,
|
||||
}
|
||||
|
||||
impl Syslogger {
|
||||
pub fn new(p: Priority, f: Facility) -> Syslogger {
|
||||
Syslogger {
|
||||
buf: String::new(),
|
||||
priority: p,
|
||||
facility: f,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Write for Syslogger {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let parsed_str = String::from_utf8_lossy(buf);
|
||||
self.buf.push_str(&parsed_str);
|
||||
|
||||
if let Some(last_newline_idx) = self.buf.rfind('\n') {
|
||||
for line in self.buf[..last_newline_idx].lines() {
|
||||
log(self.priority, self.facility, None, format_args!("{}", line));
|
||||
}
|
||||
|
||||
self.buf.drain(..=last_newline_idx);
|
||||
}
|
||||
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -535,8 +579,7 @@ mod tests {
|
|||
log(
|
||||
Priority::Error,
|
||||
Facility::User,
|
||||
file!(),
|
||||
line!(),
|
||||
Some((file!(), line!())),
|
||||
format_args!("hello syslog"),
|
||||
);
|
||||
}
|
||||
|
@ -547,16 +590,14 @@ mod tests {
|
|||
log(
|
||||
Priority::Error,
|
||||
Facility::User,
|
||||
file!(),
|
||||
line!(),
|
||||
Some((file!(), line!())),
|
||||
format_args!("before proc name"),
|
||||
);
|
||||
set_proc_name("sys_util-test");
|
||||
log(
|
||||
Priority::Error,
|
||||
Facility::User,
|
||||
file!(),
|
||||
line!(),
|
||||
Some((file!(), line!())),
|
||||
format_args!("after proc name"),
|
||||
);
|
||||
}
|
||||
|
@ -579,8 +620,7 @@ mod tests {
|
|||
log(
|
||||
Priority::Error,
|
||||
Facility::User,
|
||||
file!(),
|
||||
line!(),
|
||||
Some((file!(), line!())),
|
||||
format_args!("{}", TEST_STR),
|
||||
);
|
||||
|
||||
|
@ -600,4 +640,42 @@ mod tests {
|
|||
info!("this is info {}", true);
|
||||
debug!("this is debug info {:?}", Some("helpful stuff"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn syslogger_char() {
|
||||
init().unwrap();
|
||||
let mut syslogger = Syslogger::new(Priority::Info, Facility::Daemon);
|
||||
|
||||
let string = "Writing chars to syslog";
|
||||
for c in string.chars() {
|
||||
syslogger.write(&[c as u8]).expect("error writing char");
|
||||
}
|
||||
|
||||
syslogger
|
||||
.write(&['\n' as u8])
|
||||
.expect("error writing newline char");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn syslogger_line() {
|
||||
init().unwrap();
|
||||
let mut syslogger = Syslogger::new(Priority::Info, Facility::Daemon);
|
||||
|
||||
let s = "Writing string to syslog\n";
|
||||
syslogger
|
||||
.write(&s.as_bytes())
|
||||
.expect("error writing string");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn syslogger_partial() {
|
||||
init().unwrap();
|
||||
let mut syslogger = Syslogger::new(Priority::Info, Facility::Daemon);
|
||||
|
||||
let s = "Writing partial string";
|
||||
// Should not log because there is no newline character
|
||||
syslogger
|
||||
.write(&s.as_bytes())
|
||||
.expect("error writing string");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,18 +54,19 @@ mod mptable;
|
|||
mod regs;
|
||||
mod smbios;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::error::Error as StdError;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fmt::{self, Display};
|
||||
use std::fs::File;
|
||||
use std::io::{self, stdout};
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::bootparam::boot_params;
|
||||
use crate::bootparam::E820_RAM;
|
||||
use arch::{RunnableLinuxVm, VmComponents};
|
||||
use devices::{PciConfigIo, PciDevice, PciInterruptPin};
|
||||
use devices::{get_serial_tty_string, PciConfigIo, PciDevice, PciInterruptPin, SerialParameters};
|
||||
use io_jail::Minijail;
|
||||
use kvm::*;
|
||||
use remain::sorted;
|
||||
|
@ -87,6 +88,7 @@ pub enum Error {
|
|||
CreatePciRoot(arch::DeviceRegistrationError),
|
||||
CreatePit(sys_util::Error),
|
||||
CreatePitDevice(devices::PitError),
|
||||
CreateSerialDevices(arch::DeviceRegistrationError),
|
||||
CreateSocket(io::Error),
|
||||
CreateVcpu(sys_util::Error),
|
||||
CreateVm(sys_util::Error),
|
||||
|
@ -130,6 +132,7 @@ impl Display for Error {
|
|||
CreatePciRoot(e) => write!(f, "failed to create a PCI root hub: {}", e),
|
||||
CreatePit(e) => write!(f, "unable to create PIT: {}", e),
|
||||
CreatePitDevice(e) => write!(f, "unable to make PIT device: {}", e),
|
||||
CreateSerialDevices(e) => write!(f, "unable to create serial devices: {}", e),
|
||||
CreateSocket(e) => write!(f, "failed to create socket: {}", e),
|
||||
CreateVcpu(e) => write!(f, "failed to create VCPU: {}", e),
|
||||
CreateVm(e) => write!(f, "failed to create VM: {}", e),
|
||||
|
@ -172,6 +175,8 @@ const ZERO_PAGE_OFFSET: u64 = 0x7000;
|
|||
const KERNEL_START_OFFSET: u64 = 0x200000;
|
||||
const CMDLINE_OFFSET: u64 = 0x20000;
|
||||
const CMDLINE_MAX_SIZE: u64 = KERNEL_START_OFFSET - CMDLINE_OFFSET;
|
||||
const X86_64_SERIAL_1_3_IRQ: u32 = 4;
|
||||
const X86_64_SERIAL_2_4_IRQ: u32 = 3;
|
||||
const X86_64_IRQ_BASE: u32 = 5;
|
||||
|
||||
fn configure_system(
|
||||
|
@ -296,6 +301,7 @@ impl arch::LinuxArch for X8664arch {
|
|||
fn build_vm<F, E>(
|
||||
mut components: VmComponents,
|
||||
split_irqchip: bool,
|
||||
serial_parameters: &BTreeMap<u8, SerialParameters>,
|
||||
create_devices: F,
|
||||
) -> Result<RunnableLinuxVm>
|
||||
where
|
||||
|
@ -329,7 +335,6 @@ impl arch::LinuxArch for X8664arch {
|
|||
let vcpu_affinity = components.vcpu_affinity;
|
||||
|
||||
let irq_chip = Self::create_irq_chip(&vm)?;
|
||||
let mut cmdline = Self::get_base_linux_cmdline();
|
||||
|
||||
let mut mmio_bus = devices::Bus::new();
|
||||
|
||||
|
@ -342,13 +347,17 @@ impl arch::LinuxArch for X8664arch {
|
|||
.map_err(Error::CreatePciRoot)?;
|
||||
let pci_bus = Arc::new(Mutex::new(PciConfigIo::new(pci)));
|
||||
|
||||
let (io_bus, stdio_serial) = Self::setup_io_bus(
|
||||
let mut io_bus = Self::setup_io_bus(
|
||||
&mut vm,
|
||||
split_irqchip,
|
||||
exit_evt.try_clone().map_err(Error::CloneEventFd)?,
|
||||
Some(pci_bus.clone()),
|
||||
)?;
|
||||
|
||||
let (stdio_serial_num, stdio_serial) =
|
||||
Self::setup_serial_devices(&mut vm, &mut io_bus, &serial_parameters)?;
|
||||
|
||||
let mut cmdline = Self::get_base_linux_cmdline(stdio_serial_num);
|
||||
for param in components.extra_kernel_params {
|
||||
cmdline.insert_str(¶m).map_err(Error::Cmdline)?;
|
||||
}
|
||||
|
@ -533,11 +542,14 @@ impl X8664arch {
|
|||
}
|
||||
|
||||
/// This returns a minimal kernel command for this architecture
|
||||
fn get_base_linux_cmdline() -> kernel_cmdline::Cmdline {
|
||||
fn get_base_linux_cmdline(stdio_serial_num: Option<u8>) -> kernel_cmdline::Cmdline {
|
||||
let mut cmdline = kernel_cmdline::Cmdline::new(CMDLINE_MAX_SIZE as usize);
|
||||
cmdline
|
||||
.insert_str("console=ttyS0 noacpi reboot=k panic=-1")
|
||||
.unwrap();
|
||||
if stdio_serial_num.is_some() {
|
||||
let tty_string = get_serial_tty_string(stdio_serial_num.unwrap());
|
||||
cmdline.insert("console", &tty_string).unwrap();
|
||||
}
|
||||
cmdline.insert_str("noacpi reboot=k panic=-1").unwrap();
|
||||
|
||||
cmdline
|
||||
}
|
||||
|
||||
|
@ -565,7 +577,7 @@ impl X8664arch {
|
|||
split_irqchip: bool,
|
||||
exit_evt: EventFd,
|
||||
pci: Option<Arc<Mutex<devices::PciConfigIo>>>,
|
||||
) -> Result<(devices::Bus, Arc<Mutex<devices::Serial>>)> {
|
||||
) -> Result<(devices::Bus)> {
|
||||
struct NoDevice;
|
||||
impl devices::BusDevice for NoDevice {
|
||||
fn debug_label(&self) -> String {
|
||||
|
@ -575,46 +587,6 @@ impl X8664arch {
|
|||
|
||||
let mut io_bus = devices::Bus::new();
|
||||
|
||||
let com_evt_1_3 = EventFd::new().map_err(Error::CreateEventFd)?;
|
||||
let com_evt_2_4 = EventFd::new().map_err(Error::CreateEventFd)?;
|
||||
let stdio_serial = Arc::new(Mutex::new(devices::Serial::new_out(
|
||||
com_evt_1_3.try_clone().map_err(Error::CloneEventFd)?,
|
||||
Box::new(stdout()),
|
||||
)));
|
||||
let nul_device = Arc::new(Mutex::new(NoDevice));
|
||||
io_bus
|
||||
.insert(stdio_serial.clone(), 0x3f8, 0x8, false)
|
||||
.unwrap();
|
||||
io_bus
|
||||
.insert(
|
||||
Arc::new(Mutex::new(devices::Serial::new_sink(
|
||||
com_evt_2_4.try_clone().map_err(Error::CloneEventFd)?,
|
||||
))),
|
||||
0x2f8,
|
||||
0x8,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
io_bus
|
||||
.insert(
|
||||
Arc::new(Mutex::new(devices::Serial::new_sink(
|
||||
com_evt_1_3.try_clone().map_err(Error::CloneEventFd)?,
|
||||
))),
|
||||
0x3e8,
|
||||
0x8,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
io_bus
|
||||
.insert(
|
||||
Arc::new(Mutex::new(devices::Serial::new_sink(
|
||||
com_evt_2_4.try_clone().map_err(Error::CloneEventFd)?,
|
||||
))),
|
||||
0x2e8,
|
||||
0x8,
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
io_bus
|
||||
.insert(Arc::new(Mutex::new(devices::Cmos::new())), 0x70, 0x2, false)
|
||||
.unwrap();
|
||||
|
@ -629,6 +601,7 @@ impl X8664arch {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
let nul_device = Arc::new(Mutex::new(NoDevice));
|
||||
if split_irqchip {
|
||||
let pit_evt = EventFd::new().map_err(Error::CreateEventFd)?;
|
||||
let pit = Arc::new(Mutex::new(
|
||||
|
@ -664,12 +637,35 @@ impl X8664arch {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
vm.register_irqfd(&com_evt_1_3, 4)
|
||||
Ok(io_bus)
|
||||
}
|
||||
|
||||
/// Sets up the serial devices for this platform. Returns the serial port number and serial
|
||||
/// device to be used for stdout
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * - `vm` the vm object
|
||||
/// * - `io_bus` the I/O bus to add the devices to
|
||||
/// * - `serial_parmaters` - definitions for how the serial devices should be configured
|
||||
fn setup_serial_devices(
|
||||
vm: &mut Vm,
|
||||
io_bus: &mut devices::Bus,
|
||||
serial_parameters: &BTreeMap<u8, SerialParameters>,
|
||||
) -> Result<(Option<u8>, Option<Arc<Mutex<devices::Serial>>>)> {
|
||||
let com_evt_1_3 = EventFd::new().map_err(Error::CreateEventFd)?;
|
||||
let com_evt_2_4 = EventFd::new().map_err(Error::CreateEventFd)?;
|
||||
|
||||
let (stdio_serial_num, stdio_serial) =
|
||||
arch::add_serial_devices(io_bus, &com_evt_1_3, &com_evt_2_4, &serial_parameters)
|
||||
.map_err(Error::CreateSerialDevices)?;
|
||||
|
||||
vm.register_irqfd(&com_evt_1_3, X86_64_SERIAL_1_3_IRQ)
|
||||
.map_err(Error::RegisterIrqfd)?;
|
||||
vm.register_irqfd(&com_evt_2_4, 3)
|
||||
vm.register_irqfd(&com_evt_2_4, X86_64_SERIAL_2_4_IRQ)
|
||||
.map_err(Error::RegisterIrqfd)?;
|
||||
|
||||
Ok((io_bus, stdio_serial))
|
||||
Ok((stdio_serial_num, stdio_serial))
|
||||
}
|
||||
|
||||
/// Configures the vcpu and should be called once per vcpu from the vcpu's thread.
|
||||
|
|
Loading…
Reference in a new issue