Skip to content

Commit

Permalink
Merge pull request #1468 from hermit-os/serial_buffer_hypercall
Browse files Browse the repository at this point in the history
Uhyve: Speed up serial output
  • Loading branch information
mkroening authored Jan 14, 2025
2 parents 12b04c9 + 6004cfb commit 27b3706
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 47 deletions.
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.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ fdt = { version = "0.1", features = ["pretty-printing"] }
free-list = "0.3"
fuse-abi = { version = "0.2", features = ["linux", "zerocopy"], optional = true }
hashbrown = { version = "0.15", default-features = false }
heapless = "0.8"
hermit-entry = { version = "0.10", features = ["kernel"] }
hermit-sync = "0.1"
lock_api = "0.4"
Expand Down
4 changes: 1 addition & 3 deletions src/arch/aarch64/kernel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@ impl Console {
}

pub fn write(&mut self, buf: &[u8]) {
for byte in buf {
self.serial_port.write_byte(*byte);
}
self.serial_port.write_buf(buf);
}

pub fn read(&mut self) -> Option<u8> {
Expand Down
69 changes: 46 additions & 23 deletions src/arch/aarch64/kernel/serial.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,59 @@
use core::arch::asm;

use crate::syscalls::interfaces::serial_buf_hypercall;

enum SerialInner {
Uart(u32),
Uhyve,
}

pub struct SerialPort {
pub port_address: u32,
inner: SerialInner,
}

impl SerialPort {
pub const fn new(port_address: u32) -> Self {
Self { port_address }
pub fn new(port_address: u32) -> Self {
if crate::env::is_uhyve() {
Self {
inner: SerialInner::Uhyve,
}
} else {
Self {
inner: SerialInner::Uart(port_address),
}
}
}

pub fn write_byte(&self, byte: u8) {
let port = core::ptr::with_exposed_provenance_mut::<u8>(self.port_address as usize);

// LF newline characters need to be extended to CRLF over a real serial port.
if byte == b'\n' {
unsafe {
asm!(
"strb w8, [{port}]",
port = in(reg) port,
in("x8") b'\r',
options(nostack),
);
pub fn write_buf(&mut self, buf: &[u8]) {
match &mut self.inner {
SerialInner::Uhyve => {
serial_buf_hypercall(buf);
}
}
SerialInner::Uart(port_address) => {
let port = core::ptr::with_exposed_provenance_mut::<u8>(*port_address as usize);
for &byte in buf {
// LF newline characters need to be extended to CRLF over a real serial port.
if byte == b'\n' {
unsafe {
asm!(
"strb w8, [{port}]",
port = in(reg) port,
in("x8") b'\r',
options(nostack),
);
}
}

unsafe {
asm!(
"strb w8, [{port}]",
port = in(reg) port,
in("x8") byte,
options(nostack),
);
unsafe {
asm!(
"strb w8, [{port}]",
port = in(reg) port,
in("x8") byte,
options(nostack),
);
}
}
}
}
}

Expand Down
18 changes: 5 additions & 13 deletions src/arch/x86_64/kernel/serial.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
use alloc::collections::VecDeque;
use core::task::Waker;

use x86_64::instructions::port::Port;

use crate::arch::x86_64::kernel::apic;
use crate::arch::x86_64::kernel::core_local::increment_irq_counter;
use crate::arch::x86_64::kernel::interrupts::{self, IDT};
use crate::executor::WakerRegistration;
use crate::syscalls::interfaces::serial_buf_hypercall;

const SERIAL_IRQ: u8 = 36;

enum SerialInner {
Uart(uart_16550::SerialPort),
Uhyve(Port<u8>),
Uhyve,
}

pub struct SerialPort {
Expand All @@ -24,9 +23,8 @@ pub struct SerialPort {
impl SerialPort {
pub unsafe fn new(base: u16) -> Self {
if crate::env::is_uhyve() {
let serial = Port::new(base);
Self {
inner: SerialInner::Uhyve(serial),
inner: SerialInner::Uhyve,
buffer: VecDeque::new(),
waker: WakerRegistration::new(),
}
Expand Down Expand Up @@ -67,13 +65,7 @@ impl SerialPort {

pub fn send(&mut self, buf: &[u8]) {
match &mut self.inner {
SerialInner::Uhyve(s) => {
for &data in buf {
unsafe {
s.write(data);
}
}
}
SerialInner::Uhyve => serial_buf_hypercall(buf),
SerialInner::Uart(s) => {
for &data in buf {
s.send(data);
Expand All @@ -84,7 +76,7 @@ impl SerialPort {
}

extern "x86-interrupt" fn serial_interrupt(_stack_frame: crate::interrupts::ExceptionStackFrame) {
crate::console::CONSOLE.lock().0.buffer_input();
crate::console::CONSOLE.lock().inner.buffer_input();
increment_irq_counter(SERIAL_IRQ);
crate::executor::run();

Expand Down
41 changes: 35 additions & 6 deletions src/console.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,60 @@
use core::task::Waker;
use core::{fmt, mem};

use heapless::Vec;
use hermit_sync::{InterruptTicketMutex, Lazy};

use crate::arch;

pub(crate) struct Console(pub arch::kernel::Console);
const SERIAL_BUFFER_SIZE: usize = 256;

pub(crate) struct Console {
pub inner: arch::kernel::Console,
buffer: Vec<u8, SERIAL_BUFFER_SIZE>,
}

impl Console {
fn new() -> Self {
Self(arch::kernel::Console::new())
Self {
inner: arch::kernel::Console::new(),
buffer: Vec::new(),
}
}

pub fn write(&mut self, buf: &[u8]) {
self.0.write(buf);
if SERIAL_BUFFER_SIZE - self.buffer.len() >= buf.len() {
// unwrap: we checked that buf fits in self.buffer
self.buffer.extend_from_slice(buf).unwrap();
if buf.contains(&b'\n') {
self.inner.write(&self.buffer);
self.buffer.clear();
}
} else {
self.inner.write(&self.buffer);
self.buffer.clear();
if buf.len() >= SERIAL_BUFFER_SIZE {
self.inner.write(buf);
} else {
// unwrap: we checked that buf fits in self.buffer
self.buffer.extend_from_slice(buf).unwrap();
if buf.contains(&b'\n') {
self.inner.write(&self.buffer);
self.buffer.clear();
}
}
}
}

pub fn read(&mut self) -> Option<u8> {
self.0.read()
self.inner.read()
}

pub fn is_empty(&self) -> bool {
self.0.is_empty()
self.inner.is_empty()
}

pub fn register_waker(&mut self, waker: &Waker) {
self.0.register_waker(waker);
self.inner.register_waker(waker);
}
}

Expand Down
15 changes: 13 additions & 2 deletions src/syscalls/interfaces/uhyve.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
use core::ptr;

use memory_addresses::VirtAddr;
use uhyve_interface::parameters::ExitParams;
use uhyve_interface::parameters::{ExitParams, SerialWriteBufferParams};
use uhyve_interface::{Hypercall, HypercallAddress};

use crate::arch;
use crate::arch::mm::paging;
use crate::arch::mm::paging::{self, virtual_to_physical};
use crate::syscalls::interfaces::SyscallInterface;

/// perform a SerialWriteBuffer hypercall with `buf` as payload.
#[inline]
#[cfg_attr(target_arch = "riscv64", expect(dead_code))]
pub(crate) fn serial_buf_hypercall(buf: &[u8]) {
let p = SerialWriteBufferParams {
buf: virtual_to_physical(VirtAddr::from_ptr(core::ptr::from_ref::<[u8]>(buf))).unwrap(),
len: buf.len(),
};
uhyve_hypercall(Hypercall::SerialWriteBuffer(&p));
}

/// calculates the physical address of the struct passed as reference.
#[inline]
fn data_addr<T>(data: &T) -> u64 {
Expand Down

0 comments on commit 27b3706

Please sign in to comment.