Skip to content
Open
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
2 changes: 2 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7431,6 +7431,7 @@ dependencies = [
"minimal_rt_build",
"tmk_core",
"tmk_macros",
"tmk_protocol",
"x86defs",
]

Expand Down Expand Up @@ -8017,6 +8018,7 @@ dependencies = [
name = "tmk_protocol"
version = "0.0.0"
dependencies = [
"bitfield-struct 0.11.0",
"zerocopy",
]

Expand Down
9 changes: 9 additions & 0 deletions openhcl/hcl/src/ioctl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ pub(crate) mod ioctls {
const MSHV_VTL_RSI_SYSREG_READ: u16 = 0x42;
const MSHV_VTL_RSI_SYSREG_WRITE: u16 = 0x43;
const MSHV_VTL_RSI_SET_MEM_PERM: u16 = 0x44;
const MSHV_VTL_RSI_GET_IPA_STATE: u16 = 0x45;

#[repr(C)]
#[derive(Copy, Clone)]
Expand Down Expand Up @@ -617,6 +618,14 @@ pub(crate) mod ioctls {
cca::mshv_rsi_sysreg_rw
);

// CCA: Get the RIPAS state of an ipa
ioctl_readwrite!(
hcl_rsi_ipa_state_read,
MSHV_IOCTL,
MSHV_VTL_RSI_GET_IPA_STATE,
cca::mshv_rsi_get_ipa_state
);
Comment thread
Flgodd67 marked this conversation as resolved.
Comment on lines +621 to +627
Comment on lines +621 to +627

// CCA: Assign the address described by `mshv_rsi_set_mem_perm`
// to a plane.
// Note: This is a simplification of the memory access configuration.
Expand Down
50 changes: 50 additions & 0 deletions openhcl/hcl/src/ioctl/cca.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crate::ioctl::GetRegError;
use crate::ioctl::HvError;
use crate::ioctl::SetRegError;
use crate::ioctl::ioctls::hcl_realm_config;
use crate::ioctl::ioctls::hcl_rsi_ipa_state_read;
use crate::ioctl::ioctls::hcl_rsi_set_mem_perm;
use crate::ioctl::ioctls::hcl_rsi_sysreg_read;
use crate::ioctl::ioctls::hcl_rsi_sysreg_write;
Expand Down Expand Up @@ -70,6 +71,15 @@ pub struct mshv_rsi_set_mem_perm {
pub top_addr: u64,
}

/// CCA:
#[repr(C)]
#[derive(Clone, Copy, Default)]
#[expect(missing_docs)]
pub struct mshv_rsi_get_ipa_state {
Comment on lines +74 to +78
pub fipa: u64,
pub state: u64,
}

/// SystemReg is following encoding used by MSR/MRS which is different with
/// the encoding RSI is using. The latter doesn't left shift the register
/// number.
Expand Down Expand Up @@ -204,6 +214,17 @@ impl ProcessorRunner<'_, Cca> {
.rsi_sysreg_read(vtl, encode_rsi_sysreg(name), value)
}

/// Get the ipa ripas state from the RMM
pub fn cca_ipa_state_read(
&self,
vtl: GuestVtl,
state: &mut mshv_rsi_get_ipa_state,
) -> Result<(), Error> {
self.hcl
.rsi_get_ipa_state(vtl, state)
.map_err(|_| Error::InvalidRegisterValue)
}
Comment on lines +218 to +226
Comment on lines +217 to +226
Comment on lines +217 to +226
Comment on lines +217 to +226
Comment on lines +217 to +226

/// Update the address of the `plane_run` structure in `mshv_vtl_run.context`.
pub fn cca_set_plane_enter(&mut self) {
// SAFETY: the run page is exclusively accessed through `&mut self` while
Expand Down Expand Up @@ -469,6 +490,26 @@ impl MshvVtl {
}
Ok(())
}

/// Get the ipa RIPAS state
pub fn rsi_get_ipa_state(
&self,
vtl: GuestVtl,
plane_state: &mut mshv_rsi_get_ipa_state,
) -> Result<(), HvError> {
let _plane = match vtl {
GuestVtl::Vtl0 => 1,
_ => return Err(HvError::InvalidVtlState),
};
Comment on lines +500 to +503
Comment on lines +500 to +503

// SAFETY: Calling hcl_rsi_ipa_state_read ioctl with the correct arguments.
unsafe {
hcl_rsi_ipa_state_read(self.file.as_raw_fd(), plane_state)
.map_err(|_| HvError::InvalidVpState)?;
}

Ok(())
}
}

impl Hcl {
Expand Down Expand Up @@ -501,4 +542,13 @@ impl Hcl {
pub fn rsi_set_mem_perm(&self, vtl: GuestVtl, range: MemoryRange) -> Result<(), HvError> {
self.mshv_vtl.rsi_set_mem_perm(vtl, &range)
}

/// getting ipa RIPAS state
pub fn rsi_get_ipa_state(
&self,
vtl: GuestVtl,
plane_state: &mut mshv_rsi_get_ipa_state,
) -> Result<(), HvError> {
self.mshv_vtl.rsi_get_ipa_state(vtl, plane_state)
}
}
3 changes: 3 additions & 0 deletions openhcl/minimal_rt_build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
pub fn init() -> bool {
println!("cargo:rustc-check-cfg=cfg(minimal_rt)");

println!("cargo:rustc-check-cfg=cfg(paravisor_kernel, values(\"linux\"))");

// If the user sets this environment variable, build the binary for use as a
// boot loader. Otherwise, just build a stub binary for unit tests, clippy,
// rust-analyzer, etc.
Expand Down Expand Up @@ -52,6 +54,7 @@ pub fn init() -> bool {
}
}
"aarch64" => {
println!("cargo:rustc-cfg=paravisor_kernel=\"linux\"");
match triple.as_str() {
"aarch64-minimal_rt-none" => {
// This is a custom target, defined via
Expand Down
87 changes: 86 additions & 1 deletion openhcl/virt_mshv_vtl/src/processor/cca/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ use crate::UhCvmVpState;
use crate::UhPartitionInner;
use crate::processor::InterceptMessageState;
use aarch64defs::EsrEl2;
use aarch64defs::HpfarEl2;
use aarch64defs::InstructionAbortReason;
use aarch64defs::IssDataAbort;
use aarch64defs::IssInstructionAbort;
use aarch64defs::SystemReg;
use aarch64defs::rsi::cca_rsi_plane_exit;
use hcl::GuestVtl;
use hcl::ioctl::cca::Cca;
use hcl::ioctl::cca::mshv_rsi_get_ipa_state;
use hcl::ioctl::register;
use hv1_emulator::hv::ProcessorVtlHv;
use hv1_emulator::synic::ProcessorSynic;
Expand Down Expand Up @@ -49,6 +53,20 @@ enum CcaUnsupportedExit {
ExceptionClass { exception_class: u8, esr_el2: u64 },
#[error("CCA data abort with invalid instruction syndrome in ESR_EL2 {0:#x}")]
InvalidDataAbortIss(u64),
#[error(
"CCA instruction abort: ESR_EL2 {esr_el2:#x}, ELR_EL2 {elr_el2:#x}, FAR_EL2 {far_el2:#x},
FIPA {fipa:#x}, FIPA RIPAS state {fipa_state:#x}, IFSC {ifsc:#x}, reason {reason:?}, FNV {far_not_valid}"
)]
InstructionAbort {
esr_el2: u64,
elr_el2: u64,
far_el2: u64,
fipa: u64,
fipa_state: u8,
Comment thread
Flgodd67 marked this conversation as resolved.
ifsc: u8,
reason: InstructionAbortReason,
far_not_valid: bool,
},
}

const AARCH64_ZERO_REGISTER_INDEX: u8 = 31;
Expand Down Expand Up @@ -169,6 +187,14 @@ impl<'a> CcaExit<'a> {
self.0.far_el2
}

fn hpfar_el2(&self) -> HpfarEl2 {
self.0.hpfar_el2.into()
}

fn elr_el2(&self) -> u64 {
self.0.elr_el2
}

fn gpr_or_zero_register(&self, index: u8) -> Option<u64> {
match index {
AARCH64_ZERO_REGISTER_INDEX => Some(0),
Expand Down Expand Up @@ -345,7 +371,56 @@ impl BackingPrivate for CcaBacked {
}
ExceptionClass::InstructionAbort => {
// Handle instruction abort
todo!();
let iss = IssInstructionAbort::from_bits(esr_el2.iss());

if iss.fnv() {
tracing::warn!("CCA InstructionAbort: FAR_EL2 is not valid");

return Err(dev.fatal_error(
CcaUnsupportedExit::InstructionAbort {
esr_el2: cca_exit.0.esr_el2,
elr_el2: cca_exit.elr_el2(),
far_el2: cca_exit.far_el2(),
fipa: 0,
fipa_state: u8::MAX,
ifsc: iss.ifsc().0,
reason: InstructionAbortReason::Unknown,
far_not_valid: iss.fnv(),
Comment on lines +384 to +388
}
.into(),
));
}
Comment thread
Flgodd67 marked this conversation as resolved.
Comment thread
Flgodd67 marked this conversation as resolved.
Comment on lines +376 to +392
Comment on lines +376 to +392
Comment on lines +376 to +392
Comment thread
Flgodd67 marked this conversation as resolved.

let far = cca_exit.far_el2();
let hpfar = cca_exit.hpfar_el2();
let fipa = (hpfar.fipa() << 12) | (far & 0xfff);
Comment thread
Flgodd67 marked this conversation as resolved.
Comment on lines +376 to +396
let mut plane_state = mshv_rsi_get_ipa_state {
fipa,
state: u64::MAX,
};
if let Err(e) = this.ipa_state_read(GuestVtl::Vtl0, &mut plane_state) {
tracing::warn!(
error = ?e,
fipa,
"failed to read IPA state; state will be u8::MAX which is unavailable"
);
}

Comment on lines +397 to +408
let reason = InstructionAbortReason::from(iss.ifsc());
Comment on lines +397 to +409
Comment on lines +397 to +409
Comment on lines +376 to +409

return Err(dev.fatal_error(
CcaUnsupportedExit::InstructionAbort {
esr_el2: cca_exit.0.esr_el2,
Comment on lines +374 to +413
elr_el2: cca_exit.elr_el2(),
far_el2: cca_exit.far_el2(),
fipa,
fipa_state: plane_state.state as u8,
ifsc: iss.ifsc().0,
reason,
far_not_valid: iss.fnv(),
}
.into(),
));
}
ExceptionClass::SimdAccess => {
this.runner.cca_plane_no_trap_simd();
Expand Down Expand Up @@ -448,6 +523,16 @@ impl UhProcessor<'_, CcaBacked> {
self.runner.cca_sysreg_read(vtl, reg, val)
}

fn ipa_state_read(
&self,
vtl: GuestVtl,
state: &mut mshv_rsi_get_ipa_state,
) -> Result<(), Error> {
self.runner
.cca_ipa_state_read(vtl, state)
.map_err(Error::Hcl)
}

fn set_plane_enter(&mut self) {
self.runner.cca_set_plane_enter();
}
Expand Down
1 change: 1 addition & 0 deletions tmk/simple_tmk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ edition.workspace = true

[dependencies]
tmk_core.workspace = true
tmk_protocol.workspace = true
tmk_macros.workspace = true

[target.'cfg(target_arch = "x86_64")'.dependencies]
Expand Down
84 changes: 84 additions & 0 deletions tmk/simple_tmk/src/aarch64/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#![cfg(all(paravisor_kernel = "linux", target_arch = "aarch64"))]
#![allow(
unsafe_code,
reason = "global_asm! required for AArch64 trampoline code"
)]

use crate::prelude::*;
use tmk_protocol as _;

core::arch::global_asm! {
".global instruction_abort_outside_par_entry",
"instruction_abort_outside_par_entry:",
"movz x16, #0x0000",
"movk x16, #0x0000, lsl #16",
"movk x16, #0xffff, lsl #32",
"movk x16, #0x0000, lsl #48",
"br x16",
}

unsafe extern "C" {
fn instruction_abort_outside_par_entry() -> !;
}

#[tmk_test(expected_failure)]
fn instruction_abort_outside_par(_: TestContext<'_>) {
log!("instruction_abort_outside_par");

// SAFETY: This test intentionally jumps to an assembly entry point that
// triggers an instruction abort. The symbol is defined in this module via
// `global_asm!` and is declared `-> !`, so it is not expected to return.
unsafe {
instruction_abort_outside_par_entry();
}
}

core::arch::global_asm! {
".global instruction_abort_ripas_empty_entry",
"instruction_abort_ripas_empty_entry:",
"movz x16, #0x0000",
"br x16",
}

unsafe extern "C" {
fn instruction_abort_ripas_empty_entry() -> !;
}

#[tmk_test(expected_failure)]
fn instruction_abort_ripas_empty(_: TestContext<'_>) {
log!("instruction_abort_ripas_empty");

// SAFETY: This test intentionally transfers control to an assembly entry
// point that executes from an address chosen to provoke the expected
// instruction abort. The entry point is defined above and never returns.
unsafe {
instruction_abort_ripas_empty_entry();
}
}

core::arch::global_asm! {
".global instruction_abort_permissions_enabled_entry",
"instruction_abort_permissions_enabled_entry:",
"movz x16, #0xf000",
"movk x16, #0x847f, lsl #16",
"br x16",
}

unsafe extern "C" {
fn instruction_abort_permissions_enabled_entry() -> !;
}

#[tmk_test(expected_failure)]
fn instruction_abort_permissions_enabled(_: TestContext<'_>) {
log!("instruction_abort_permissions_enabled");

// SAFETY: This test intentionally calls an assembly entry point that jumps
// to an address expected to fault under the configured permissions. The
// entry point is defined in this module and is declared `-> !`.
unsafe {
instruction_abort_permissions_enabled_entry();
}
}
1 change: 1 addition & 0 deletions tmk/simple_tmk/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

mod prelude;

mod aarch64;
mod common;
mod x86_64;

Expand Down
Loading
Loading