Skip to content

Commit

Permalink
rmm: Add support for MMIO handling
Browse files Browse the repository at this point in the history
This commit helps to handle the Memory-mapped I/O (MMIO) accesses by Realms.
When a Realm accesses device's registers using MMIO, it causes a data abort
which was the main reason for the kernel stuck. In this commit, RMM receives
the emulated value from the Host (nw-linux) and injects the value to the
Realm after setting the Realm's PC value to the next point. With these fixes,
the guest kernel in the Realm now could boot to the next stage.

Reference: Arm Armv9-A Architecture Registers
           TF-RMM (https://github.com/TF-RMM/tf-rmm)

[before]
...
[    0.000000] rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=1
[    0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
       //  no more log after this point  //

[after]
...
[    0.000000] rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=1
[    0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
[    0.000000] GICv3: 32 SPIs implemented
[    0.000000] GICv3: 0 Extended SPIs implemented
[    0.000000] Root IRQ handler: gic_handle_irq
[    0.000000] GICv3: GICv3 features: 16 PPIs, DirectLPI
[    0.000000] GICv3: CPU0: found redistributor 0 region 0:0x000000003ffd0000
[    0.000000] rcu: srcu_init: Setting srcu_struct sizes based on contention.
[    0.000000] arch_timer: cp15 timer(s) running at 100.00MHz (virt).
...
[    0.692188] RPC: Registered tcp NFSv4.1 backchannel transport module.
[    0.702813] kvm [1]: HYP mode not available
[    0.710786] Initialise system trusted keyrings
[    0.718649] Unpacking initramfs...
  • Loading branch information
zpzigi754 committed Jul 10, 2023
1 parent ed9962a commit 9d63ba7
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 1 deletion.
29 changes: 29 additions & 0 deletions rmm/armv9a/src/helper/regs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,43 @@ define_sys_register!(VBAR_EL2, RES0[10 - 0]);

define_sys_register!(
ESR_EL2,
// Exception Class.
EC[31 - 26],
// Instruction Length for synchronous exceptions.
IL[25 - 25],
// Instruction Specific Syndrome.
ISS[24 - 00],
ISS_BRK_CMT[15 - 00],
S1PTW[7 - 7],
DFSC[5 - 0]
);

define_bits!(
EsrEl2,
// Exception Class.
EC[31 - 26],
// Instruction Length for synchronous exceptions.
IL[25 - 25],
// Instruction syndrome valid.
ISV[24 - 24],
// Syndrome Access Size (ISV == '1')
SAS[23 - 22],
// Syndrome Sign Extend (ISV == '1')
SSE[21 - 21],
// Syndrome Register Transfer (ISV == '1')
SRT[20 - 16],
// Width of the register accessed by the instruction is Sixty-Four (ISV == '1')
SF[15 - 15],
// Acquire/Release. (ISV == '1')
AR[14 - 14],
S1PTW[7 - 7],
// Write not Read.
WNR[6 - 6],
DFSC[5 - 0]
);

pub const ESR_EL2_EC_DATA_ABORT: u64 = 36;

define_register!(SP);
define_sys_register!(SP_EL0);
define_sys_register!(SP_EL1);
Expand Down
50 changes: 49 additions & 1 deletion rmm/armv9a/src/realm/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ use monitor::realm::mm::address::{GuestPhysAddr, PhysAddr};
use monitor::realm::mm::IPATranslation;
use monitor::realm::vcpu::VCPU;
use monitor::realm::Realm;
use monitor::rmi::rec::run::Run;
use monitor::rmi::rec::run::{Run, REC_ENTRY_FLAG_EMUL_MMIO};
use monitor::rmi::MapProt;

use crate::gic::{GIC_FEATURES, ICH_HCR_EL2_EOI_COUNT_MASK, ICH_HCR_EL2_NS_MASK};
use crate::helper;
use crate::helper::bits_in_reg;
use crate::helper::VTTBR_EL2;
use crate::helper::{EsrEl2, ESR_EL2_EC_DATA_ABORT};
use crate::realm;
use crate::realm::context::Context;
use crate::realm::mm::page_table::pte;
Expand Down Expand Up @@ -292,4 +293,51 @@ impl monitor::rmi::Interface for RMI {
}
Ok(())
}

fn emulate_mmio(&self, id: usize, vcpu: usize, run: &Run) -> Result<(), &str> {
let realm = get_realm(id).ok_or("Not exist Realm")?;
let mut locked_realm = realm.lock();
let vcpu = locked_realm.vcpus.get_mut(vcpu).ok_or("Not exist VCPU")?;
let context = &mut vcpu.lock().context;

let flags = unsafe { run.entry_flags() };

// Host has not completed emulation for an Emulatable Abort.
if (flags & REC_ENTRY_FLAG_EMUL_MMIO) == 0 {
return Ok(());
}

let esr_el2 = context.sys_regs.esr_el2;
let esr = EsrEl2::new(esr_el2);
let isv = esr.get_masked_value(EsrEl2::ISV);
let ec = esr.get_masked_value(EsrEl2::EC);
let wnr = esr.get_masked_value(EsrEl2::WNR);
let rt = esr.get_masked_value(EsrEl2::SRT) as usize;

if ec != ESR_EL2_EC_DATA_ABORT || isv == 0 {
// TODO: change the error value into a common one
return Err("RMI_ERROR_REC");
}

// MMIO read case
if wnr == 0 && rt != 31 {
let sas = esr.get_masked_value(EsrEl2::SAS);
let mask: u64 = match sas {
0 => 0xff, // byte
1 => 0xffff, // half-word
2 => 0xffffffff, // word
3 => 0xffffffff_ffffffff, // double word
_ => unreachable!(), // SAS consists of two bits
};
let val = unsafe { run.entry_gpr0() } & mask;
let sign_extended = esr.get_masked_value(EsrEl2::SSE);
if sign_extended != 0 {
// TODO
unimplemented!();
}
context.gp_regs[rt] = val;
}
context.elr += 4;
Ok(())
}
}
1 change: 1 addition & 0 deletions rmm/monitor/src/rmi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ pub trait Interface {
fn get_reg(&self, id: usize, vcpu: usize, register: usize) -> Result<usize, &str>;
fn receive_gic_state_from_host(&self, id: usize, vcpu: usize, run: &Run) -> Result<(), &str>;
fn send_gic_state_to_host(&self, id: usize, vcpu: usize, run: &mut Run) -> Result<(), &str>;
fn emulate_mmio(&self, id: usize, vcpu: usize, run: &Run) -> Result<(), &str>;
}

pub(crate) fn dummy() {
Expand Down
1 change: 1 addition & 0 deletions rmm/monitor/src/rmi/rec/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ pub fn set_event_handler(mainloop: &mut Mainloop) {
}
}
let _ = rmi.receive_gic_state_from_host(rec.rd.id(), rec.id(), run);
let _ = rmi.emulate_mmio(rec.rd.id(), rec.id(), run);

let ripas = rec.ripas_addr();
if ripas > 0 {
Expand Down
24 changes: 24 additions & 0 deletions rmm/monitor/src/rmi/rec/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ impl Run {
&mut *(ptr as *mut Self)
}

pub unsafe fn entry_flags(&self) -> u64 {
self.entry.inner.flags.val
}

#[allow(dead_code)]
pub unsafe fn entry_gpr0(&self) -> u64 {
self.entry.inner.gprs.val[0]
Expand Down Expand Up @@ -133,6 +137,26 @@ union Flags {
reserved: [u8; 0x200],
}

/// Whether the host has completed emulation for an Emulatable Data Abort.
/// val 0: Host has not completed emulation for an Emulatable Abort.
/// val 1: Host has completed emulation for an Emulatable Abort.
pub const REC_ENTRY_FLAG_EMUL_MMIO: u64 = 1 << 0;
/// Whether to inject a Synchronous External Abort (SEA) into the Realm.
/// val 0: Do not inject an SEA into the Realm.
/// val 1: Inject an SEA into the Realm.
#[allow(dead_code)]
pub const REC_ENTRY_FLAG_INJECT_SEA: u64 = 1 << 1;
/// Whether to trap WFI execution by the Realm.
/// val 0: Trap is disabled.
/// val 1: Trap is enabled.
#[allow(dead_code)]
pub const REC_ENTRY_FLAG_TRAP_WFI: u64 = 1 << 2;
/// Whether to trap WFE execution by the Realm.
/// val 0: Trap is disabled.
/// val 1: Trap is enabled.
#[allow(dead_code)]
pub const REC_ENTRY_FLAG_TRAP_WFE: u64 = 1 << 3;

/// General-purpose registers
#[repr(C)]
union GPRs {
Expand Down

0 comments on commit 9d63ba7

Please sign in to comment.