Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forward debug console to host #190

Merged
Merged
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
27 changes: 20 additions & 7 deletions s-mode-utils/src/sbi_console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,40 @@

use sbi::api::debug_console::console_puts;
use sbi::SbiMessage;
use spin::{Mutex, Once};

use crate::ecall::ecall_send;
use crate::print::{Console, ConsoleWriter};

/// Driver for an SBI based console.
pub struct SbiConsole {}
pub struct SbiConsole {
buffer: Mutex<&'static mut [u8]>,
}

static SBI_CONSOLE: SbiConsole = SbiConsole {};
static SBI_CONSOLE: Once<SbiConsole> = Once::new();

impl SbiConsole {
/// Sets the SBI console as the system console.
pub fn set_as_console() {
Console::set_writer(&SBI_CONSOLE);
/// Sets the SBI debug console as the system console. Uses `console_buffer` for buffering console
/// output.
pub fn set_as_console(console_buffer: &'static mut [u8]) {
let console = SbiConsole {
buffer: Mutex::new(console_buffer),
};
SBI_CONSOLE.call_once(|| console);
Console::set_writer(SBI_CONSOLE.get().unwrap());
}
}

impl ConsoleWriter for SbiConsole {
/// Write an entire byte sequence to the SBI console.
fn write_bytes(&self, bytes: &[u8]) {
// Ignore errors as there isn't currently a way to report them if the console doesn't work.
let _ = console_puts(bytes);
let mut buffer = self.buffer.lock();
for chunk in bytes.chunks(buffer.len()) {
let (dest, _) = buffer.split_at_mut(chunk.len());
dest.copy_from_slice(chunk);
// Ignore errors as there isn't currently a way to report them if the console doesn't work.
let _ = console_puts(&*dest);
}
}
}

Expand Down
64 changes: 61 additions & 3 deletions src/host_vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use riscv_regs::{
CSR_HTINST, CSR_HTVAL, CSR_SCAUSE, CSR_STVAL,
};
use s_mode_utils::print::*;
use sbi::{self, SbiMessage};
use sbi::{self, DebugConsoleFunction, Error as SbiError, SbiMessage, SbiReturn, StateFunction};

use crate::guest_tracking::{GuestVm, Guests, Result as GuestTrackingResult};
use crate::smp;
Expand Down Expand Up @@ -421,12 +421,28 @@ impl HostVmRunner {
println!("Host VM requested shutdown");
return ControlFlow::Break(());
}
Ok(HartState(sbi::StateFunction::HartStart { hart_id, .. })) => {
Ok(HartState(StateFunction::HartStart { hart_id, .. })) => {
smp::send_ipi(CpuId::new(hart_id as usize));
}
Ok(HartState(sbi::StateFunction::HartStop)) => {
Ok(HartState(StateFunction::HartStop)) => {
return ControlFlow::Continue(())
}
Ok(DebugConsole(DebugConsoleFunction::PutString { len, addr })) => {
let sbi_ret = match self.handle_put_string(&vm, addr, len) {
Ok(n) => SbiReturn::success(n),
Err(n) => SbiReturn {
error_code: SbiError::InvalidAddress as i64,
return_value: n,
},
};

self.gprs.set_reg(GprIndex::A0, sbi_ret.error_code as u64);
self.gprs.set_reg(GprIndex::A1, sbi_ret.return_value);
}
Ok(PutChar(c)) => {
print!("{}", c as u8 as char);
self.gprs.set_reg(GprIndex::A0, 0);
}
_ => {
println!("Unhandled ECALL from host");
return ControlFlow::Break(());
Expand Down Expand Up @@ -496,6 +512,48 @@ impl HostVmRunner {

Ok(())
}

fn handle_put_string<T: GuestStagePagingMode>(
&mut self,
vm: &FinalizedVm<T>,
addr: u64,
len: u64,
) -> core::result::Result<u64, u64> {
// Pin the pages that we'll be printing from. We assume that the buffer is physically
// contiguous, which should be the case since that's how we set up the host VM's address
// space.
let page_addr = GuestPageAddr::with_round_down(
GuestPhysAddr::guest(addr, vm.page_owner_id()),
PageSize::Size4k,
);
let offset = addr - page_addr.bits();
let num_pages = PageSize::num_4k_pages(offset + len);
let pinned = vm
.vm_pages()
.pin_shared_pages(page_addr, num_pages)
.map_err(|_| 0u64)?;

// Print the bytes in chunks. We copy to a temporary buffer as the bytes could be modified
// concurrently by the VM on another CPU.
let mut copied = 0;
let mut hyp_addr = pinned.range().base().bits() + offset;
while copied != len {
let mut buf = [0u8; 256];
let to_copy = core::cmp::min(buf.len(), (len - copied) as usize);
for c in buf.iter_mut() {
// Safety: We've confirmed that the address is within a region of accessible memory
// and cannot be remapped as long as we hold the pin. `u8`s are always aligned and
// properly initialized.
*c = unsafe { core::ptr::read_volatile(hyp_addr as *const u8) };
hyp_addr += 1;
}
let s = core::str::from_utf8(&buf[..to_copy]).map_err(|_| copied)?;
print!("{s}");
copied += to_copy as u64;
}

Ok(len)
}
}

impl VmCpuExitReporting for HostVmRunner {
Expand Down
63 changes: 10 additions & 53 deletions src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ pub enum VmExitCause {
FatalEcall(SbiMessage),
ResumableEcall(SbiMessage),
BlockingEcall(SbiMessage, TlbVersion),
ForwardedEcall(SbiMessage),
PageFault(Exception, GuestPageAddr),
MmioFault(MmioOperation, GuestPhysAddr),
Wfi(DecodedInstruction),
Expand Down Expand Up @@ -193,11 +194,11 @@ impl From<SbiError> for EcallError {

#[derive(Clone, Copy, Debug)]
enum EcallAction {
LegacyOk,
Unhandled,
Continue(SbiReturn),
Break(VmExitCause, SbiReturn),
Retry(VmExitCause),
Forward(SbiMessage),
}

impl From<EcallResult<u64>> for EcallAction {
Expand Down Expand Up @@ -489,9 +490,6 @@ impl<'a, T: GuestStagePagingMode> FinalizedVm<'a, T> {
match exit {
VmCpuTrap::Ecall(Some(sbi_msg)) => {
match self.handle_ecall(sbi_msg, &mut active_vcpu) {
EcallAction::LegacyOk => {
active_vcpu.set_ecall_result(Legacy(0));
}
EcallAction::Unhandled => {
active_vcpu.set_ecall_result(Standard(SbiReturn::from(
SbiError::NotSupported,
Expand All @@ -504,6 +502,9 @@ impl<'a, T: GuestStagePagingMode> FinalizedVm<'a, T> {
active_vcpu.set_ecall_result(Standard(sbi_ret));
break reason;
}
EcallAction::Forward(sbi_msg) => {
break VmExitCause::ForwardedEcall(sbi_msg);
}
EcallAction::Retry(reason) => {
break reason;
}
Expand Down Expand Up @@ -705,18 +706,12 @@ impl<'a, T: GuestStagePagingMode> FinalizedVm<'a, T> {
/// Handles ecalls from the guest.
fn handle_ecall(&self, msg: SbiMessage, active_vcpu: &mut ActiveVmCpu<T>) -> EcallAction {
match msg {
SbiMessage::PutChar(c) => {
// put char - legacy command
print!("{}", c as u8 as char);
EcallAction::LegacyOk
}
SbiMessage::PutChar(_) => EcallAction::Forward(msg),
SbiMessage::Reset(ResetFunction::Reset { .. }) => {
EcallAction::Break(VmExitCause::FatalEcall(msg), SbiReturn::success(0))
}
SbiMessage::Base(base_func) => EcallAction::Continue(self.handle_base_msg(base_func)),
SbiMessage::DebugConsole(debug_con_func) => {
self.handle_debug_console(debug_con_func, active_vcpu.active_pages())
}
SbiMessage::DebugConsole(debug_con_func) => self.handle_debug_console(debug_con_func),
SbiMessage::HartState(hsm_func) => self.handle_hart_state_msg(hsm_func),
SbiMessage::Nacl(nacl_func) => self.handle_nacl_msg(nacl_func, active_vcpu),
SbiMessage::TeeHost(host_func) => self.handle_tee_host_msg(host_func, active_vcpu),
Expand Down Expand Up @@ -890,48 +885,10 @@ impl<'a, T: GuestStagePagingMode> FinalizedVm<'a, T> {
SbiReturn::success(ret)
}

// Handles the printing of characters from VM memory.
// Copies the characters to a bounce buffer, parses them as UTF-8, and sends them on to the
// console.
// On success or error, returns the number of bytes written.
fn debug_console_print(
&self,
mut len: u64,
addr: u64,
active_pages: &ActiveVmPages<T>,
) -> core::result::Result<u64, u64> {
let mut chunk = [0u8; 256];
let mut curr_addr = addr;
while len > 0 {
let count = core::cmp::min(len as usize, chunk.len());
active_pages
.copy_from_guest(
&mut chunk[0..count],
RawAddr::guest(curr_addr, self.page_owner_id()),
)
.map_err(|_| curr_addr - addr)?;
let s = core::str::from_utf8(&chunk[0..count]).map_err(|_| curr_addr - addr)?;
print!("{s}");
curr_addr += count as u64;
len -= count as u64;
}
Ok(len)
}

fn handle_debug_console(
&self,
debug_con_func: DebugConsoleFunction,
active_pages: &ActiveVmPages<T>,
) -> EcallAction {
fn handle_debug_console(&self, debug_con_func: DebugConsoleFunction) -> EcallAction {
match debug_con_func {
DebugConsoleFunction::PutString { len, addr } => {
EcallAction::Continue(match self.debug_console_print(len, addr, active_pages) {
Ok(n) => SbiReturn::success(n),
Err(n) => SbiReturn {
error_code: sbi::SBI_ERR_INVALID_ADDRESS,
return_value: n,
},
})
DebugConsoleFunction::PutString { .. } => {
EcallAction::Forward(SbiMessage::DebugConsole(debug_con_func))
}
}
}
Expand Down
Loading