Skip to content
Open
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

87 changes: 83 additions & 4 deletions init/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <string.h>
#include <time.h>
#include <dirent.h>
#include <termios.h>

#include <net/if.h>
#include <sys/ioctl.h>
Expand Down Expand Up @@ -756,6 +757,71 @@ void clock_worker()
}
#endif

int reopen_fd(int fd, char *path, int flags)
{
int newfd = open(path,flags);
if (newfd < 0) {
printf("Failed to open '%s': %s\n", path,strerror(errno));
return -1;
}

close(fd);
if (dup2(newfd, fd) < 0) {
perror("dup2");
close(newfd);
return -1;
}
close(newfd);
return 0;
}

int setup_redirects()
{
DIR *ports_dir = opendir("/sys/class/virtio-ports");
if (ports_dir == NULL) {
printf("Unable to open ports directory!\n");
return -4;
}

char path[2048];
char name_buf[1024];

struct dirent *entry = NULL;
while ((entry=readdir(ports_dir))) {
char* port_identifier = entry->d_name;
int result_len = snprintf(path, sizeof(path), "/sys/class/virtio-ports/%s/name", port_identifier);

// result was truncated
if (result_len > sizeof(name_buf) - 1) {
printf("Path buffer too small");
return -1;
}

FILE *port_name_file = fopen(path, "r");
if (port_name_file == NULL) {
continue;
}

char *port_name = fgets(name_buf, sizeof(name_buf), port_name_file);
fclose(port_name_file);

if (port_name != NULL && strcmp(port_name, "krun-stdin\n") == 0) {
// if previous snprintf didn't fail, this one cannot fail either
snprintf(path, sizeof(path), "/dev/%s", port_identifier);
reopen_fd(STDIN_FILENO, path, O_RDONLY);
} else if (port_name != NULL && strcmp(port_name, "krun-stdout\n") == 0) {
snprintf(path, sizeof(path), "/dev/%s", port_identifier);
reopen_fd(STDOUT_FILENO, path, O_WRONLY);
} else if (port_name != NULL && strcmp(port_name, "krun-stderr\n") == 0) {
snprintf(path, sizeof(path), "/dev/%s", port_identifier);
reopen_fd(STDERR_FILENO, path, O_WRONLY);
}
}

closedir(ports_dir);
return 0;
}

int main(int argc, char **argv)
{
struct ifreq ifr;
Expand Down Expand Up @@ -848,10 +914,23 @@ int main(int argc, char **argv)
}
#endif

if (execvp(exec_argv[0], exec_argv) < 0) {
printf("Couldn't execute '%s' inside the vm: %s\n", exec_argv[0], strerror(errno));
exit(-3);
}
// We need to fork ourselves, because pid 1 cannot doesn't receive SIGINT signal
int pid = fork();
if (pid < 0) {
perror("fork");
exit(-3);
} if (pid == 0) { // child
if (setup_redirects() < 0) {
exit(-4);
}
if (execvp(exec_argv[0], exec_argv) < 0) {
printf("Couldn't execute '%s' inside the vm: %s\n", exec_argv[0], strerror(errno));
exit(-3);
}
} else { // parent
// wait for children since we can't exit init
waitpid(pid, NULL, 0);
}

return 0;
}
10 changes: 5 additions & 5 deletions src/arch/src/x86_64/regs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ mod tests {
}

fn read_u64(gm: &GuestMemoryMmap, offset: u64) -> u64 {
let read_addr = GuestAddress(offset as u64);
let read_addr = GuestAddress(offset);
gm.read_obj(read_addr).unwrap()
}

Expand Down Expand Up @@ -253,7 +253,7 @@ mod tests {
assert_eq!((i << 21) + 0x83u64, read_u64(gm, PDE_START + (i * 8)));
}

assert_eq!(PML4_START as u64, sregs.cr3);
assert_eq!({ PML4_START }, sregs.cr3);
assert!(sregs.cr4 & X86_CR4_PAE != 0);
assert!(sregs.cr0 & X86_CR0_PG != 0);
}
Expand Down Expand Up @@ -298,9 +298,9 @@ mod tests {
let expected_regs: kvm_regs = kvm_regs {
rflags: 0x0000_0000_0000_0002u64,
rip: 1,
rsp: super::super::layout::BOOT_STACK_POINTER as u64,
rbp: super::super::layout::BOOT_STACK_POINTER as u64,
rsi: super::super::layout::ZERO_PAGE_START as u64,
rsp: super::super::layout::BOOT_STACK_POINTER,
rbp: super::super::layout::BOOT_STACK_POINTER,
rsi: super::super::layout::ZERO_PAGE_START,
..Default::default()
};

Expand Down
2 changes: 2 additions & 0 deletions src/cpuid/src/cpu_leaf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ pub mod leaf_0x4 {
use crate::bit_helper::BitRange;

// inherit eax from leaf_cache_parameters
#[allow(unused_imports)]
pub use crate::cpu_leaf::leaf_cache_parameters::eax::*;

pub const MAX_CORES_PER_PACKAGE_BITRANGE: BitRange = bit_range!(31, 26);
Expand Down Expand Up @@ -300,6 +301,7 @@ pub mod leaf_0x8000001d {
pub const LEAF_NUM: u32 = 0x8000_001d;

// inherit eax from leaf_cache_parameters
#[allow(unused_imports)]
pub use crate::cpu_leaf::leaf_cache_parameters::eax;
}

Expand Down
2 changes: 1 addition & 1 deletion src/devices/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ crossbeam-channel = "0.5"
env_logger = "0.9.0"
libc = ">=0.2.39"
log = "0.4.0"
nix = "0.24.1"
nix = { version = "0.24.1", features = ["poll"] }
rand = "0.8.5"
vm-memory = { version = ">=0.13", features = ["backend-mmap"] }

Expand Down
10 changes: 5 additions & 5 deletions src/devices/src/legacy/serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -524,13 +524,13 @@ mod tests {
fn test_serial_dlab() {
let mut serial = Serial::new_sink(EventFd::new(utils::eventfd::EFD_NONBLOCK).unwrap());

serial.write(0, u64::from(LCR), &[LCR_DLAB_BIT as u8]);
serial.write(0, u64::from(LCR), &[LCR_DLAB_BIT]);
serial.write(0, u64::from(DLAB_LOW), &[0x12_u8]);
serial.write(0, u64::from(DLAB_HIGH), &[0x34_u8]);

let mut data = [0u8];
serial.read(0, u64::from(LCR), &mut data[..]);
assert_eq!(data[0], LCR_DLAB_BIT as u8);
assert_eq!(data[0], { LCR_DLAB_BIT });
serial.read(0, u64::from(DLAB_LOW), &mut data[..]);
assert_eq!(data[0], 0x12);
serial.read(0, u64::from(DLAB_HIGH), &mut data[..]);
Expand All @@ -541,16 +541,16 @@ mod tests {
fn test_serial_modem() {
let mut serial = Serial::new_sink(EventFd::new(utils::eventfd::EFD_NONBLOCK).unwrap());

serial.write(0, u64::from(MCR), &[MCR_LOOP_BIT as u8]);
serial.write(0, u64::from(MCR), &[MCR_LOOP_BIT]);
serial.write(0, u64::from(DATA), &[b'a']);
serial.write(0, u64::from(DATA), &[b'b']);
serial.write(0, u64::from(DATA), &[b'c']);

let mut data = [0u8];
serial.read(0, u64::from(MSR), &mut data[..]);
assert_eq!(data[0], DEFAULT_MODEM_STATUS as u8);
assert_eq!(data[0], { DEFAULT_MODEM_STATUS });
serial.read(0, u64::from(MCR), &mut data[..]);
assert_eq!(data[0], MCR_LOOP_BIT as u8);
assert_eq!(data[0], { MCR_LOOP_BIT });
serial.read(0, u64::from(DATA), &mut data[..]);
assert_eq!(data[0], b'a');
serial.read(0, u64::from(DATA), &mut data[..]);
Expand Down
1 change: 0 additions & 1 deletion src/devices/src/virtio/block/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ pub mod request;
pub mod test_utils;

pub use self::device::{Block, CacheType};
pub use self::event_handler::*;
pub use self::request::*;

use vm_memory::GuestMemoryError;
Expand Down
152 changes: 152 additions & 0 deletions src/devices/src/virtio/console/console_control.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
use std::collections::VecDeque;
use std::ops::Deref;
use std::sync::{Arc, Mutex};

use utils::eventfd::EventFd;
use utils::eventfd::EFD_NONBLOCK;
use vm_memory::{ByteValued, GuestMemoryMmap};

use crate::virtio::console::defs::control_event::{
VIRTIO_CONSOLE_CONSOLE_PORT, VIRTIO_CONSOLE_PORT_ADD, VIRTIO_CONSOLE_PORT_NAME,
VIRTIO_CONSOLE_PORT_OPEN, VIRTIO_CONSOLE_RESIZE,
};

#[derive(Copy, Clone, Debug, Default)]
#[repr(C, packed(4))]
pub struct VirtioConsoleControl {
/// Port number
pub id: u32,
/// The kind of control event
pub event: u16,
/// Extra information for the event
pub value: u16,
}

// Safe because it only has data and has no implicit padding.
// But NOTE that this relies on CPU being little endian, to have correct semantics
unsafe impl ByteValued for VirtioConsoleControl {}

#[derive(Copy, Clone, Debug, Default)]
#[repr(C, packed)]
pub struct VirtioConsoleResize {
// NOTE: the order of these fields in the actual kernel implementation and in the spec are swapped,
// we follow the order in the kernel to get it working correctly
pub rows: u16,
pub cols: u16,
}

// Safe because it only has data and has no implicit padding.
// but NOTE, that we rely on CPU being little endian, for the values to be correct
unsafe impl ByteValued for VirtioConsoleResize {}

pub enum Payload {
ConsoleControl(VirtioConsoleControl),
Bytes(Vec<u8>),
}

impl Deref for Payload {
type Target = [u8];

fn deref(&self) -> &Self::Target {
match self {
Payload::ConsoleControl(b) => b.as_slice(),
Payload::Bytes(b) => b.as_slice(),
}
}
}

// Utility for sending commands into control rx queue
pub struct ConsoleControl {
queue: Mutex<VecDeque<Payload>>,
queue_evt: EventFd,
}

impl ConsoleControl {
pub fn new() -> Arc<Self> {
Arc::new(Self {
queue: Default::default(),
queue_evt: EventFd::new(EFD_NONBLOCK).unwrap(),
})
}

pub fn mark_console_port(&self, _mem: &GuestMemoryMmap, port_id: u32) {
self.push_msg(VirtioConsoleControl {
id: port_id,
event: VIRTIO_CONSOLE_CONSOLE_PORT,
value: 1,
})
}

pub fn console_resize(&self, port_id: u32, new_size: VirtioConsoleResize) {
let mut buf = Vec::new();
buf.extend(
VirtioConsoleControl {
id: port_id,
event: VIRTIO_CONSOLE_RESIZE,
value: 0,
}
.as_slice(),
);
buf.extend(new_size.as_slice());
self.push_vec(buf)
}

/// Adds another port with the specified port_id
pub fn port_add(&self, port_id: u32) {
self.push_msg(VirtioConsoleControl {
id: port_id,
event: VIRTIO_CONSOLE_PORT_ADD,
value: 0,
})
}

pub fn port_open(&self, port_id: u32, open: bool) {
self.push_msg(VirtioConsoleControl {
id: port_id,
event: VIRTIO_CONSOLE_PORT_OPEN,
value: open as u16,
})
}

pub fn port_name(&self, port_id: u32, name: &str) {
let mut buf: Vec<u8> = Vec::new();

buf.extend_from_slice(
VirtioConsoleControl {
id: port_id,
event: VIRTIO_CONSOLE_PORT_NAME,
value: 1, // Unspecified/unused in the spec, lets use the same value as QEMU.
}
.as_slice(),
);

// The spec says the name shouldn't be NUL terminated.
buf.extend(name.as_bytes());
self.push_vec(buf)
}

pub fn queue_pop(&self) -> Option<Payload> {
let mut queue = self.queue.lock().expect("Poisoned lock");
queue.pop_front()
}

pub fn queue_evt(&self) -> &EventFd {
&self.queue_evt
}

fn push_msg(&self, msg: VirtioConsoleControl) {
let mut queue = self.queue.lock().expect("Poisoned lock");
queue.push_back(Payload::ConsoleControl(msg));
if let Err(e) = self.queue_evt.write(1) {
log::trace!("ConsoleControl failed to write to notify {e}")
}
}

fn push_vec(&self, buf: Vec<u8>) {
let mut queue = self.queue.lock().expect("Poisoned lock");
queue.push_back(Payload::Bytes(buf));
if let Err(e) = self.queue_evt.write(1) {
log::trace!("ConsoleControl failed to write to notify {e}")
}
}
}
Loading