diff --git a/s-mode-utils/src/sbi_console.rs b/s-mode-utils/src/sbi_console.rs index e27f231d..2cbbafda 100644 --- a/s-mode-utils/src/sbi_console.rs +++ b/s-mode-utils/src/sbi_console.rs @@ -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 = 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); + } } } diff --git a/src/host_vm.rs b/src/host_vm.rs index 66c5e66f..a789d87a 100644 --- a/src/host_vm.rs +++ b/src/host_vm.rs @@ -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; @@ -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(()); @@ -496,6 +512,48 @@ impl HostVmRunner { Ok(()) } + + fn handle_put_string( + &mut self, + vm: &FinalizedVm, + addr: u64, + len: u64, + ) -> core::result::Result { + // 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 { diff --git a/src/vm.rs b/src/vm.rs index 6f8bc324..79c90bf8 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -129,6 +129,7 @@ pub enum VmExitCause { FatalEcall(SbiMessage), ResumableEcall(SbiMessage), BlockingEcall(SbiMessage, TlbVersion), + ForwardedEcall(SbiMessage), PageFault(Exception, GuestPageAddr), MmioFault(MmioOperation, GuestPhysAddr), Wfi(DecodedInstruction), @@ -193,11 +194,11 @@ impl From for EcallError { #[derive(Clone, Copy, Debug)] enum EcallAction { - LegacyOk, Unhandled, Continue(SbiReturn), Break(VmExitCause, SbiReturn), Retry(VmExitCause), + Forward(SbiMessage), } impl From> for EcallAction { @@ -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, @@ -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; } @@ -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) -> 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), @@ -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, - ) -> core::result::Result { - 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, - ) -> 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)) } } } diff --git a/src/vm_cpu.rs b/src/vm_cpu.rs index 9712b87b..d2371996 100644 --- a/src/vm_cpu.rs +++ b/src/vm_cpu.rs @@ -11,7 +11,7 @@ use page_tracking::TlbVersion; use riscv_page_tables::GuestStagePagingMode; use riscv_pages::{GuestPhysAddr, GuestVirtAddr, PageOwnerId, RawAddr}; use riscv_regs::*; -use sbi::{self, api::tee_host::TsmShmemAreaRef, SbiMessage, SbiReturnType}; +use sbi::{self, api::tee_host::TsmShmemAreaRef, SbiMessage, SbiReturn, SbiReturnType}; use spin::{Mutex, MutexGuard, Once, RwLock}; use crate::smp::PerCpu; @@ -364,12 +364,18 @@ struct PrevTlb { tlb_version: TlbVersion, } +// An operation that's pending a return value from the vCPU's host. +enum PendingOperation { + Mmio(MmioOperation), + Ecall(SbiMessage), +} + // The architectural state of a vCPU. struct VmCpuArchState { regs: VmCpuRegisters, pmu: VmPmuState, prev_tlb: Option, - pending_mmio_op: Option, + pending_op: Option, shmem_area: Option, } @@ -406,7 +412,7 @@ impl VmCpuArchState { regs, pmu: VmPmuState::default(), prev_tlb: None, - pending_mmio_op: None, + pending_op: None, shmem_area: None, } } @@ -553,7 +559,7 @@ impl<'vcpu, 'pages, 'host, T: GuestStagePagingMode> ActiveVmCpu<'vcpu, 'pages, ' /// Runs this vCPU until it traps. pub fn run(&mut self) -> VmCpuTrap { - self.complete_pending_mmio_op(); + self.complete_pending_op(); match self.host_context { VmCpuParent::HostVm(ref host_vcpu) => { @@ -791,6 +797,10 @@ impl<'vcpu, 'pages, 'host, T: GuestStagePagingMode> ActiveVmCpu<'vcpu, 'pages, ' ResumableEcall(msg) | FatalEcall(msg) | BlockingEcall(msg, _) => { self.report_ecall_exit(msg); } + ForwardedEcall(msg) => { + self.report_ecall_exit(msg); + self.arch.pending_op = Some(PendingOperation::Ecall(msg)); + } PageFault(exception, page_addr) => { self.report_pf_exit(exception, page_addr.into()); } @@ -817,7 +827,7 @@ impl<'vcpu, 'pages, 'host, T: GuestStagePagingMode> ActiveVmCpu<'vcpu, 'pages, ' self.host_context.set_guest_gpr(GprIndex::A0, val); // We'll complete a load instruction the next time this vCPU is run. - self.arch.pending_mmio_op = Some(mmio_op); + self.arch.pending_op = Some(PendingOperation::Mmio(mmio_op)); } Wfi(inst) => { self.report_vi_exit(inst.raw() as u64); @@ -1052,45 +1062,61 @@ impl<'vcpu, 'pages, 'host, T: GuestStagePagingMode> ActiveVmCpu<'vcpu, 'pages, ' self.arch.shmem_area = None; } - // Completes any pending MMIO operation for this CPU. - fn complete_pending_mmio_op(&mut self) { - // Complete any pending load operations. The host is expected to have written the value - // to complete the load to A0. - if let Some(mmio_op) = self.arch.pending_mmio_op { - let val = self.host_context.guest_gpr(GprIndex::A0); - use MmioOpcode::*; - // Write the value to the actual destination register. - match mmio_op.opcode() { - Load8 => { - self.set_gpr(mmio_op.register(), val as i8 as u64); - } - Load8U => { - self.set_gpr(mmio_op.register(), val as u8 as u64); - } - Load16 => { - self.set_gpr(mmio_op.register(), val as i16 as u64); - } - Load16U => { - self.set_gpr(mmio_op.register(), val as u16 as u64); - } - Load32 => { - self.set_gpr(mmio_op.register(), val as i32 as u64); - } - Load32U => { - self.set_gpr(mmio_op.register(), val as u32 as u64); - } - Load64 => { - self.set_gpr(mmio_op.register(), val); - } - _ => (), - }; + // Completes any pending MMIO or ECALL result from the host for this vCPU. + fn complete_pending_op(&mut self) { + match self.arch.pending_op { + Some(PendingOperation::Mmio(mmio_op)) => { + // Complete any pending load operations. The host is expected to have written the + // value to complete the load to A0. + let val = self.host_context.guest_gpr(GprIndex::A0); + use MmioOpcode::*; + // Write the value to the actual destination register. + match mmio_op.opcode() { + Load8 => { + self.set_gpr(mmio_op.register(), val as i8 as u64); + } + Load8U => { + self.set_gpr(mmio_op.register(), val as u8 as u64); + } + Load16 => { + self.set_gpr(mmio_op.register(), val as i16 as u64); + } + Load16U => { + self.set_gpr(mmio_op.register(), val as u16 as u64); + } + Load32 => { + self.set_gpr(mmio_op.register(), val as i32 as u64); + } + Load32U => { + self.set_gpr(mmio_op.register(), val as u32 as u64); + } + Load64 => { + self.set_gpr(mmio_op.register(), val); + } + _ => (), + }; + self.host_context.set_guest_gpr(GprIndex::A0, 0); - self.arch.pending_mmio_op = None; - self.host_context.set_guest_gpr(GprIndex::A0, 0); + // Advance SEPC past the faulting instruction. + self.inc_sepc(mmio_op.len() as u64); + } + Some(PendingOperation::Ecall(msg)) => { + // Forward the SBI call return value from the A0/A1 values provided by the host. + let sbi_ret = match msg { + SbiMessage::PutChar(_) => { + SbiReturnType::Legacy(self.host_context.guest_gpr(GprIndex::A0)) + } + _ => SbiReturnType::Standard(SbiReturn { + error_code: self.host_context.guest_gpr(GprIndex::A0) as i64, + return_value: self.host_context.guest_gpr(GprIndex::A1), + }), + }; - // Advance SEPC past the faulting instruction. - self.inc_sepc(mmio_op.len() as u64); + self.set_ecall_result(sbi_ret); + } + None => (), } + self.arch.pending_op = None; } fn save(&mut self) { diff --git a/test-workloads/src/bin/consts.rs b/test-workloads/src/bin/consts.rs index f33dbaaf..18e19c65 100644 --- a/test-workloads/src/bin/consts.rs +++ b/test-workloads/src/bin/consts.rs @@ -34,6 +34,8 @@ /// |-------------------------| 0x1_0000_0000 /// | Shared pages | /// |-------------------------| +NUM_GUEST_SHARED_PAGES +/// | Shared console buffer | +/// |-------------------------| +4kB /// | | /// +-------------------------+ 0x1_8000_0000 @@ -52,8 +54,8 @@ pub const GUEST_ZERO_PAGES_END_ADDRESS: u64 = GUEST_ZERO_PAGES_START_ADDRESS + NUM_GUEST_ZERO_PAGES * PAGE_SIZE_4K - 1; pub const GUEST_SHARED_PAGES_START_ADDRESS: u64 = 0x1_0000_0000; pub const NUM_GUEST_SHARED_PAGES: u64 = 1; -pub const GUEST_SHARED_PAGES_END_ADDRESS: u64 = - GUEST_SHARED_PAGES_START_ADDRESS + NUM_GUEST_SHARED_PAGES * PAGE_SIZE_4K - 1; +pub const GUEST_DBCN_ADDRESS: u64 = + GUEST_SHARED_PAGES_START_ADDRESS + NUM_GUEST_SHARED_PAGES * PAGE_SIZE_4K; pub const GUEST_RAM_END_ADDRESS: u64 = 0x1_8000_0000; pub const GUEST_SHARE_PING: u64 = 0xBAAD_F00D; pub const GUEST_SHARE_PONG: u64 = 0xF00D_BAAD; diff --git a/test-workloads/src/bin/guestvm.rs b/test-workloads/src/bin/guestvm.rs index 327c605d..7ff23de2 100644 --- a/test-workloads/src/bin/guestvm.rs +++ b/test-workloads/src/bin/guestvm.rs @@ -358,13 +358,23 @@ fn test_interrupts() { #[no_mangle] #[allow(clippy::zero_ptr)] extern "C" fn kernel_init(_hart_id: u64, boot_args: u64) { - SbiConsole::set_as_console(); + base::probe_sbi_extension(sbi::EXT_TEE_GUEST).expect("TEE-Guest extension not present"); + + // Convert a page to shared memory for use with the debug console. + // + // Safety: We haven't touched this memory and we won't touch it until the call returns. + unsafe { + tee_guest::share_memory(GUEST_DBCN_ADDRESS, PAGE_SIZE_4K) + .expect("GuestVm -- ShareMemory failed"); + } + let console_mem = unsafe { + core::slice::from_raw_parts_mut(GUEST_DBCN_ADDRESS as *mut u8, PAGE_SIZE_4K as usize) + }; + SbiConsole::set_as_console(console_mem); println!("*****************************************"); println!("Hello world from Tellus guest "); - base::probe_sbi_extension(sbi::EXT_TEE_GUEST).expect("TEE-Guest extension not present"); - let vectors_enabled = boot_args & BOOT_ARG_VECTORS_ENABLED != 0; if vectors_enabled { println!("guestvm vector extension enabled (on)"); diff --git a/test-workloads/src/bin/tellus.rs b/test-workloads/src/bin/tellus.rs index e2996765..84f34667 100644 --- a/test-workloads/src/bin/tellus.rs +++ b/test-workloads/src/bin/tellus.rs @@ -32,8 +32,9 @@ use s_mode_utils::ecall::ecall_send; use s_mode_utils::{print::*, sbi_console::SbiConsole}; use sbi::api::{base, nacl, pmu, reset, tee_host, tee_interrupt}; use sbi::{ - PmuCounterConfigFlags, PmuCounterStartFlags, PmuCounterStopFlags, PmuEventType, PmuFirmware, - PmuHardware, SbiMessage, EXT_PMU, EXT_TEE_HOST, EXT_TEE_INTERRUPT, + Error as SbiError, PmuCounterConfigFlags, PmuCounterStartFlags, PmuCounterStopFlags, + PmuEventType, PmuFirmware, PmuHardware, SbiMessage, SbiReturn, EXT_PMU, EXT_TEE_HOST, + EXT_TEE_INTERRUPT, }; // Dummy global allocator - panic if anything tries to do an allocation. @@ -234,21 +235,60 @@ fn check_vectors() { } } +fn do_guest_puts( + dbcn_gpa_range: Option>, + dbcn_spa_range: Option>, + guest_addr: u64, + len: u64, +) -> Result { + // Make sure the range the guest wants to print is fully contained within a GPA region it + // converted to shared memory and that we've mapped the backing pages. + let guest_end_addr = guest_addr.checked_add(len).ok_or(0u64)? - 1; + let dbcn_gpa_range = dbcn_gpa_range + .filter(|r| r.contains(&guest_addr) && r.contains(&guest_end_addr)) + .ok_or(0u64)?; + let offset = guest_addr - dbcn_gpa_range.start; + let dbcn_spa_range = dbcn_spa_range + .filter(|r| r.end - r.start >= offset + len) + .ok_or(0u64)?; + + let mut copied = 0; + let mut host_addr = dbcn_spa_range.start + 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 host address is in a valid part of our address + // space. `u8`s are always aligned and properly initialized. + *c = unsafe { core::ptr::read_volatile(host_addr as *const u8) }; + host_addr += 1; + } + let s = core::str::from_utf8(&buf[..to_copy]).map_err(|_| copied)?; + print!("{s}"); + copied += to_copy as u64; + } + + Ok(len) +} + +static mut CONSOLE_BUFFER: [u8; 256] = [0; 256]; + /// The entry point of the Rust part of the kernel. #[no_mangle] extern "C" fn kernel_init(hart_id: u64, fdt_addr: u64) { const NUM_TEE_PTE_PAGES: u64 = 10; const NUM_CONVERTED_ZERO_PAGES: u64 = NUM_GUEST_ZERO_PAGES + NUM_GUEST_SHARED_PAGES; + // Safety: We're giving SbiConsole exclusive ownership of CONSOLE_BUFFER and will not touch it + // for the remainder of this program. + unsafe { SbiConsole::set_as_console(&mut CONSOLE_BUFFER) }; + println!("Tellus: Booting the test VM"); + if hart_id != 0 { // TODO handle more than 1 cpu abort(); } - SbiConsole::set_as_console(); - - println!("Tellus: Booting the test VM"); - // Safe because we trust the host to boot with a valid fdt_addr pass in register a1. let fdt = match unsafe { Fdt::new_from_raw_pointer(fdt_addr as *const u8) } { Ok(f) => f, @@ -415,6 +455,8 @@ extern "C" fn kernel_init(hart_id: u64, fdt_addr: u64) { next_page += NUM_CONVERTED_ZERO_PAGES * PAGE_SIZE_4K; let shared_page_base = next_page; + next_page += NUM_GUEST_SHARED_PAGES * PAGE_SIZE_4K; + let dbcn_page_base = next_page; // Tell the guest if we have vector support via its boot argument. let boot_arg = if vector_enabled { @@ -493,7 +535,14 @@ extern "C" fn kernel_init(hart_id: u64, fdt_addr: u64) { sie.modify(sie::sext.val(1)); CSR.sie.set(sie.get()); - let mut shared_mem_region: Option> = None; + // DBCN shared memory region mapping. + let mut dbcn_gpa_range: Option> = None; + let mut dbcn_spa_range: Option> = None; + + // Non-DBCN shared memory region mapping. + let mut shared_gpa_range: Option> = None; + let mut shared_spa_range: Option> = None; + let mut mmio_region: Option> = None; loop { // Safety: running a VM will only write the `TsmShmemArea` struct that was registered @@ -526,13 +575,18 @@ extern "C" fn kernel_init(hart_id: u64, fdt_addr: u64) { }); } ShareMemory { addr, len } => { - if shared_mem_region.is_some() { - panic!("GuestVm already set a shared memory region"); - } - shared_mem_region = Some(Range { + let range = Range { start: addr, end: addr + len, - }); + }; + if addr == GUEST_DBCN_ADDRESS { + dbcn_gpa_range = Some(range); + } else { + if shared_gpa_range.is_some() { + panic!("GuestVm already set a shared memory region"); + } + shared_gpa_range = Some(range); + } if blocked { tee_host::tvm_run(vmid, 0).unwrap_err(); @@ -542,13 +596,14 @@ extern "C" fn kernel_init(hart_id: u64, fdt_addr: u64) { } UnshareMemory { addr, len } => { assert_eq!( - shared_mem_region, + shared_gpa_range, Some(Range { start: addr, end: addr + len, }) ); - shared_mem_region = None; + shared_gpa_range = None; + shared_spa_range = None; if blocked { tee_host::tvm_run(vmid, 0).unwrap_err(); @@ -581,6 +636,26 @@ extern "C" fn kernel_init(hart_id: u64, fdt_addr: u64) { } } } + Ok(DebugConsole(sbi::DebugConsoleFunction::PutString { len, addr })) => { + let sbi_ret = match do_guest_puts( + dbcn_gpa_range.clone(), + dbcn_spa_range.clone(), + addr, + len, + ) { + Ok(n) => SbiReturn::success(n), + Err(n) => SbiReturn { + error_code: SbiError::InvalidAddress as i64, + return_value: n, + }, + }; + shmem.set_gpr(GprIndex::A0 as usize, sbi_ret.error_code as u64); + shmem.set_gpr(GprIndex::A1 as usize, sbi_ret.return_value); + } + Ok(PutChar(c)) => { + print!("{}", c as u8 as char); + shmem.set_gpr(GprIndex::A0 as usize, 0); + } _ => { println!("Unexpected ECALL from guest"); break; @@ -590,10 +665,11 @@ extern "C" fn kernel_init(hart_id: u64, fdt_addr: u64) { Trap::Exception(GuestLoadPageFault) | Trap::Exception(GuestStorePageFault) => { let fault_addr = (shmem.csr(CSR_HTVAL) << 2) | (CSR.stval.get() & 0x3); match fault_addr { - addr if shared_mem_region + addr if shared_gpa_range .as_ref() .filter(|r| r.contains(&addr)) - .is_some() => + .is_some() + && shared_spa_range.is_none() => { // Safety: shared_page_base points to pages that will only be accessed // as volatile from here on. @@ -607,6 +683,10 @@ extern "C" fn kernel_init(hart_id: u64, fdt_addr: u64) { ) .expect("Tellus -- TvmAddSharedPages failed"); } + shared_spa_range = Some(Range { + start: shared_page_base, + end: shared_page_base + NUM_GUEST_SHARED_PAGES * PAGE_SIZE_4K, + }); // Safety: We own the page, and are writing a value expected by the // guest. Note that any access to shared pages must use volatile memory @@ -618,6 +698,29 @@ extern "C" fn kernel_init(hart_id: u64, fdt_addr: u64) { ); } } + addr if dbcn_gpa_range + .as_ref() + .filter(|r| r.contains(&addr)) + .is_some() + && dbcn_spa_range.is_none() => + { + // Safety: dbcn_page_base points to pages that will only be accessed + // as volatile from here on. + unsafe { + tee_host::add_shared_pages( + vmid, + dbcn_page_base, + sbi::TsmPageType::Page4k, + 1, + addr & !(PAGE_SIZE_4K - 1), + ) + .expect("Tellus -- TvmAddSharedPages failed"); + } + dbcn_spa_range = Some(Range { + start: dbcn_page_base, + end: dbcn_page_base + PAGE_SIZE_4K, + }); + } addr if mmio_region.as_ref().filter(|r| r.contains(&addr)).is_some() => { let inst = DecodedInstruction::from_raw(shmem.csr(CSR_HTINST) as u32) .expect("Failed to decode faulting MMIO instruction")