diff --git a/plat/Cargo.toml b/plat/Cargo.toml index 0be55a748c6e..a69bd66083a3 100644 --- a/plat/Cargo.toml +++ b/plat/Cargo.toml @@ -18,6 +18,7 @@ max_level_debug = ["log/max_level_debug", "islet_rmm/max_level_debug"] max_level_trace = ["log/max_level_trace", "islet_rmm/max_level_trace"] stat = ["islet_rmm/stat"] gst_page_table = ["islet_rmm/gst_page_table"] +ns_state_save = ["islet_rmm/ns_state_save"] fvp = ["islet_rmm/fvp"] qemu = ["islet_rmm/qemu"] diff --git a/plat/src/main.rs b/plat/src/main.rs index cc1a1fc9c32e..2603c522e554 100644 --- a/plat/src/main.rs +++ b/plat/src/main.rs @@ -9,7 +9,6 @@ extern crate log; mod entry; mod plat; -use aarch64_cpu::registers::*; use islet_rmm::allocator; use islet_rmm::config::PlatformMemoryLayout; use islet_rmm::cpu; @@ -24,15 +23,19 @@ extern "C" { #[no_mangle] pub unsafe fn main(x0: u64, x1: u64, x2: u64, x3: u64) -> ! { let cpuid: usize = x0 as usize; - info!( - "booted on core {:2} with EL{}!", - cpuid, - CurrentEL.read(CurrentEL::EL) as u8 - ); - info!( - "boot args: x0:0x{:X}, x1:0x{:X}, x2:0x{:X}, x3:0x{:X}", - x0, x1, x2, x3 - ); + // Do not print here until MMU is turned on, except on cpu0. + // Cores other than cpu0 at this point are still in its mmu + // off state with d-cache disabled and i-cache enabled. + // This may cause incosistencies between cpus with mmu enabled + // and cpus with mmu disabled. + // Logging involves buffer allocation and its internal data struct + // for heap (linked_list) could be corrupted due to the reason above. + if x0 == 0 { + info!( + "boot args: x0:0x{:X}, x1:0x{:X}, x2:0x{:X}, x3:0x{:X}", + x0, x1, x2, x3 + ); + } if cpuid != cpu::get_cpu_id() { panic!( diff --git a/rmm/Cargo.toml b/rmm/Cargo.toml index faafce2ea64e..914218906c50 100644 --- a/rmm/Cargo.toml +++ b/rmm/Cargo.toml @@ -40,6 +40,7 @@ stat = [] gst_page_table = [] fvp = [] qemu = [] +ns_state_save = [] # The below are features relevant for model checking mc_rmi_features = [] diff --git a/rmm/src/asm.rs b/rmm/src/asm.rs index 394841f5d2b7..40c0887ea8da 100644 --- a/rmm/src/asm.rs +++ b/rmm/src/asm.rs @@ -81,3 +81,14 @@ pub fn dcache_flush(addr: usize, len: usize) { } } } + +#[inline(always)] +pub fn isb() { + #[cfg(target_arch = "aarch64")] + unsafe { + core::arch::asm!("isb", options(nomem, nostack)) + } + + #[cfg(not(target_arch = "aarch64"))] + unimplemented!() +} diff --git a/rmm/src/lib.rs b/rmm/src/lib.rs index 328544883b7d..2aa5098257ec 100755 --- a/rmm/src/lib.rs +++ b/rmm/src/lib.rs @@ -79,6 +79,11 @@ use core::ptr::addr_of; pub unsafe fn start(cpu_id: usize, layout: PlatformMemoryLayout) { let el3_shared_buf = layout.el3_shared_buf; setup_mmu_cfg(layout); + info!( + "booted on core {:2} with EL{}!", + cpu_id, + CurrentEL.read(CurrentEL::EL) as u8 + ); setup_el2(); #[cfg(feature = "gst_page_table")] setup_gst(); diff --git a/rmm/src/rec/mod.rs b/rmm/src/rec/mod.rs index b8e7dde85afc..52bab532625f 100644 --- a/rmm/src/rec/mod.rs +++ b/rmm/src/rec/mod.rs @@ -374,6 +374,7 @@ pub fn run_prepare(rd: &Rd, vcpu: usize, rec: &mut Rec<'_>, incr_pc: usize) -> R if incr_pc == 1 { rec.context.elr_el2 += 4; } + timer::update_timer_assertion(rec); debug!("resuming: {:#x}", rec.context.elr_el2); rec.into_current(); @@ -391,3 +392,19 @@ pub fn run() -> Result<[usize; 4], Error> { pub fn max_recs_order() -> usize { MAX_RECS_ORDER_VALUE as usize } + +// Note: Islet intends to manage states only for the realm world. +// Handling the ns state here does not align Islet's desgin. +// Save the host state only if necessary. +pub fn save_host_state(rec: &Rec<'_>) { + pmu::save_host_state(rec); + // TODO: Apply 'ns_state_save' feature to the save_host_state func. + // For that, we need to move the code here from the corresponding patches + // in the nw-linux. + timer::save_host_state(rec); +} + +pub fn restore_host_state(rec: &Rec<'_>) { + pmu::restore_host_state(rec); + timer::restore_host_state(rec); +} diff --git a/rmm/src/rec/timer.rs b/rmm/src/rec/timer.rs index 4650aabc7fa7..e9fc75d448d1 100644 --- a/rmm/src/rec/timer.rs +++ b/rmm/src/rec/timer.rs @@ -1,9 +1,48 @@ use super::Rec; +use crate::asm::isb; use crate::rmi::error::Error; use crate::rmi::rec::run::Run; use aarch64_cpu::registers::*; +#[cfg(feature = "ns_state_save")] +mod ns_timer { + use super::*; + use crate::config::NUM_OF_CPU; + use crate::cpu::get_cpu_id; + use crate::rec::context::TimerRegister; + use core::array::from_fn; + use lazy_static::lazy_static; + use spin::mutex::Mutex; + + lazy_static! { + static ref NS_TIMER: [Mutex; NUM_OF_CPU] = + from_fn(|_| Mutex::new(TimerRegister::default())); + } + + pub(super) fn restore() { + let ns_timer = NS_TIMER[get_cpu_id()].lock(); + CNTVOFF_EL2.set(ns_timer.cntvoff_el2); + CNTPOFF_EL2.set(ns_timer.cntpoff_el2); + CNTV_CVAL_EL0.set(ns_timer.cntv_cval_el0); + CNTV_CTL_EL0.set(ns_timer.cntv_ctl_el0); + CNTP_CVAL_EL0.set(ns_timer.cntp_cval_el0); + CNTP_CTL_EL0.set(ns_timer.cntp_ctl_el0); + CNTHCTL_EL2.set(ns_timer.cnthctl_el2); + } + + pub(super) fn save() { + let mut timer = NS_TIMER[get_cpu_id()].lock(); + timer.cntvoff_el2 = CNTVOFF_EL2.get(); + timer.cntv_cval_el0 = CNTV_CVAL_EL0.get(); + timer.cntv_ctl_el0 = CNTV_CTL_EL0.get(); + timer.cntpoff_el2 = CNTPOFF_EL2.get(); + timer.cntp_cval_el0 = CNTP_CVAL_EL0.get(); + timer.cntp_ctl_el0 = CNTP_CTL_EL0.get(); + timer.cnthctl_el2 = CNTHCTL_EL2.get(); + } +} + pub fn init_timer(rec: &mut Rec<'_>) { let timer = &mut rec.context.timer; timer.cnthctl_el2 = (CNTHCTL_EL2::EL1PCEN::SET + CNTHCTL_EL2::EL1PCTEN::SET).into(); @@ -49,3 +88,51 @@ pub fn send_state_to_host(rec: &Rec<'_>, run: &mut Run) -> Result<(), Error> { run.set_cntp_cval(timer.cntp_cval_el0 - timer.cntpoff_el2); Ok(()) } + +pub fn save_host_state(_rec: &Rec<'_>) { + #[cfg(feature = "ns_state_save")] + ns_timer::save(); +} + +pub fn restore_host_state(_rec: &Rec<'_>) { + #[cfg(feature = "ns_state_save")] + ns_timer::restore(); +} + +// RMM spec A6.2 Realm timers, I +// On REC entry, for both the EL1 Virtual Timer and the EL1 Physical Timer, +// if the EL1 timer asserts its output in the state described in the REC exit +// structure from the previous REC exit then the RMM masks the hardware timer +// signal before returning to the Realm. +pub fn update_timer_assertion(rec: &mut Rec<'_>) { + const CNTHCTL_EL2_CNTVMASK: u64 = 0x1 << 18; + const CNTHCTL_EL2_CNTPMASK: u64 = 0x1 << 19; + let timer = &mut rec.context.timer; + + // Get recently saved timer control registers + let cnthctl_old = timer.cnthctl_el2; + + // Check if virtual timer is asserted + if CNTV_CTL_EL0.matches_all( + CNTV_CTL_EL0::ISTATUS::SET + CNTV_CTL_EL0::IMASK::CLEAR + CNTV_CTL_EL0::ENABLE::SET, + ) { + timer.cnthctl_el2 |= CNTHCTL_EL2_CNTVMASK; + } else { + timer.cnthctl_el2 &= !CNTHCTL_EL2_CNTVMASK; // Clear MASK + } + + // Check if physical timer is asserted + if CNTP_CTL_EL0.matches_all( + CNTP_CTL_EL0::ISTATUS::SET + CNTP_CTL_EL0::IMASK::CLEAR + CNTP_CTL_EL0::ENABLE::SET, + ) { + timer.cnthctl_el2 |= CNTHCTL_EL2_CNTPMASK; + } else { + timer.cnthctl_el2 &= !CNTHCTL_EL2_CNTPMASK; // Clear MASK + } + + // If cnthctl changed, write it back and ensure synchronization + if cnthctl_old != timer.cnthctl_el2 { + CNTHCTL_EL2.set(timer.cnthctl_el2); + isb(); + } +} diff --git a/rmm/src/rmi/rec/handlers.rs b/rmm/src/rmi/rec/handlers.rs index 0f25f1ffb908..5d5a813ee618 100644 --- a/rmm/src/rmi/rec/handlers.rs +++ b/rmm/src/rmi/rec/handlers.rs @@ -233,15 +233,24 @@ pub fn set_event_handler(rmi: &mut RmiHandle) { crate::rsi::ripas::complete_ripas(&mut rec, &run)?; let wfx_flag = run.entry_flags(); - if wfx_flag.get_masked(EntryFlag::TRAP_WFI | EntryFlag::TRAP_WFE) != 0 { - warn!("Islet does not support re-configuring the WFI(E) trap"); - warn!("TWI(E) in HCR_EL2 is currently fixed to 'no trap'"); + #[cfg(not(any(miri, test, fuzzing)))] + { + let hcr_el2 = HCR_EL2.get(); + let mut wfx = 0; + let wfx_mask = !(3 << 13); + if wfx_flag.get_masked(EntryFlag::TRAP_WFI) != 0 { + wfx |= 1 << 13; + } + if wfx_flag.get_masked(EntryFlag::TRAP_WFE) != 0 { + wfx |= 1 << 14; + } + HCR_EL2.set((hcr_el2 & wfx_mask) | wfx); } #[cfg(not(any(miri, test, fuzzing)))] activate_stage2_mmu(&rec); - crate::rec::pmu::save_host_state(&rec); + crate::rec::save_host_state(&rec); let mut ret_ns; loop { ret_ns = true; @@ -305,7 +314,7 @@ pub fn set_event_handler(rmi: &mut RmiHandle) { crate::rec::gic::send_state_to_host(&rec, &mut run)?; crate::rec::timer::send_state_to_host(&rec, &mut run)?; crate::rec::pmu::send_state_to_host(&rec, &mut run)?; - crate::rec::pmu::restore_host_state(&rec); + crate::rec::restore_host_state(&rec); // NOTICE: do not modify `run` after copy_to_ptr(). it won't have any effect. rmm.page_table.map(run_pa, false); diff --git a/scripts/cca_base.py b/scripts/cca_base.py index 3e0d1f3bbd09..4f07220eb9e9 100755 --- a/scripts/cca_base.py +++ b/scripts/cca_base.py @@ -207,6 +207,7 @@ def get_rmm_features(self): features += ["--features", "stat"] if args.normal_world == "acs": features += ["--features", "gst_page_table"] + features += ["--features", "ns_state_save"] features += ["--features", self.platform_name] diff --git a/scripts/tests/acs.sh b/scripts/tests/acs.sh index d3af2a078222..b012ffc44324 100755 --- a/scripts/tests/acs.sh +++ b/scripts/tests/acs.sh @@ -3,7 +3,7 @@ set -e # Control these variables -EXPECTED=95 +EXPECTED=97 TIMEOUT=30 ROOT=$(git rev-parse --show-toplevel) diff --git a/skipped-tests.txt b/skipped-tests.txt index 20a5ff730c15..08d645a32537 100644 --- a/skipped-tests.txt +++ b/skipped-tests.txt @@ -1,7 +1,5 @@ cmd_multithread_realm_mp -exception_rec_exit_wfi exception_rec_exit_wfe attestation_rec_exit_irq -gic_timer_rel1_trig mm_gpf_exception cmd_secure_test