Skip to content

Commit b399309

Browse files
committed
virtio-console: Implement redirection for stdin/stdout/stderr
TODO: implement stderr here too! This is done by creating a port for each of stdin/stdout/stderr, Each of these ports is multidirectional but we only use one direction. Port 0 must always be a console - this is implied by the specification. Signed-off-by: Matej Hrica <[email protected]>
1 parent 4373a0c commit b399309

File tree

6 files changed

+149
-8
lines changed

6 files changed

+149
-8
lines changed

init/init.c

+66
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,68 @@ void clock_worker()
756756
}
757757
#endif
758758

759+
int reopen_fd(int fd, char *path, int flags)
760+
{
761+
int newfd = open(path,flags);
762+
if (newfd < 0) {
763+
printf("Failed to open '%s': %s\n", path,strerror(errno));
764+
return -1;
765+
}
766+
767+
close(fd);
768+
if (dup2(newfd, fd) < 0) {
769+
perror("dup2");
770+
close(newfd);
771+
return -1;
772+
}
773+
close(newfd);
774+
return 0;
775+
}
776+
777+
int setup_redirects()
778+
{
779+
DIR *ports_dir = opendir("/sys/class/virtio-ports");
780+
if (ports_dir == NULL) {
781+
printf("Unable to open ports directory!\n");
782+
return -4;
783+
}
784+
785+
char path[2048];
786+
char name_buf[1024];
787+
788+
struct dirent *entry = NULL;
789+
while ((entry=readdir(ports_dir))) {
790+
char* port_identifier = entry->d_name;
791+
int result_len = snprintf(path, sizeof(path), "/sys/class/virtio-ports/%s/name", port_identifier);
792+
793+
// result was truncated
794+
if (result_len > sizeof(name_buf) - 1) {
795+
printf("Path buffer too small");
796+
return -1;
797+
}
798+
799+
FILE *port_name_file = fopen(path, "r");
800+
if (port_name_file == NULL) {
801+
continue;
802+
}
803+
804+
char *port_name = fgets(name_buf, sizeof(name_buf), port_name_file);
805+
fclose(port_name_file);
806+
807+
if (port_name != NULL && strcmp(port_name, "krun-stdin\n") == 0) {
808+
// if previous snprintf didn't fail, this one cannot fail either
809+
snprintf(path, sizeof(path), "/dev/%s", port_identifier);
810+
reopen_fd(STDIN_FILENO, path, O_RDONLY);
811+
}else if (port_name != NULL && strcmp(port_name, "krun-stdout\n") == 0) {
812+
snprintf(path, sizeof(path), "/dev/%s", port_identifier);
813+
reopen_fd(STDOUT_FILENO, path, O_WRONLY);
814+
}
815+
}
816+
817+
closedir(ports_dir);
818+
return 0;
819+
}
820+
759821
int main(int argc, char **argv)
760822
{
761823
struct ifreq ifr;
@@ -848,6 +910,10 @@ int main(int argc, char **argv)
848910
}
849911
#endif
850912

913+
if (setup_redirects() < 0) {
914+
exit(-4);
915+
}
916+
851917
if (execvp(exec_argv[0], exec_argv) < 0) {
852918
printf("Couldn't execute '%s' inside the vm: %s\n", exec_argv[0], strerror(errno));
853919
exit(-3);

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::cmp;
22
use std::io::Write;
3+
use std::iter::zip;
34
use std::mem::{size_of, size_of_val};
45
use std::os::unix::io::{AsRawFd, RawFd};
56
use std::sync::atomic::AtomicUsize;
@@ -206,7 +207,9 @@ impl Console {
206207
"Device is ready: initialization {}",
207208
if cmd.value == 1 { "ok" } else { "failed" }
208209
);
209-
self.control.port_add(0);
210+
for port_id in 0..self.ports.len() {
211+
self.control.port_add(port_id as u32);
212+
}
210213
}
211214
control_event::VIRTIO_CONSOLE_PORT_READY => {
212215
if cmd.value != 1 {

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

+2-3
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ use std::os::unix::io::AsRawFd;
33
use polly::event_manager::{EventManager, Subscriber};
44
use utils::epoll::{EpollEvent, EventSet};
55

6-
use super::device::{get_win_size, Console, RXQ_INDEX, TXQ_INDEX};
6+
use super::device::{get_win_size, Console};
77
use crate::virtio::console::device::{CONTROL_RXQ_INDEX, CONTROL_TXQ_INDEX};
8+
use crate::virtio::console::port_queue_mapping::{queue_idx_to_port_id, QueueDirection};
89
use crate::virtio::device::VirtioDevice;
910

1011
impl Console {
@@ -92,8 +93,6 @@ impl Console {
9293
impl Subscriber for Console {
9394
fn process(&mut self, event: &EpollEvent, event_manager: &mut EventManager) {
9495
let source = event.fd();
95-
let rxq = self.queue_events[RXQ_INDEX].as_raw_fd();
96-
let txq = self.queue_events[TXQ_INDEX].as_raw_fd();
9796

9897
let control_rxq = self.queue_events[CONTROL_RXQ_INDEX].as_raw_fd();
9998
let control_txq = self.queue_events[CONTROL_TXQ_INDEX].as_raw_fd();

src/devices/src/virtio/console/mod.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@ pub use self::port::PortDescription;
1313

1414
mod defs {
1515
pub const CONSOLE_DEV_ID: &str = "virtio_console";
16-
pub const NUM_PORTS: usize = 1;
17-
// 2 control queues and then an rx and tx queue for each port
18-
pub const NUM_QUEUES: usize = 2 + NUM_PORTS * 2;
19-
pub const QUEUE_SIZES: &[u16] = &[256; NUM_QUEUES];
16+
pub const QUEUE_SIZE: u16 = 32;
2017

2118
pub mod uapi {
2219
/// The device conforms to the virtio spec version 1.0.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#[derive(Debug, Eq, PartialEq)]
2+
pub(crate) enum QueueDirection {
3+
Rx,
4+
Tx,
5+
}
6+
7+
#[must_use]
8+
pub(crate) fn port_id_to_queue_idx(queue_direction: QueueDirection, port_id: usize) -> usize {
9+
match queue_direction {
10+
QueueDirection::Rx if port_id == 0 => 0,
11+
QueueDirection::Rx => 2 + 2 * port_id,
12+
QueueDirection::Tx if port_id == 0 => 1,
13+
QueueDirection::Tx => 2 + 2 * port_id + 1,
14+
}
15+
}
16+
17+
#[must_use]
18+
pub(crate) fn queue_idx_to_port_id(queue_index: usize) -> (QueueDirection, usize) {
19+
let port_id = match queue_index {
20+
0 | 1 => 0,
21+
2 | 3 => {
22+
panic!("Invalid argument: {queue_index} is not a valid receiveq nor transmitq index!")
23+
}
24+
_ => queue_index / 2 - 1,
25+
};
26+
27+
let direction = if queue_index % 2 == 0 {
28+
QueueDirection::Rx
29+
} else {
30+
QueueDirection::Tx
31+
};
32+
33+
(direction, port_id)
34+
}
35+
36+
pub(crate) fn num_queues(num_ports: usize) -> usize {
37+
// 2 control queues and then an rx and tx queue for each port
38+
2 + 2 * num_ports
39+
}
40+
41+
#[cfg(test)]
42+
mod test {
43+
use super::*;
44+
45+
#[test]
46+
fn test_port_id_to_queue_idx() {
47+
assert_eq!(port_id_to_queue_idx(QueueDirection::Rx, 0), 0);
48+
assert_eq!(port_id_to_queue_idx(QueueDirection::Tx, 0), 1);
49+
assert_eq!(port_id_to_queue_idx(QueueDirection::Rx, 1), 4);
50+
assert_eq!(port_id_to_queue_idx(QueueDirection::Tx, 1), 5);
51+
}
52+
53+
#[test]
54+
fn test_queue_idx_to_port_id_ok() {
55+
assert_eq!(queue_idx_to_port_id(0), (QueueDirection::Rx, 0));
56+
assert_eq!(queue_idx_to_port_id(1), (QueueDirection::Tx, 0));
57+
assert_eq!(queue_idx_to_port_id(4), (QueueDirection::Rx, 1));
58+
assert_eq!(queue_idx_to_port_id(5), (QueueDirection::Tx, 1));
59+
assert_eq!(queue_idx_to_port_id(6), (QueueDirection::Rx, 2));
60+
assert_eq!(queue_idx_to_port_id(7), (QueueDirection::Tx, 2));
61+
}
62+
63+
#[test]
64+
#[should_panic]
65+
fn test_queue_idx_to_port_id_panic_rx_control() {
66+
let _ = queue_idx_to_port_id(2);
67+
}
68+
69+
#[test]
70+
#[should_panic]
71+
fn test_queue_idx_to_port_id_panic_tx_control() {
72+
let _ = queue_idx_to_port_id(3);
73+
}
74+
}

src/vmm/src/builder.rs

+2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ use arch::ArchMemoryInfo;
4848
use arch::InitrdConfig;
4949
#[cfg(feature = "tee")]
5050
use kvm_bindings::KVM_MAX_CPUID_ENTRIES;
51+
use libc::{STDIN_FILENO, STDOUT_FILENO};
52+
use nix::unistd::isatty;
5153
use polly::event_manager::{Error as EventManagerError, EventManager};
5254
use utils::eventfd::EventFd;
5355
use utils::time::TimestampUs;

0 commit comments

Comments
 (0)