Skip to content

Commit 5bedcf3

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

File tree

7 files changed

+303
-45
lines changed

7 files changed

+303
-45
lines changed

include/libkrun.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -985,6 +985,78 @@ 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+
* Each device appears in the guest with port 0 accessible as /dev/hvcN (hvc0, hvc1, etc.) in the order
997+
* devices are added. If the implicit console is not disabled via `krun_disable_implicit_console`,
998+
* the first explicitly added device will occupy the "hvc1" ID. Additional ports within each device
999+
* (port 1, 2, ...) appear as /dev/vportNpM character devices.
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 TTY port to a multi-port virtio-console device.
1011+
*
1012+
* The TTY file descriptor is used for both input and output. This port will be marked with the
1013+
* VIRTIO_CONSOLE_CONSOLE_PORT flag, enabling console-specific features like window resize signals.
1014+
* In the guest, port 0 of each device appears as /dev/hvcN, while subsequent ports appear as
1015+
* /dev/vportNpM character devices (regardless of whether they are TTY or generic I/O ports).
1016+
*
1017+
* This port type supports terminal features including window size detection and resize signals,
1018+
* making it suitable for interactive terminal sessions.
1019+
*
1020+
* Arguments:
1021+
* "ctx_id" - the configuration context ID
1022+
* "console_id" - the console ID returned by krun_add_virtio_console_multiport()
1023+
* "name" - the name of the port for identifying the port in the guest, can be empty ("")
1024+
* "tty_fd" - file descriptor for the TTY to use for both input, output, and determining terminal size
1025+
*
1026+
* Returns:
1027+
* Zero on success or a negative error number on failure.
1028+
*/
1029+
int32_t krun_add_console_port_tty(uint32_t ctx_id,
1030+
uint32_t console_id,
1031+
const char *name,
1032+
int tty_fd);
1033+
1034+
/*
1035+
* Adds a generic I/O port to a multi-port virtio-console device, suitable for arbitrary bidirectional
1036+
* data streams that don't require terminal functionality.
1037+
*
1038+
* This port will NOT be marked with the VIRTIO_CONSOLE_CONSOLE_PORT flag, meaning it won't support
1039+
* console-specific features like window resize signals. Like all ports, if this is port 0 of
1040+
* the device, it will appear as /dev/hvcN in the guest; otherwise it only appears as /dev/vportNpM
1041+
* (also accessible via /sys/class/virtio-ports/).
1042+
*
1043+
*
1044+
* Arguments:
1045+
* "ctx_id" - the configuration context ID
1046+
* "console_id" - the console ID returned by krun_add_virtio_console_multiport()
1047+
* "name" - the name of the port for identifying the port in the guest, can be empty ("")
1048+
* "input_fd" - file descriptor to use for input (host writes, guest reads)
1049+
* "output_fd" - file descriptor to use for output (guest writes, host reads)
1050+
*
1051+
* Returns:
1052+
* Zero on success or a negative error number on failure.
1053+
*/
1054+
int32_t krun_add_console_port_inout(uint32_t ctx_id,
1055+
uint32_t console_id,
1056+
const char *name,
1057+
int input_fd,
1058+
int output_fd);
1059+
9881060
/**
9891061
* Configure block device to be used as root filesystem.
9901062
*

src/devices/src/virtio/console/device.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,10 @@ impl Console {
116116
self.sigwinch_evt.as_raw_fd()
117117
}
118118

119-
pub fn update_console_size(&mut self, cols: u16, rows: u16) {
120-
log::debug!("update_console_size: {cols} {rows}");
121-
// Note that we currently only support resizing on the first/main console
119+
pub fn update_console_size(&mut self, port_id: u32, cols: u16, rows: u16) {
120+
log::debug!("update_console_size {port_id}: {cols} {rows}");
122121
self.control
123-
.console_resize(0, VirtioConsoleResize { rows, cols });
122+
.console_resize(port_id, VirtioConsoleResize { rows, cols });
124123
}
125124

126125
pub(crate) fn process_control_rx(&mut self) -> bool {

src/devices/src/virtio/console/event_handler.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,11 @@ impl Console {
8888
error!("Failed to read the sigwinch event: {e:?}");
8989
}
9090

91-
if let Some(term) = self.ports[0].terminal() {
92-
let (cols, rows) = term.get_win_size();
93-
self.update_console_size(cols, rows);
91+
for i in 0..self.ports.len() {
92+
if let Some(term) = self.ports[i].terminal() {
93+
let (cols, rows) = term.get_win_size();
94+
self.update_console_size(i as u32, cols, rows);
95+
}
9496
}
9597
}
9698

src/libkrun/src/lib.rs

Lines changed: 118 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@ use devices::virtio::CacheType;
1313
use env_logger::{Env, Target};
1414
#[cfg(feature = "gpu")]
1515
use krun_display::DisplayBackend;
16-
use libc::c_char;
17-
#[cfg(feature = "net")]
18-
use libc::c_int;
19-
use libc::size_t;
16+
use libc::{c_char, c_int, size_t};
2017
use once_cell::sync::Lazy;
2118
use polly::event_manager::EventManager;
2219
#[cfg(all(feature = "blk", not(feature = "tee")))]
@@ -29,16 +26,20 @@ use std::env;
2926
use std::ffi::CString;
3027
use std::ffi::{c_void, CStr};
3128
use std::fs::File;
29+
use std::io::IsTerminal;
3230
#[cfg(target_os = "linux")]
3331
use std::os::fd::AsRawFd;
34-
use std::os::fd::{FromRawFd, RawFd};
32+
use std::os::fd::{BorrowedFd, FromRawFd, RawFd};
3533
use std::path::PathBuf;
3634
use std::slice;
3735
use std::sync::atomic::{AtomicI32, Ordering};
3836
use std::sync::LazyLock;
3937
use std::sync::Mutex;
4038
use utils::eventfd::EventFd;
41-
use vmm::resources::{DefaultVirtioConsoleConfig, SerialConsoleConfig, VmResources};
39+
use vmm::resources::{
40+
DefaultVirtioConsoleConfig, PortConfig, SerialConsoleConfig, VirtioConsoleConfigMode,
41+
VmResources,
42+
};
4243
#[cfg(feature = "blk")]
4344
use vmm::vmm_config::block::{BlockDeviceConfig, BlockRootConfig};
4445
#[cfg(not(feature = "tee"))]
@@ -2118,24 +2119,128 @@ pub unsafe extern "C" fn krun_add_virtio_console_default(
21182119
Entry::Occupied(mut ctx_cfg) => {
21192120
let cfg = ctx_cfg.get_mut();
21202121

2121-
cfg.vmr.virtio_consoles.push(DefaultVirtioConsoleConfig {
2122-
input_fd,
2123-
output_fd,
2124-
err_fd,
2125-
});
2122+
cfg.vmr
2123+
.virtio_consoles
2124+
.push(VirtioConsoleConfigMode::Autoconfigure(
2125+
DefaultVirtioConsoleConfig {
2126+
input_fd,
2127+
output_fd,
2128+
err_fd,
2129+
},
2130+
));
21262131
}
21272132
Entry::Vacant(_) => return -libc::ENOENT,
21282133
}
21292134

21302135
KRUN_SUCCESS
21312136
}
21322137

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

0 commit comments

Comments
 (0)