Skip to content

Commit 6010bfe

Browse files
committed
Introduce explict APIs for adding custom multiport virtio consoles
Signed-off-by: Matej Hrica <[email protected]>
1 parent e4f70b4 commit 6010bfe

File tree

4 files changed

+270
-12
lines changed

4 files changed

+270
-12
lines changed

include/libkrun.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -985,6 +985,69 @@ int32_t krun_add_serial_console_default(uint32_t ctx_id,
985985
int input_fd,
986986
int output_fd);
987987

988+
/*
989+
* Adds a multi-port virtio-console device to the guest with explicitly configured ports.
990+
*
991+
* This function creates a new virtio-console device that can have multiple ports added to it
992+
* via krun_add_console_port_tty() and krun_add_console_port_inout(). Unlike krun_add_virtio_console_default(),
993+
* this does not do any automatic detections to configure ports based on the file descriptors.
994+
*
995+
* The function can be called multiple times for adding multiple virtio-console devices.
996+
* In the guest, the consoles will appear in the same order as they are added (that is,
997+
* the first added console will be "hvc0", the second "hvc1", ...). However, if the
998+
* implicit console is not disabled via `krun_disable_implicit_console`, the first
999+
* console created with the function will occupy the "hvc1" ID.
1000+
*
1001+
* Arguments:
1002+
* "ctx_id" - the configuration context ID.
1003+
*
1004+
* Returns:
1005+
* The console_id (>= 0) on success or a negative error number on failure.
1006+
*/
1007+
int32_t krun_add_virtio_console_multiport(uint32_t ctx_id);
1008+
1009+
/*
1010+
* Adds a console port to a multi-port virtio-console device using a TTY file descriptor.
1011+
*
1012+
* The TTY file descriptor is used for both input and output. This is typically used for
1013+
* interactive terminal sessions.
1014+
*
1015+
* Arguments:
1016+
* "ctx_id" - the configuration context ID.
1017+
* "console_id" - the console ID returned by krun_add_virtio_console_multiport().
1018+
* "name" - the name of the port. Use empty string "" for the console port (port 0).
1019+
* "tty_fd" - file descriptor for the TTY to use for both input and output.
1020+
*
1021+
* Returns:
1022+
* Zero on success or a negative error number on failure.
1023+
*/
1024+
int32_t krun_add_console_port_tty(uint32_t ctx_id,
1025+
uint32_t console_id,
1026+
const char *name,
1027+
int tty_fd);
1028+
1029+
/*
1030+
* Adds a console port to a multi-port virtio-console device using separate input/output file descriptors.
1031+
*
1032+
* This function allows specifying separate file descriptors for input and output, which is useful
1033+
* for non-interactive scenarios such as piping data in/out of the guest.
1034+
*
1035+
* Arguments:
1036+
* "ctx_id" - the configuration context ID.
1037+
* "console_id" - the console ID returned by krun_add_virtio_console_multiport().
1038+
* "name" - the name of the port. Use empty string "" for the console port (port 0).
1039+
* "input_fd" - file descriptor to use for input.
1040+
* "output_fd" - file descriptor to use for output.
1041+
*
1042+
* Returns:
1043+
* Zero on success or a negative error number on failure.
1044+
*/
1045+
int32_t krun_add_console_port_inout(uint32_t ctx_id,
1046+
uint32_t console_id,
1047+
const char *name,
1048+
int input_fd,
1049+
int output_fd);
1050+
9881051
/**
9891052
* Configure block device to be used as root filesystem.
9901053
*

src/libkrun/src/lib.rs

Lines changed: 113 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ use std::sync::atomic::{AtomicI32, Ordering};
3838
use std::sync::LazyLock;
3939
use std::sync::Mutex;
4040
use utils::eventfd::EventFd;
41-
use vmm::resources::{DefaultVirtioConsoleConfig, SerialConsoleConfig, VmResources};
41+
use vmm::resources::{
42+
DefaultVirtioConsoleConfig, PortConfig, SerialConsoleConfig, VirtioConsoleConfigMode,
43+
VmResources,
44+
};
4245
#[cfg(feature = "blk")]
4346
use vmm::vmm_config::block::{BlockDeviceConfig, BlockRootConfig};
4447
#[cfg(not(feature = "tee"))]
@@ -2118,18 +2121,122 @@ pub unsafe extern "C" fn krun_add_virtio_console_default(
21182121
Entry::Occupied(mut ctx_cfg) => {
21192122
let cfg = ctx_cfg.get_mut();
21202123

2121-
cfg.vmr.virtio_consoles.push(DefaultVirtioConsoleConfig {
2122-
input_fd,
2123-
output_fd,
2124-
err_fd,
2125-
});
2124+
cfg.vmr
2125+
.virtio_consoles
2126+
.push(VirtioConsoleConfigMode::Autoconfigure(
2127+
DefaultVirtioConsoleConfig {
2128+
input_fd,
2129+
output_fd,
2130+
err_fd,
2131+
},
2132+
));
21262133
}
21272134
Entry::Vacant(_) => return -libc::ENOENT,
21282135
}
21292136

21302137
KRUN_SUCCESS
21312138
}
21322139

2140+
#[allow(clippy::missing_safety_doc)]
2141+
#[no_mangle]
2142+
pub unsafe extern "C" fn krun_add_virtio_console_multiport(ctx_id: u32) -> i32 {
2143+
match CTX_MAP.lock().unwrap().entry(ctx_id) {
2144+
Entry::Occupied(mut ctx_cfg) => {
2145+
let cfg = ctx_cfg.get_mut();
2146+
let console_id = cfg.vmr.virtio_consoles.len() as i32;
2147+
2148+
cfg.vmr
2149+
.virtio_consoles
2150+
.push(VirtioConsoleConfigMode::Explicit(Vec::new()));
2151+
2152+
console_id
2153+
}
2154+
Entry::Vacant(_) => -libc::ENOENT,
2155+
}
2156+
}
2157+
2158+
#[allow(clippy::missing_safety_doc)]
2159+
#[no_mangle]
2160+
pub unsafe extern "C" fn krun_add_console_port_tty(
2161+
ctx_id: u32,
2162+
console_id: u32,
2163+
name: *const libc::c_char,
2164+
tty_fd: libc::c_int,
2165+
) -> i32 {
2166+
if name.is_null() {
2167+
return -libc::EINVAL;
2168+
}
2169+
2170+
let name_str = match std::ffi::CStr::from_ptr(name).to_str() {
2171+
Ok(s) => s.to_string(),
2172+
Err(_) => return -libc::EINVAL,
2173+
};
2174+
2175+
match CTX_MAP.lock().unwrap().entry(ctx_id) {
2176+
Entry::Occupied(mut ctx_cfg) => {
2177+
let cfg = ctx_cfg.get_mut();
2178+
2179+
if (console_id as usize) >= cfg.vmr.virtio_consoles.len() {
2180+
return -libc::ENOENT;
2181+
}
2182+
2183+
match &mut cfg.vmr.virtio_consoles[console_id as usize] {
2184+
VirtioConsoleConfigMode::Explicit(ports) => {
2185+
ports.push(PortConfig::Tty {
2186+
name: name_str,
2187+
tty_fd,
2188+
});
2189+
KRUN_SUCCESS
2190+
}
2191+
VirtioConsoleConfigMode::Autoconfigure(_) => -libc::EINVAL,
2192+
}
2193+
}
2194+
Entry::Vacant(_) => -libc::ENOENT,
2195+
}
2196+
}
2197+
2198+
#[allow(clippy::missing_safety_doc)]
2199+
#[no_mangle]
2200+
pub unsafe extern "C" fn krun_add_console_port_inout(
2201+
ctx_id: u32,
2202+
console_id: u32,
2203+
name: *const libc::c_char,
2204+
input_fd: libc::c_int,
2205+
output_fd: libc::c_int,
2206+
) -> i32 {
2207+
if name.is_null() {
2208+
return -libc::EINVAL;
2209+
}
2210+
2211+
let name_str = match std::ffi::CStr::from_ptr(name).to_str() {
2212+
Ok(s) => s.to_string(),
2213+
Err(_) => return -libc::EINVAL,
2214+
};
2215+
2216+
match CTX_MAP.lock().unwrap().entry(ctx_id) {
2217+
Entry::Occupied(mut ctx_cfg) => {
2218+
let cfg = ctx_cfg.get_mut();
2219+
2220+
if (console_id as usize) >= cfg.vmr.virtio_consoles.len() {
2221+
return -libc::ENOENT;
2222+
}
2223+
2224+
match &mut cfg.vmr.virtio_consoles[console_id as usize] {
2225+
VirtioConsoleConfigMode::Explicit(ports) => {
2226+
ports.push(PortConfig::InOut {
2227+
name: name_str,
2228+
input_fd,
2229+
output_fd,
2230+
});
2231+
KRUN_SUCCESS
2232+
}
2233+
VirtioConsoleConfigMode::Autoconfigure(_) => -libc::EINVAL,
2234+
}
2235+
}
2236+
Entry::Vacant(_) => -libc::ENOENT,
2237+
}
2238+
}
2239+
21332240
#[allow(clippy::missing_safety_doc)]
21342241
#[no_mangle]
21352242
pub unsafe extern "C" fn krun_add_serial_console_default(

src/vmm/src/builder.rs

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ use super::{Error, Vmm};
2424
#[cfg(target_arch = "x86_64")]
2525
use crate::device_manager::legacy::PortIODeviceManager;
2626
use crate::device_manager::mmio::MMIODeviceManager;
27-
use crate::resources::{DefaultVirtioConsoleConfig, VmResources};
27+
use crate::resources::{
28+
DefaultVirtioConsoleConfig, PortConfig, VirtioConsoleConfigMode, VmResources,
29+
};
2830
use crate::vmm_config::external_kernel::{ExternalKernel, KernelFormat};
2931
#[cfg(feature = "net")]
3032
use crate::vmm_config::net::NetBuilder;
@@ -937,13 +939,13 @@ pub fn build_microvm(
937939
console_id += 1;
938940
}
939941

940-
for console in vm_resources.virtio_consoles.iter() {
942+
for console_cfg in vm_resources.virtio_consoles.iter() {
941943
attach_console_devices(
942944
&mut vmm,
943945
event_manager,
944946
intc.clone(),
945947
vm_resources,
946-
Some(console),
948+
Some(console_cfg),
947949
console_id,
948950
)?;
949951
console_id += 1;
@@ -1982,19 +1984,88 @@ fn autoconfigure_console_ports(
19821984
}
19831985
}
19841986

1987+
/// Creates explicit ports from configuration
1988+
fn create_explicit_ports(
1989+
port_configs: &[PortConfig],
1990+
) -> std::result::Result<Vec<PortDescription>, StartMicrovmError> {
1991+
let mut ports = Vec::with_capacity(port_configs.len());
1992+
1993+
for port_cfg in port_configs {
1994+
let port_desc = match port_cfg {
1995+
PortConfig::Tty { name, tty_fd } => {
1996+
let input = port_io::input_to_raw_fd_dup(*tty_fd).unwrap();
1997+
let output = port_io::output_to_raw_fd_dup(*tty_fd).unwrap();
1998+
let terminal = {
1999+
let term_fd = unsafe { BorrowedFd::borrow_raw(*tty_fd) };
2000+
if isatty(term_fd).unwrap_or(false) {
2001+
Some(port_io::term_fd(*tty_fd).unwrap()
2002+
as Box<dyn port_io::PortTerminalProperties>)
2003+
} else {
2004+
None
2005+
}
2006+
};
2007+
2008+
PortDescription {
2009+
name: name.clone().into(),
2010+
input: Some(input),
2011+
output: Some(output),
2012+
terminal,
2013+
}
2014+
}
2015+
PortConfig::InOut {
2016+
name,
2017+
input_fd,
2018+
output_fd,
2019+
} => {
2020+
let input = port_io::input_to_raw_fd_dup(*input_fd).unwrap();
2021+
let output = port_io::output_to_raw_fd_dup(*output_fd).unwrap();
2022+
2023+
PortDescription {
2024+
name: name.clone().into(),
2025+
input: Some(input),
2026+
output: Some(output),
2027+
terminal: None,
2028+
}
2029+
}
2030+
};
2031+
2032+
ports.push(port_desc);
2033+
}
2034+
2035+
Ok(ports)
2036+
}
2037+
19852038
fn attach_console_devices(
19862039
vmm: &mut Vmm,
19872040
event_manager: &mut EventManager,
19882041
intc: IrqChip,
19892042
vm_resources: &VmResources,
1990-
cfg: Option<&DefaultVirtioConsoleConfig>,
2043+
cfg: Option<&VirtioConsoleConfigMode>,
19912044
id_number: u32,
19922045
) -> std::result::Result<(), StartMicrovmError> {
19932046
use self::StartMicrovmError::*;
19942047

19952048
let creating_implicit_console = cfg.is_none();
19962049

1997-
let ports = autoconfigure_console_ports(vmm, vm_resources, cfg, creating_implicit_console)?;
2050+
let ports = match cfg {
2051+
None => {
2052+
// Implicit console - autoconfigure
2053+
autoconfigure_console_ports(vmm, vm_resources, None, creating_implicit_console)?
2054+
}
2055+
Some(VirtioConsoleConfigMode::Autoconfigure(autocfg)) => {
2056+
// Explicit autoconfigure mode
2057+
autoconfigure_console_ports(
2058+
vmm,
2059+
vm_resources,
2060+
Some(autocfg),
2061+
creating_implicit_console,
2062+
)?
2063+
}
2064+
Some(VirtioConsoleConfigMode::Explicit(ports)) => {
2065+
// Explicit ports mode - convert ExplicitPortConfig to PortDescription
2066+
create_explicit_ports(ports)?
2067+
}
2068+
};
19982069

19992070
let console = Arc::new(Mutex::new(devices::virtio::Console::new(ports).unwrap()));
20002071

src/vmm/src/resources.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,23 @@ pub struct DefaultVirtioConsoleConfig {
9292
pub err_fd: RawFd,
9393
}
9494

95+
pub enum VirtioConsoleConfigMode {
96+
Autoconfigure(DefaultVirtioConsoleConfig),
97+
Explicit(Vec<PortConfig>),
98+
}
99+
100+
pub enum PortConfig {
101+
Tty {
102+
name: String,
103+
tty_fd: RawFd,
104+
},
105+
InOut {
106+
name: String,
107+
input_fd: RawFd,
108+
output_fd: RawFd,
109+
},
110+
}
111+
95112
/// A data structure that encapsulates the device configurations
96113
/// held in the Vmm.
97114
#[derive(Default)]
@@ -151,7 +168,7 @@ pub struct VmResources {
151168
/// Serial consoles to attach to the guest
152169
pub serial_consoles: Vec<SerialConsoleConfig>,
153170
/// Virtio consoles to attach to the guest
154-
pub virtio_consoles: Vec<DefaultVirtioConsoleConfig>,
171+
pub virtio_consoles: Vec<VirtioConsoleConfigMode>,
155172
}
156173

157174
impl VmResources {

0 commit comments

Comments
 (0)