From a76b9024867f3d67df587bf2137d7bfeca53ae60 Mon Sep 17 00:00:00 2001 From: Wojciech Ozga Date: Mon, 13 Jan 2025 17:32:25 -0600 Subject: [PATCH] Emulate mtinst when not provided by hardware Signed-off-by: Wojciech Ozga --- Makefile | 1 + .../hypervisor_rootfs/run_linux_vm.sh | 2 +- .../handlers/mmio/mmio_load_request.rs | 53 ++++++++++++++----- .../handlers/mmio/mmio_load_response.rs | 3 +- .../handlers/mmio/mmio_store_request.rs | 53 ++++++++++++------- .../confidential_flow/handlers/mmio/mod.rs | 45 ++++++++++++++++ .../extensions/compressed_instructions.rs | 17 +----- .../core/architecture/riscv/specification.rs | 42 +++++++++++++++ tools/ace_run_hypervisor.sh | 2 +- 9 files changed, 165 insertions(+), 53 deletions(-) diff --git a/Makefile b/Makefile index a96a216..57a03e8 100644 --- a/Makefile +++ b/Makefile @@ -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: diff --git a/confidential-vms/linux_vm/hypervisor_rootfs/run_linux_vm.sh b/confidential-vms/linux_vm/hypervisor_rootfs/run_linux_vm.sh index 9bfd645..7697969 100755 --- a/confidential-vms/linux_vm/hypervisor_rootfs/run_linux_vm.sh +++ b/confidential-vms/linux_vm/hypervisor_rootfs/run_linux_vm.sh @@ -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 diff --git a/security-monitor/src/confidential_flow/handlers/mmio/mmio_load_request.rs b/security-monitor/src/confidential_flow/handlers/mmio/mmio_load_request.rs index 4bc10d1..26cb4c4 100644 --- a/security-monitor/src/confidential_flow/handlers/mmio/mmio_load_request.rs +++ b/security-monitor/src/confidential_flow/handlers/mmio/mmio_load_request.rs @@ -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); } } diff --git a/security-monitor/src/confidential_flow/handlers/mmio/mmio_load_response.rs b/security-monitor/src/confidential_flow/handlers/mmio/mmio_load_response.rs index f33cc8c..2df6cc7 100644 --- a/security-monitor/src/confidential_flow/handlers/mmio/mmio_load_response.rs +++ b/security-monitor/src/confidential_flow/handlers/mmio/mmio_load_response.rs @@ -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 } } diff --git a/security-monitor/src/confidential_flow/handlers/mmio/mmio_store_request.rs b/security-monitor/src/confidential_flow/handlers/mmio/mmio_store_request.rs index bf9cc50..2c5e56c 100644 --- a/security-monitor/src/confidential_flow/handlers/mmio/mmio_store_request.rs +++ b/security-monitor/src/confidential_flow/handlers/mmio/mmio_store_request.rs @@ -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, - gpr_value: usize, + gpr_value: Result, } 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); } } diff --git a/security-monitor/src/confidential_flow/handlers/mmio/mod.rs b/security-monitor/src/confidential_flow/handlers/mmio/mod.rs index f047f61..e88f3d5 100644 --- a/security-monitor/src/confidential_flow/handlers/mmio/mod.rs +++ b/security-monitor/src/confidential_flow/handlers/mmio/mod.rs @@ -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) + } + } +} diff --git a/security-monitor/src/core/architecture/riscv/extensions/compressed_instructions.rs b/security-monitor/src/core/architecture/riscv/extensions/compressed_instructions.rs index ee3ecfd..5598cf7 100644 --- a/security-monitor/src/core/architecture/riscv/extensions/compressed_instructions.rs +++ b/security-monitor/src/core/architecture/riscv/extensions/compressed_instructions.rs @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , 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