Skip to content

Commit

Permalink
Emulate mtinst when not provided by hardware
Browse files Browse the repository at this point in the history
Signed-off-by: Wojciech Ozga <[email protected]>
wojciechozga committed Jan 13, 2025
1 parent 56cf466 commit a76b902
Showing 9 changed files with 165 additions and 53 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -87,6 +87,7 @@ emulator: setup devtools
tools: setup
mkdir -p $(TOOLS_WORK_DIR) ;\
cp -rf $(TOOLS_SOURCE_DIR)/*.sh $(TOOLS_WORK_DIR)/ ;\
cp -rf $(TOOLS_SOURCE_DIR)/ace $(TOOLS_WORK_DIR)/ ;\
PATH="$(RISCV_GNU_TOOLCHAIN_WORK_DIR)/bin:$(PATH)" TOOLS_WORK_DIR=$(TOOLS_WORK_DIR) ACE_DIR=$(ACE_DIR) $(MAKE) -C tools/cove_tap_tool;

verify:
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ INITRAMFS=/root/linux_vm/rootfs.cpio

HOST_PORT="$((3000 + RANDOM % 3000))"
INTERACTIVE="-nographic"
SMP=2
SMP=1
MEMORY=1G

for i in "$@"; do
Original file line number Diff line number Diff line change
@@ -4,8 +4,8 @@
use crate::confidential_flow::handlers::mmio::{MmioAccessFault, MmioLoadPending};
use crate::confidential_flow::handlers::sbi::SbiResponse;
use crate::confidential_flow::{ApplyToConfidentialHart, ConfidentialFlow};
use crate::core::architecture::is_bit_enabled;
use crate::core::architecture::specification::CAUSE_LOAD_ACCESS;
use crate::core::architecture::specification::{CAUSE_LOAD_ACCESS, *};
use crate::core::architecture::GeneralPurposeRegister;
use crate::core::control_data::{ConfidentialHart, HypervisorHart, ResumableOperation};
use crate::non_confidential_flow::DeclassifyToHypervisor;

@@ -14,34 +14,32 @@ pub struct MmioLoadRequest {
mcause: usize,
mtval: usize,
mtval2: usize,
mtinst: usize,
instruction: usize,
instruction_length: usize,
}

impl MmioLoadRequest {
pub fn from_confidential_hart(confidential_hart: &ConfidentialHart) -> Self {
let (instruction, instruction_length) = super::read_trapped_instruction(confidential_hart);
Self {
mcause: confidential_hart.csrs().mcause.read(),
mtval: confidential_hart.csrs().mtval.read(),
mtval2: confidential_hart.csrs().mtval2.read(),
mtinst: confidential_hart.csrs().mtinst.read(),
instruction,
instruction_length,
}
}

pub fn handle(self, confidential_flow: ConfidentialFlow) -> ! {
// According to the RISC-V privilege spec, mtinst encodes faulted instruction (bit 0 is 1) or a pseudo instruction
assert!(self.mtinst & 0x1 > 0);
let instruction = self.mtinst | 0x3;
let instruction_length = if is_bit_enabled(self.mtinst, 1) { riscv_decode::instruction_length(instruction as u16) } else { 2 };

let fault_address = (self.mtval2 << 2) | (self.mtval & 0x3);
if !MmioAccessFault::tried_to_access_valid_mmio_region(confidential_flow.confidential_vm_id(), fault_address) {
let mmio_access_fault_handler = MmioAccessFault::new(CAUSE_LOAD_ACCESS.into(), self.mtval, instruction_length);
let mmio_access_fault_handler = MmioAccessFault::new(CAUSE_LOAD_ACCESS.into(), self.mtval, self.instruction_length);
confidential_flow.apply_and_exit_to_confidential_hart(ApplyToConfidentialHart::MmioAccessFault(mmio_access_fault_handler));
}

match crate::core::architecture::decode_result_register(instruction) {
match crate::core::architecture::decode_result_register(self.instruction) {
Ok(gpr) => confidential_flow
.set_resumable_operation(ResumableOperation::MmioLoad(MmioLoadPending::new(instruction_length, gpr)))
.set_resumable_operation(ResumableOperation::MmioLoad(MmioLoadPending::new(self.instruction_length, gpr)))
.into_non_confidential_flow()
.declassify_and_exit_to_hypervisor(DeclassifyToHypervisor::MmioLoadRequest(self)),
Err(error) => {
@@ -52,12 +50,39 @@ impl MmioLoadRequest {
}

pub fn declassify_to_hypervisor_hart(&self, hypervisor_hart: &mut HypervisorHart) {
use crate::core::architecture::riscv::specification::*;
let mut mtinst = if self.instruction & INSN_MASK_LB == INSN_MATCH_LB {
INSN_MATCH_LB
} else if self.instruction & INSN_MASK_LBU == INSN_MATCH_LBU {
INSN_MATCH_LBU
} else if self.instruction & INSN_MASK_LH == INSN_MATCH_LH {
INSN_MATCH_LH
} else if self.instruction & INSN_MASK_LHU == INSN_MATCH_LHU {
INSN_MATCH_LHU
} else if self.instruction & INSN_MASK_LW == INSN_MATCH_LW {
INSN_MATCH_LW
} else if self.instruction & INSN_MASK_C_LW == INSN_MATCH_C_LW {
INSN_MATCH_LW
} else if self.instruction & INSN_MASK_LWU == INSN_MATCH_LWU {
INSN_MATCH_LWU
} else if self.instruction & INSN_MASK_LD == INSN_MATCH_LD {
INSN_MATCH_LD
} else if self.instruction & INSN_MASK_C_LD == INSN_MATCH_C_LD {
INSN_MATCH_LD
} else {
debug!("Not supported load instruction {:x}", self.instruction);
0
};

mtinst = mtinst | ((GeneralPurposeRegister::a0 as usize) << 7) | 1 << 0;
if self.instruction_length != 2 {
mtinst = mtinst | 1 << 1;
}

// The security monitor exposes `scause` and `stval` via hart's CSRs but `htval` and `htinst` via the NACL shared memory.
hypervisor_hart.csrs_mut().scause.write(self.mcause);
hypervisor_hart.csrs_mut().stval.write(self.mtval);
hypervisor_hart.shared_memory_mut().write_csr(CSR_HTVAL.into(), self.mtval2);
hypervisor_hart.shared_memory_mut().write_csr(CSR_HTINST.into(), self.mtinst);
hypervisor_hart.shared_memory_mut().write_csr(CSR_HTINST.into(), mtinst);
SbiResponse::success().declassify_to_hypervisor_hart(hypervisor_hart);
}
}
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::confidential_flow::handlers::mmio::MmioLoadPending;
use crate::confidential_flow::{ConfidentialFlow, DeclassifyToConfidentialVm};
use crate::core::architecture::GeneralPurposeRegister;
use crate::core::control_data::{ConfidentialHart, HypervisorHart};

pub struct MmioLoadResponse {
@@ -12,7 +13,7 @@ pub struct MmioLoadResponse {

impl MmioLoadResponse {
pub fn from_hypervisor_hart(hypervisor_hart: &HypervisorHart, request: MmioLoadPending) -> Self {
let value = hypervisor_hart.shared_memory().gpr(request.gpr_storing_load_result());
let value = hypervisor_hart.shared_memory().gpr(GeneralPurposeRegister::a0);
Self { value, request }
}

Original file line number Diff line number Diff line change
@@ -4,8 +4,8 @@
use crate::confidential_flow::handlers::mmio::{MmioAccessFault, MmioStorePending};
use crate::confidential_flow::handlers::sbi::SbiResponse;
use crate::confidential_flow::{ApplyToConfidentialHart, ConfidentialFlow};
use crate::core::architecture::specification::CAUSE_STORE_ACCESS;
use crate::core::architecture::{is_bit_enabled, GeneralPurposeRegister};
use crate::core::architecture::specification::{CAUSE_STORE_ACCESS, *};
use crate::core::architecture::GeneralPurposeRegister;
use crate::core::control_data::{ConfidentialHart, HypervisorHart, ResumableOperation};
use crate::error::Error;
use crate::non_confidential_flow::DeclassifyToHypervisor;
@@ -15,28 +15,20 @@ pub struct MmioStoreRequest {
mcause: usize,
mtval: usize,
mtval2: usize,
mtinst: usize,
instruction: usize,
instruction_length: usize,
gpr: Result<GeneralPurposeRegister, Error>,
gpr_value: usize,
gpr_value: Result<usize, Error>,
}

impl MmioStoreRequest {
pub fn from_confidential_hart(confidential_hart: &ConfidentialHart) -> Self {
let mcause = confidential_hart.csrs().mcause.read();
let mtinst = confidential_hart.csrs().mtinst.read();
let mtval = confidential_hart.csrs().mtval.read();
let mtval2 = confidential_hart.csrs().mtval2.read();

// According to the RISC-V privilege spec, mtinst encodes faulted instruction when bit 0 is 1.
// Otherwise it is a pseudo instruction.
assert!(mtinst & 0x1 > 0);
let instruction = mtinst | 0x3;
let instruction_length = if is_bit_enabled(mtinst, 1) { riscv_decode::instruction_length(instruction as u16) } else { 2 };
let gpr = crate::core::architecture::decode_result_register(instruction);
let gpr_value = gpr.as_ref().and_then(|ref gpr| Ok(confidential_hart.gprs().read(**gpr))).unwrap_or(0);

Self { mcause, mtval, mtval2, mtinst, instruction_length, gpr, gpr_value }
let (instruction, instruction_length) = super::read_trapped_instruction(confidential_hart);
let gpr_value =
crate::core::architecture::decode_result_register(instruction).and_then(|gpr| Ok(confidential_hart.gprs().read(gpr)));
Self { mcause, mtval, mtval2, instruction, instruction_length, gpr_value }
}

pub fn handle(self, confidential_flow: ConfidentialFlow) -> ! {
@@ -46,7 +38,7 @@ impl MmioStoreRequest {
confidential_flow.apply_and_exit_to_confidential_hart(ApplyToConfidentialHart::MmioAccessFault(mmio_access_fault_handler));
}

match self.gpr {
match self.gpr_value {
Ok(_) => confidential_flow
.set_resumable_operation(ResumableOperation::MmioStore(MmioStorePending::new(self.instruction_length)))
.into_non_confidential_flow()
@@ -59,12 +51,33 @@ impl MmioStoreRequest {
}

pub fn declassify_to_hypervisor_hart(&self, hypervisor_hart: &mut HypervisorHart) {
use crate::core::architecture::riscv::specification::*;
let mut mtinst = if self.instruction & INSN_MASK_SB == INSN_MATCH_SB {
INSN_MATCH_SB
} else if self.instruction & INSN_MASK_SH == INSN_MATCH_SH {
INSN_MATCH_SH
} else if self.instruction & INSN_MASK_SW == INSN_MATCH_SW {
INSN_MATCH_SW
} else if self.instruction & INSN_MASK_C_SW == INSN_MATCH_C_SW {
INSN_MATCH_SW
} else if self.instruction & INSN_MASK_SD == INSN_MATCH_SD {
INSN_MATCH_SD
} else if self.instruction & INSN_MASK_C_SD == INSN_MATCH_C_SD {
INSN_MATCH_SD
} else {
debug!("Not supported store instruction {:x}", self.instruction);
0
};

mtinst |= ((GeneralPurposeRegister::a0 as usize) << 20) | 1 << 0;
if self.instruction_length != 2 {
mtinst |= 1 << 1;
}

hypervisor_hart.csrs_mut().scause.write(self.mcause);
hypervisor_hart.csrs_mut().stval.write(self.mtval);
hypervisor_hart.shared_memory_mut().write_gpr(*self.gpr.as_ref().unwrap_or(&GeneralPurposeRegister::zero), self.gpr_value);
hypervisor_hart.shared_memory_mut().write_gpr(GeneralPurposeRegister::a0, *self.gpr_value.as_ref().unwrap_or(&0));
hypervisor_hart.shared_memory_mut().write_csr(CSR_HTVAL.into(), self.mtval2);
hypervisor_hart.shared_memory_mut().write_csr(CSR_HTINST.into(), self.mtinst);
hypervisor_hart.shared_memory_mut().write_csr(CSR_HTINST.into(), mtinst);
SbiResponse::success().declassify_to_hypervisor_hart(hypervisor_hart);
}
}
45 changes: 45 additions & 0 deletions security-monitor/src/confidential_flow/handlers/mmio/mod.rs
Original file line number Diff line number Diff line change
@@ -20,3 +20,48 @@ mod mmio_store_pending;
mod mmio_store_request;
mod mmio_store_response;
mod remove_mmio_region;

core::arch::global_asm!(
r#"
.attribute arch, "rv64gc"
.option norvc
.text
.align 4
.global _load_u64_from_confidential_vm_memory
_load_u64_from_confidential_vm_memory:
lui a1, 0xa0
csrs mstatus, a1
ld a2, (a0)
csrc mstatus, a1
addi a0, a2, 0
ret
"#
);

extern "C" {
fn _load_u64_from_confidential_vm_memory(address: usize) -> usize;
}

fn read_trapped_instruction(confidential_hart: &crate::core::control_data::ConfidentialHart) -> (usize, usize) {
match confidential_hart.csrs().mtinst.read() {
0 => {
let guest_virtual_address = confidential_hart.csrs().mepc.read_from_main_memory();
let mut low = unsafe { _load_u64_from_confidential_vm_memory(guest_virtual_address & !7) };
low = low >> (8 * (guest_virtual_address & 7));
let instruction_length = riscv_decode::instruction_length(low as u16);
let mut high = 0;
if instruction_length > (8 - (guest_virtual_address & 7)) {
high = unsafe { _load_u64_from_confidential_vm_memory((guest_virtual_address + 7) & !7) };
high = high << (8 * (8 - (guest_virtual_address & 7)));
}
(low | high, instruction_length)
}
mtinst => {
let instruction = mtinst | 0x3;
let instruction_length =
if crate::core::architecture::is_bit_enabled(mtinst, 1) { riscv_decode::instruction_length(instruction as u16) } else { 2 };
(instruction, instruction_length)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: 2023 IBM Corporation
// SPDX-FileContributor: Wojciech Ozga <[email protected]>, IBM Research - Zurich
// SPDX-License-Identifier: Apache-2.0
use crate::core::architecture::specification::*;
use crate::core::architecture::GeneralPurposeRegister;
use crate::error::Error;

@@ -24,22 +25,6 @@ pub fn decode_result_register(mtinst: usize) -> Result<GeneralPurposeRegister, E
// section once compressed instructions are supported in the
// rust-decode crate!
const SH_RS2C: usize = 2;
const INSN_MATCH_C_LD: usize = 0x6000;
const INSN_MASK_C_LD: usize = 0xe003;
const INSN_MATCH_C_SD: usize = 0xe000;
const INSN_MASK_C_SD: usize = 0xe003;
const INSN_MATCH_C_LW: usize = 0x4000;
const INSN_MASK_C_LW: usize = 0xe003;
const INSN_MATCH_C_SW: usize = 0xc000;
const INSN_MASK_C_SW: usize = 0xe003;
const INSN_MATCH_C_LDSP: usize = 0x6002;
const INSN_MASK_C_LDSP: usize = 0xe003;
const INSN_MATCH_C_SDSP: usize = 0xe002;
const INSN_MASK_C_SDSP: usize = 0xe003;
const INSN_MATCH_C_LWSP: usize = 0x4002;
const INSN_MASK_C_LWSP: usize = 0xe003;
const INSN_MATCH_C_SWSP: usize = 0xc002;
const INSN_MASK_C_SWSP: usize = 0xe003;

let log_regbytes = 3; // for 64b!
let shift_right = |x: usize, y: isize| {
42 changes: 42 additions & 0 deletions security-monitor/src/core/architecture/riscv/specification.rs
Original file line number Diff line number Diff line change
@@ -430,3 +430,45 @@ pub const SR_FS_DIRTY: usize = 0x00006000;

pub const HCOUNTEREN_TM: usize = 1;
pub const HCOUNTEREN_TM_MASK: usize = 1 << HCOUNTEREN_TM;

// Compressed extension
pub const INSN_MATCH_C_LD: usize = 0x6000;
pub const INSN_MASK_C_LD: usize = 0xe003;
pub const INSN_MATCH_C_SD: usize = 0xe000;
pub const INSN_MASK_C_SD: usize = 0xe003;
pub const INSN_MATCH_C_LW: usize = 0x4000;
pub const INSN_MASK_C_LW: usize = 0xe003;
pub const INSN_MATCH_C_SW: usize = 0xc000;
pub const INSN_MASK_C_SW: usize = 0xe003;
pub const INSN_MATCH_C_LDSP: usize = 0x6002;
pub const INSN_MASK_C_LDSP: usize = 0xe003;
pub const INSN_MATCH_C_SDSP: usize = 0xe002;
pub const INSN_MASK_C_SDSP: usize = 0xe003;
pub const INSN_MATCH_C_LWSP: usize = 0x4002;
pub const INSN_MASK_C_LWSP: usize = 0xe003;
pub const INSN_MATCH_C_SWSP: usize = 0xc002;
pub const INSN_MASK_C_SWSP: usize = 0xe003;

pub const INSN_MATCH_LB: usize = 0x3;
pub const INSN_MASK_LB: usize = 0x707f;
pub const INSN_MATCH_LH: usize = 0x1003;
pub const INSN_MASK_LH: usize = 0x707f;
pub const INSN_MATCH_LW: usize = 0x2003;
pub const INSN_MASK_LW: usize = 0x707f;
pub const INSN_MATCH_LBU: usize = 0x4003;
pub const INSN_MASK_LBU: usize = 0x707f;
pub const INSN_MATCH_LHU: usize = 0x5003;
pub const INSN_MASK_LHU: usize = 0x707f;
pub const INSN_MATCH_SB: usize = 0x23;
pub const INSN_MASK_SB: usize = 0x707f;
pub const INSN_MATCH_SH: usize = 0x1023;
pub const INSN_MASK_SH: usize = 0x707f;
pub const INSN_MATCH_SW: usize = 0x2023;
pub const INSN_MASK_SW: usize = 0x707f;

pub const INSN_MATCH_LD: usize = 0x3003;
pub const INSN_MASK_LD: usize = 0x707f;
pub const INSN_MATCH_LWU: usize = 0x6003;
pub const INSN_MASK_LWU: usize = 0x707f;
pub const INSN_MATCH_SD: usize = 0x3023;
pub const INSN_MASK_SD: usize = 0x707f;
2 changes: 1 addition & 1 deletion tools/ace_run_hypervisor.sh
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ DRIVE=${ACE_DIR}/hypervisor/buildroot/images/rootfs.ext4

HOST_PORT="$((3000 + RANDOM % 3000))"
INTERACTIVE="-nographic"
SMP=2
SMP=1
MEMORY=8G

for i in "$@"; do

0 comments on commit a76b902

Please sign in to comment.