diff --git a/confidential-vms/baremetal/src/uart.rs b/confidential-vms/baremetal/src/uart.rs index 0d510fa..f55db2d 100644 --- a/confidential-vms/baremetal/src/uart.rs +++ b/confidential-vms/baremetal/src/uart.rs @@ -46,6 +46,7 @@ impl Uart { } pub fn println(&mut self, out: &str) { + use alloc::format; crate::sync::acquire(crate::sync::UART_SYNC_ADDRESS); for c in out.bytes() { self.put(c); diff --git a/confidential-vms/baremetal/src/worker.rs b/confidential-vms/baremetal/src/worker.rs index 0500708..c931856 100644 --- a/confidential-vms/baremetal/src/worker.rs +++ b/confidential-vms/baremetal/src/worker.rs @@ -28,6 +28,10 @@ extern "C" fn worker_init(hart_id: usize) { riscv::register::sstatus::set_sie(); } + loop { + uart.println(&format!("Hello from hart id: {}", hart_id)); + } + sbi::system_reset::system_reset( sbi::system_reset::ResetType::Shutdown, sbi::system_reset::ResetReason::NoReason, diff --git a/security-monitor/src/confidential_flow/control_flow/mod.rs b/security-monitor/src/confidential_flow/control_flow/mod.rs index 060cc2d..55998c7 100644 --- a/security-monitor/src/confidential_flow/control_flow/mod.rs +++ b/security-monitor/src/confidential_flow/control_flow/mod.rs @@ -1,9 +1,11 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 +use crate::core::architecture::HartLifecycleStateTransition; use crate::core::architecture::SbiExtension::*; use crate::core::control_data::{ConfidentialVmId, ControlData, HardwareHart}; use crate::core::transformations::{ExposeToConfidentialVm, PendingRequest}; +use crate::error::Error; use crate::non_confidential_flow::NonConfidentialFlow; extern "C" { @@ -60,9 +62,11 @@ impl<'a> ConfidentialFlow<'a> { } VsEcall(Rfence(_)) => invalid_call::handle(self), VsEcall(Hsm(HartStart)) => sbi_hsm_hart_start::handle(confidential_hart.sbi_hsm_hart_start(), self), + VsEcall(Hsm(HartStop)) => sbi_hsm_hart_stop::handle(self), + VsEcall(Hsm(HartSuspend)) => sbi_hsm_hart_suspend::handle(confidential_hart.sbi_hsm_hart_suspend(), self), VsEcall(Hsm(HartGetStatus)) => hypercall::handle(confidential_hart.hypercall_request(), self), VsEcall(Ipi(SendIpi)) => inter_hart_request::handle(confidential_hart.sbi_ipi(), self), - VsEcall(Srst(SystemReset)) => hypercall::handle(confidential_hart.hypercall_request(), self), + VsEcall(Srst(SystemReset)) => sbi_srst::handle(confidential_hart.hypercall_request(), self), VsEcall(_) => invalid_call::handle(self), GuestLoadPageFault => { guest_load_page_fault::handle(confidential_hart.guest_load_page_fault_request(), self) @@ -117,6 +121,10 @@ impl<'a> ConfidentialFlow<'a> { } Some(GuestStorePageFault(request)) => guest_store_page_fault_result::handle(self, request), Some(SharePage(request)) => share_page_result::handle(self.hart.share_page_result(), self, request), + Some(SbiHsmHartStart()) => self.exit_to_confidential_vm(ExposeToConfidentialVm::SbiHsmHartStart()), + Some(SbiHsmHartStartPending()) => { + self.exit_to_confidential_vm(ExposeToConfidentialVm::SbiHsmHartStartPending()) + } None => self.exit_to_confidential_vm(ExposeToConfidentialVm::Resume()), } } @@ -151,6 +159,22 @@ impl<'a> ConfidentialFlow<'a> { self.hart.confidential_hart().confidential_hart_id() } + pub fn transit_hart_lifecycle(&mut self, state_transition: HartLifecycleStateTransition) -> Result<(), Error> { + use crate::core::architecture::HartLifecycleStateTransition::*; + match state_transition { + StoppedToStartPending(request) => { + ControlData::try_confidential_vm_mut(self.confidential_vm_id(), |ref mut confidential_vm| { + confidential_vm.transit_confidential_hart_to_start_pending(request) + }) + } + StartedToSuspended(request) => { + self.hart.confidential_hart_mut().transition_from_started_to_suspended(request) + } + SuspendedToStarted() => self.hart.confidential_hart_mut().transition_from_suspended_to_started(), + StartedToStopped() => self.hart.confidential_hart_mut().transition_from_started_to_stopped(), + } + } + pub fn set_pending_request(self, request: PendingRequest) -> Self { if let Err(error) = self.hart.confidential_hart_mut().set_pending_request(request) { self.exit_to_confidential_vm(error.into_confidential_transformation()); diff --git a/security-monitor/src/confidential_flow/handlers/mod.rs b/security-monitor/src/confidential_flow/handlers/mod.rs index 9b0c335..b94b805 100644 --- a/security-monitor/src/confidential_flow/handlers/mod.rs +++ b/security-monitor/src/confidential_flow/handlers/mod.rs @@ -11,5 +11,8 @@ pub mod inter_hart_request; pub mod interrupt; pub mod invalid_call; pub mod sbi_hsm_hart_start; +pub mod sbi_hsm_hart_stop; +pub mod sbi_hsm_hart_suspend; +pub mod sbi_srst; pub mod share_page; pub mod share_page_result; diff --git a/security-monitor/src/confidential_flow/handlers/sbi_hsm_hart_start.rs b/security-monitor/src/confidential_flow/handlers/sbi_hsm_hart_start.rs index a2d3d9d..cb82e9e 100644 --- a/security-monitor/src/confidential_flow/handlers/sbi_hsm_hart_start.rs +++ b/security-monitor/src/confidential_flow/handlers/sbi_hsm_hart_start.rs @@ -2,21 +2,27 @@ // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 use crate::confidential_flow::ConfidentialFlow; -use crate::core::control_data::ControlData; -use crate::core::transformations::{ExposeToHypervisor, InterHartRequest, PendingRequest, SbiHsmHartStart, SbiRequest}; +use crate::core::architecture::HartLifecycleStateTransition; +use crate::core::transformations::{ExposeToHypervisor, PendingRequest, SbiHsmHartStart, SbiRequest}; -/// Handles HartStart function from the HSM extension of SBI. -pub fn handle(request: SbiHsmHartStart, confidential_flow: ConfidentialFlow) -> ! { +/// Starts a confidential hart. This is an implementation of the HartStart function from the HSM extension of SBI. +/// +/// This call is triggered by a confidential hart that wants to start another confidential hart. Error is returned to +/// the caller if the targetted confidential hart is not in the stopped state or it does not exist. The security monitor +/// moves targetted confidential harts into `StartPending` state and informs then the hypervisor that these harts are +/// runnable. Once the hypervisor schedules such a confidential hart for execution, the confidential hart will change +/// the state to `Started`. +pub fn handle(request: SbiHsmHartStart, mut confidential_flow: ConfidentialFlow) -> ! { let confidential_hart_id = request.confidential_hart_id; - // TODO: we must check the state of the hart and only allow starting a HART that is in the shutdown state - match ControlData::try_confidential_vm_mut(confidential_flow.confidential_vm_id(), |ref mut confidential_vm| { - Ok(confidential_vm.add_inter_hart_request(InterHartRequest::SbiHsmHartStart(request))) - }) { + match confidential_flow.transit_hart_lifecycle(HartLifecycleStateTransition::StoppedToStartPending(request)) { Ok(_) => confidential_flow - .set_pending_request(PendingRequest::SbiRequest()) + .set_pending_request(PendingRequest::SbiHsmHartStart()) .into_non_confidential_flow() .exit_to_hypervisor(ExposeToHypervisor::SbiRequest(SbiRequest::kvm_hsm_hart_start(confidential_hart_id))), Err(error) => { + // starting a confidential hart might fail if the incoming request is invalid. For example, the confidential + // hart id does not exist or is the same as the one currently assigned to the hardware hart. In such cases, + // return an error to the calling confidential hart. confidential_flow.exit_to_confidential_vm(error.into_confidential_transformation()); } } diff --git a/security-monitor/src/confidential_flow/handlers/sbi_hsm_hart_stop.rs b/security-monitor/src/confidential_flow/handlers/sbi_hsm_hart_stop.rs new file mode 100644 index 0000000..a486d0e --- /dev/null +++ b/security-monitor/src/confidential_flow/handlers/sbi_hsm_hart_stop.rs @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2023 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 +use crate::confidential_flow::ConfidentialFlow; +use crate::core::architecture::HartLifecycleStateTransition; +use crate::core::transformations::{ExposeToHypervisor, SbiRequest}; + +/// Stops the confidential hart as defined in the HSM extension of SBI. Error is returned to the confidential hart if +/// the security monitor cannot stop it, for example, because it is not in the started state. +/// +/// The request to stop the confidential hart comes from the confidential hart itself. The security monitor stops the +/// hart and informs the hypervisor that the hart has been stopped. The hypervisor should not resume execution of a +/// stopped confidential hart. Only another confidential hart of the confidential VM can start the confidential hart. +pub fn handle(mut confidential_flow: ConfidentialFlow) -> ! { + match confidential_flow.transit_hart_lifecycle(HartLifecycleStateTransition::StartedToStopped()) { + Ok(_) => confidential_flow + .into_non_confidential_flow() + .exit_to_hypervisor(ExposeToHypervisor::SbiRequest(SbiRequest::kvm_hsm_hart_stop())), + Err(error) => confidential_flow.exit_to_confidential_vm(error.into_confidential_transformation()), + } +} diff --git a/security-monitor/src/confidential_flow/handlers/sbi_hsm_hart_suspend.rs b/security-monitor/src/confidential_flow/handlers/sbi_hsm_hart_suspend.rs new file mode 100644 index 0000000..9f3904e --- /dev/null +++ b/security-monitor/src/confidential_flow/handlers/sbi_hsm_hart_suspend.rs @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2023 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 +use crate::confidential_flow::ConfidentialFlow; +use crate::core::architecture::HartLifecycleStateTransition; +use crate::core::transformations::{ExposeToHypervisor, SbiHsmHartSuspend, SbiRequest}; + +/// Suspends a confidential hart that made this request. This is an implementation of the HartSuspend function from the +/// HSM extension of SBI. +/// +/// The request to suspend the confidential hart comes frmo the confidential hart itself. The security monitor suspends +/// the confidential hart and informs about it the hypervisor. This functions returns an error to the calling +/// confidential hart if this confidential hart cannot be suspended, for example, because it is not in the started +/// state. +pub fn handle(request: SbiHsmHartSuspend, mut confidential_flow: ConfidentialFlow) -> ! { + match confidential_flow.transit_hart_lifecycle(HartLifecycleStateTransition::StartedToSuspended(request)) { + Ok(_) => confidential_flow + .into_non_confidential_flow() + .exit_to_hypervisor(ExposeToHypervisor::SbiRequest(SbiRequest::kvm_hsm_hart_suspend())), + Err(error) => confidential_flow.exit_to_confidential_vm(error.into_confidential_transformation()), + } +} diff --git a/security-monitor/src/confidential_flow/handlers/sbi_srst.rs b/security-monitor/src/confidential_flow/handlers/sbi_srst.rs new file mode 100644 index 0000000..473073b --- /dev/null +++ b/security-monitor/src/confidential_flow/handlers/sbi_srst.rs @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2023 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 +use crate::confidential_flow::ConfidentialFlow; +use crate::core::control_data::ControlData; +use crate::core::transformations::{ExposeToHypervisor, SbiRequest}; + +pub fn handle(request: SbiRequest, confidential_flow: ConfidentialFlow) -> ! { + let confidential_vm_id = confidential_flow.confidential_vm_id(); + match ControlData::try_write(|control_data| control_data.remove_confidential_vm(confidential_vm_id)) { + Ok(_) => { + confidential_flow.into_non_confidential_flow().exit_to_hypervisor(ExposeToHypervisor::SbiRequest(request)) + } + Err(error) => confidential_flow.exit_to_confidential_vm(error.into_confidential_transformation()), + } +} diff --git a/security-monitor/src/confidential_flow/handlers/share_page.rs b/security-monitor/src/confidential_flow/handlers/share_page.rs index e66e61d..4913988 100644 --- a/security-monitor/src/confidential_flow/handlers/share_page.rs +++ b/security-monitor/src/confidential_flow/handlers/share_page.rs @@ -14,7 +14,7 @@ pub fn handle(request: Result<(SharePageRequest, SbiRequest), Error>, confidenti match request { Ok((share_page_request, sbi_request)) => { debug!( - "Confidential VM[confidential_vm_id={:?}] requested a shared page mapped to address [guest_physical_address={:?}]", + "Confidential VM[{:?}] requested a shared page mapped to address [{:?}]", confidential_flow.confidential_vm_id(), share_page_request.confidential_vm_virtual_address() ); diff --git a/security-monitor/src/confidential_flow/handlers/share_page_result.rs b/security-monitor/src/confidential_flow/handlers/share_page_result.rs index c87c4ee..f3bf2ef 100644 --- a/security-monitor/src/confidential_flow/handlers/share_page_result.rs +++ b/security-monitor/src/confidential_flow/handlers/share_page_result.rs @@ -25,8 +25,9 @@ pub fn handle(share_page_result: SharePageResult, confidential_flow: Confidentia }; debug!( - "Hypervisor shared a page with the confidential VM [confidential_vm_id={:?}] at address [physical address=0x{:x}]", - confidential_vm_id, share_page_result.hypervisor_page_address() + "Hypervisor shared a page with the confidential VM [{:?}] at address [physical address=0x{:x}]", + confidential_vm_id, + share_page_result.hypervisor_page_address() ); let transformation = ControlData::try_confidential_vm_mut(confidential_vm_id, |mut confidential_vm| { diff --git a/security-monitor/src/core/architecture/mod.rs b/security-monitor/src/core/architecture/mod.rs index 294e0d9..7d1b733 100644 --- a/security-monitor/src/core/architecture/mod.rs +++ b/security-monitor/src/core/architecture/mod.rs @@ -2,8 +2,9 @@ // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 pub use riscv::{ - decode_result_register, AceExtension, BaseExtension, FpRegisters, GpRegister, GpRegisters, HartState, HsmExtension, - IpiExtension, RfenceExtension, SbiExtension, SrstExtension, TrapReason, + decode_result_register, AceExtension, BaseExtension, FpRegisters, GpRegister, GpRegisters, HartArchitecturalState, + HartLifecycleState, HartLifecycleStateTransition, HsmExtension, IpiExtension, RfenceExtension, SbiExtension, + SrstExtension, TrapReason, }; mod riscv; diff --git a/security-monitor/src/core/architecture/riscv/hart_state.rs b/security-monitor/src/core/architecture/riscv/hart_architectural_state.rs similarity index 83% rename from security-monitor/src/core/architecture/riscv/hart_state.rs rename to security-monitor/src/core/architecture/riscv/hart_architectural_state.rs index 9a66a81..dac7ac6 100644 --- a/security-monitor/src/core/architecture/riscv/hart_state.rs +++ b/security-monitor/src/core/architecture/riscv/hart_architectural_state.rs @@ -3,11 +3,11 @@ // SPDX-License-Identifier: Apache-2.0 use crate::core::architecture::{FpRegisters, GpRegister, GpRegisters, TrapReason}; -/// HartState is the dump state of the processor's core, called in RISC-V a hardware thread (HART). +/// HartArchitecturalState is the dump state of the processor's core, called in RISC-V a hardware thread (HART). #[repr(C)] -pub struct HartState { - // gprs must be the first element in this structure because it is used to calculate the HartState address in the - // memory. This address is used by the assembly code. +pub struct HartArchitecturalState { + // gprs must be the first element in this structure because it is used to calculate the HartArchitecturalState + // address in the memory. This address is used by the assembly code. pub gprs: GpRegisters, // floating-point related pub fprs: FpRegisters, @@ -54,9 +54,9 @@ pub struct HartState { pub mtval2: usize, } -impl HartState { - pub fn from_existing(id: usize, existing: &HartState) -> HartState { - HartState { +impl HartArchitecturalState { + pub fn from_existing(id: usize, existing: &HartArchitecturalState) -> HartArchitecturalState { + HartArchitecturalState { id, gprs: existing.gprs.clone(), // M-mode @@ -102,8 +102,8 @@ impl HartState { } } - pub fn empty(id: usize) -> HartState { - HartState { + pub fn empty(id: usize) -> HartArchitecturalState { + HartArchitecturalState { id, gprs: GpRegisters::empty(), sstatus: 0, @@ -145,7 +145,7 @@ impl HartState { } } -impl HartState { +impl HartArchitecturalState { pub fn gpr(&self, register: GpRegister) -> usize { self.gprs.get(register) } @@ -154,12 +154,33 @@ impl HartState { self.gprs.set(register, value) } + pub fn reset(&mut self) { + self.gprs = GpRegisters::empty(); + self.fprs = FpRegisters::empty(); + self.fcsr = 0; + self.htinst = 0; + self.htval = 0; + self.sepc = 0; + self.scounteren = 0; + self.vsie = 0; + self.vstvec = 0; + self.vsscratch = 0; + self.vsepc = 0; + self.vscause = 0; + self.vstval = 0; + self.hvip = 0; + self.vsatp = 0; + // TODO: what should be the sstatus on reset? + // self.sstatus = 0; + self.vsstatus = 0; + } + pub fn trap_reason(&self) -> TrapReason { TrapReason::from_hart_state(self) } } -impl core::fmt::Debug for HartState { +impl core::fmt::Debug for HartArchitecturalState { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { write!(f, "hart_id: {}, ", self.id)?; GpRegisters::iter().try_for_each(|x| -> core::fmt::Result { diff --git a/security-monitor/src/core/architecture/riscv/hart_lifecycle_state.rs b/security-monitor/src/core/architecture/riscv/hart_lifecycle_state.rs new file mode 100644 index 0000000..d72d447 --- /dev/null +++ b/security-monitor/src/core/architecture/riscv/hart_lifecycle_state.rs @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2023 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 + +/// Hart lifecycle states as documented in the SBI specification of the HSM extension. +#[derive(PartialEq)] +pub enum HartLifecycleState { + Started, + Stopped, + StartPending, + // + // StopPending is never used because the security monitor stops the hart directly and only informs a hypervisor + // about it for the bookkeeping pourposes. + // StopPending, + Suspended, + // + // SuspendPending is never used because the security monitor stops the hart directly and only informs a hypervisor + // about it for the bookkeeping pourposes. + // SuspendPending, + // + // ResumePending is never used because the security monitor stops the hart directly and only informs a hypervisor + // about it for the bookkeeping pourposes. + // ResumePending, +} diff --git a/security-monitor/src/core/architecture/riscv/hart_lifecycle_state_transition.rs b/security-monitor/src/core/architecture/riscv/hart_lifecycle_state_transition.rs new file mode 100644 index 0000000..a7b4b27 --- /dev/null +++ b/security-monitor/src/core/architecture/riscv/hart_lifecycle_state_transition.rs @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2023 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 +use crate::core::transformations::{SbiHsmHartStart, SbiHsmHartSuspend}; + +pub enum HartLifecycleStateTransition { + StoppedToStartPending(SbiHsmHartStart), + StartedToSuspended(SbiHsmHartSuspend), + SuspendedToStarted(), + StartedToStopped(), +} diff --git a/security-monitor/src/core/architecture/riscv/mod.rs b/security-monitor/src/core/architecture/riscv/mod.rs index e8ada9b..83e5577 100644 --- a/security-monitor/src/core/architecture/riscv/mod.rs +++ b/security-monitor/src/core/architecture/riscv/mod.rs @@ -4,13 +4,17 @@ pub use compressed_instructions::decode_result_register; pub use fp_registers::FpRegisters; pub use gp_registers::{GpRegister, GpRegisters}; -pub use hart_state::HartState; +pub use hart_architectural_state::HartArchitecturalState; +pub use hart_lifecycle_state::HartLifecycleState; +pub use hart_lifecycle_state_transition::HartLifecycleStateTransition; pub use sbi::{AceExtension, BaseExtension, HsmExtension, IpiExtension, RfenceExtension, SbiExtension, SrstExtension}; pub use trap_reason::TrapReason; mod compressed_instructions; mod fp_registers; mod gp_registers; -mod hart_state; +mod hart_architectural_state; +mod hart_lifecycle_state; +mod hart_lifecycle_state_transition; mod sbi; mod trap_reason; diff --git a/security-monitor/src/core/architecture/riscv/sbi.rs b/security-monitor/src/core/architecture/riscv/sbi.rs index 0bf6fc1..879cdcd 100644 --- a/security-monitor/src/core/architecture/riscv/sbi.rs +++ b/security-monitor/src/core/architecture/riscv/sbi.rs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -use crate::core::architecture::{GpRegister, HartState}; +use crate::core::architecture::{GpRegister, HartArchitecturalState}; #[derive(Debug)] pub enum SbiExtension { @@ -16,7 +16,7 @@ pub enum SbiExtension { } impl SbiExtension { - pub fn decode(hart_state: &HartState) -> Self { + pub fn decode(hart_state: &HartArchitecturalState) -> Self { match (hart_state.gpr(GpRegister::a7), hart_state.gpr(GpRegister::a6)) { (AceExtension::EXTID, function_id) => Self::Ace(AceExtension::from_function_id(function_id)), (BaseExtension::EXTID, function_id) => Self::Base(BaseExtension::from_function_id(function_id)), diff --git a/security-monitor/src/core/architecture/riscv/trap_reason.rs b/security-monitor/src/core/architecture/riscv/trap_reason.rs index 6821279..c29b415 100644 --- a/security-monitor/src/core/architecture/riscv/trap_reason.rs +++ b/security-monitor/src/core/architecture/riscv/trap_reason.rs @@ -2,7 +2,7 @@ // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 use crate::core::architecture::riscv::sbi::SbiExtension; -use crate::core::architecture::HartState; +use crate::core::architecture::HartArchitecturalState; #[derive(Debug)] pub enum TrapReason { @@ -24,7 +24,7 @@ impl TrapReason { const GUEST_LOAD_PAGE_FAULT: usize = 21; const GUEST_STORE_PAGE_FAULT: usize = 23; - pub fn from_hart_state(hart_state: &HartState) -> Self { + pub fn from_hart_state(hart_state: &HartArchitecturalState) -> Self { let mcause = riscv::register::mcause::read(); if mcause.is_interrupt() { return Self::Interrupt; diff --git a/security-monitor/src/core/control_data/confidential_hart.rs b/security-monitor/src/core/control_data/confidential_hart.rs index 19cf184..0185ac1 100644 --- a/security-monitor/src/core/control_data/confidential_hart.rs +++ b/security-monitor/src/core/control_data/confidential_hart.rs @@ -1,25 +1,35 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -use crate::core::architecture::{FpRegisters, GpRegister, GpRegisters, HartState, TrapReason}; +use crate::core::architecture::{ + FpRegisters, GpRegister, GpRegisters, HartArchitecturalState, HartLifecycleState, TrapReason, +}; use crate::core::control_data::ConfidentialVmId; use crate::core::transformations::{ ExposeToConfidentialVm, GuestLoadPageFaultRequest, GuestLoadPageFaultResult, GuestStorePageFaultRequest, GuestStorePageFaultResult, InterHartRequest, MmioLoadRequest, MmioStoreRequest, PendingRequest, SbiHsmHartStart, - SbiIpi, SbiRemoteFenceI, SbiRemoteSfenceVma, SbiRemoteSfenceVmaAsid, SbiRequest, SbiResult, SharePageRequest, + SbiHsmHartSuspend, SbiIpi, SbiRemoteFenceI, SbiRemoteSfenceVma, SbiRemoteSfenceVmaAsid, SbiRequest, SbiResult, + SharePageRequest, }; use crate::error::Error; /// ConfidentialHart represents the dump state of the confidential VM's hart (aka vcpu). The only publicly exposed way -/// to modify the virtual hart state (registers/CSRs) is by calling the constructor or applying a transformation. +/// to modify the confidential hart architectural state (registers/CSRs) is by calling the constructor or applying a +/// transformation. #[repr(C)] pub struct ConfidentialHart { // If there is no confidential vm id assigned to this hart then it means that this confidential hart is a dummy - // one. A dummy virtual hart means that the confidential_hart is not associated with any confidential VM + // one. A dummy virtual hart means that the confidential_hart is not associated with any confidential VM but is + // used to prevent some concurrency issues like attempts of assigning the same confidential hart to many physical + // cores. confidential_vm_id: Option, - // Safety: Careful, HardwareHart and ConfidentialHart must both start with the HartState element because based on - // this we automatically calculate offsets of registers' and CSRs' for the asm code. - confidential_hart_state: HartState, + // Safety: Careful, HardwareHart and ConfidentialHart must both start with the HartArchitecturalState element + // because based on this we automatically calculate offsets of registers' and CSRs' for the asm code. + confidential_hart_state: HartArchitecturalState, + /// The confidential hart's lifecycle follow the finite state machine (FSM) of a hart defined in SBI HSM extension. + lifecycle_state: HartLifecycleState, + /// A pending request indicates that the confidential hart sent a request to the hypervisor and is waiting for its + /// reply. The pending request defines the expected response. pending_request: Option, } @@ -27,12 +37,12 @@ impl ConfidentialHart { /// Constructs a dummy hart. This dummy hart carries no confidential information. It is used to indicate that a real /// confidential hart has been assigned to a hardware hart for execution. pub fn dummy(id: usize) -> Self { - Self::new(HartState::empty(id)) + Self::new(HartArchitecturalState::empty(id), HartLifecycleState::Stopped) } /// Constructs a confidential hart with the state after a reset. - pub fn from_vm_hart_reset(id: usize, from: &HartState) -> Self { - let mut confidential_hart_state = HartState::from_existing(id, from); + pub fn from_vm_hart_reset(id: usize, from: &HartArchitecturalState) -> Self { + let mut confidential_hart_state = HartArchitecturalState::from_existing(id, from); GpRegisters::iter().for_each(|x| { confidential_hart_state.gprs.0[x] = 0; }); @@ -40,11 +50,12 @@ impl ConfidentialHart { confidential_hart_state.fprs.0[x] = 0; }); // TODO: reset PC and other state-related csrs - Self::new(confidential_hart_state) + Self::new(confidential_hart_state, HartLifecycleState::Stopped) } - pub fn from_vm_hart(id: usize, from: &HartState) -> Self { - let mut confidential_hart = Self::new(HartState::from_existing(id, from)); + pub fn from_vm_hart(id: usize, from: &HartArchitecturalState) -> Self { + let hart_architectural_state = HartArchitecturalState::from_existing(id, from); + let mut confidential_hart = Self::new(hart_architectural_state, HartLifecycleState::Started); // We create a virtual hart as a result of the SBI request the ESM call traps in the security monitor, which // creates the confidential VM but then the security monitor makes an SBI call to the hypervisor to let him know // that this VM become an confidential VM. The hypervisor should then return to the confidential VM providing it @@ -53,7 +64,9 @@ impl ConfidentialHart { confidential_hart } - fn new(mut confidential_hart_state: HartState) -> Self { + fn new(mut confidential_hart_state: HartArchitecturalState, lifecycle_state: HartLifecycleState) -> Self { + let confidential_vm_id = None; + let pending_request = None; // delegate VS-level interrupts directly to the confidential VM. All other // interrupts will trap in the security monitor. confidential_hart_state.mideleg = 0b010001001110; @@ -63,7 +76,7 @@ impl ConfidentialHart { confidential_hart_state.medeleg = 0b1011001111111111; confidential_hart_state.hedeleg = confidential_hart_state.medeleg; - Self { confidential_hart_state, pending_request: None, confidential_vm_id: None } + Self { confidential_vm_id, confidential_hart_state, lifecycle_state, pending_request } } pub fn set_confidential_vm_id(&mut self, confidential_vm_id: ConfidentialVmId) { @@ -86,6 +99,16 @@ impl ConfidentialHart { self.confidential_vm_id.is_none() } + /// Returns true if this confidential hart can execute. + pub fn is_executable(&self) -> bool { + let hart_states_allowed_to_resume = + [HartLifecycleState::Started, HartLifecycleState::StartPending, HartLifecycleState::Suspended]; + hart_states_allowed_to_resume.contains(&self.lifecycle_state) + } + + /// Stores a pending request inside the confidential hart's state. Before the next execution of this confidential + /// hart, the security monitor will declassify a response to this request that should come from another security + /// domain, like hypervisor. pub fn set_pending_request(&mut self, request: PendingRequest) -> Result<(), Error> { assure!(self.pending_request.is_none(), Error::PendingRequest())?; self.pending_request = Some(request); @@ -93,19 +116,81 @@ impl ConfidentialHart { } } -// functions to inject information to a confidential VM. +// Methods related to lifecycle state transitions of the confidential hart. These methods manipulate the internal hart +// state in a response to requests from (1) the confidential hart it self (started->stop or started->suspend), from +// other confidential hart (stopped->started), or hypervisor (suspend->started). Check out the SBI' HSM extensions for +// more details. +impl ConfidentialHart { + /// Changes the lifecycle state of the hart into the `StartPending` state. Confidential hart's state is set as if + /// the hart was reset. This function is called as a response of another confidential hart (typically a boot hart) + /// to start another confidential hart. Returns error if the confidential hart is not in stopped state. + pub fn transition_from_stopped_to_start_pending(&mut self, request: SbiHsmHartStart) -> Result<(), Error> { + // A hypervisor might try to schedule a stopped confidential hart. This is forbidden. + assure!(self.lifecycle_state == HartLifecycleState::Stopped, Error::CannotStartNotStoppedHart())?; + // if this is a dummy hart, then the confidential hart is already running, i.e., it is in the `started` state. + assure_not!(self.is_dummy(), Error::HartAlreadyRunning())?; + // let's set up the confidential hart so that it can be run + self.lifecycle_state = HartLifecycleState::StartPending; + self.pending_request = Some(PendingRequest::SbiHsmHartStartPending()); + // Following the SBI documentation of the function `hart start` in the HSM extension, only satp, sstatus.SIE, + // a0, a1 have defined values, all other registers are in an undefined state. The hart will start + // executing in the supervisor mode. + const SIE: usize = 1 << 1; + // We clear all VS-related state but leave the configuration of M/HS-mode related registers, for example, we + // want the interrupt delegations configuration to remain untouched. + self.confidential_hart_state.reset(); + self.confidential_hart_state.vsatp = 0; + self.confidential_hart_state.sstatus &= !(SIE); + self.confidential_hart_state.set_gpr(GpRegister::a1, self.confidential_hart_id()); + self.confidential_hart_state.set_gpr(GpRegister::a2, request.opaque); + self.confidential_hart_state.mepc = request.start_address; + Ok(()) + } + + /// Changes the lifecycle state of the confidential hart to the `Started` state. + pub fn transition_from_start_pending_to_started(&mut self) { + assert!(!self.is_dummy()); + if self.lifecycle_state == HartLifecycleState::StartPending { + self.lifecycle_state = HartLifecycleState::Started; + } + } + + pub fn transition_from_started_to_suspended(&mut self, _request: SbiHsmHartSuspend) -> Result<(), Error> { + assert!(!self.is_dummy()); + assure!(self.lifecycle_state == HartLifecycleState::Started, Error::CannotSuspedNotStartedHart())?; + self.lifecycle_state = HartLifecycleState::Suspended; + Ok(()) + } + + pub fn transition_from_started_to_stopped(&mut self) -> Result<(), Error> { + assert!(!self.is_dummy()); + assure!(self.lifecycle_state == HartLifecycleState::Started, Error::CannotStopNotStartedHart())?; + self.lifecycle_state = HartLifecycleState::Stopped; + Ok(()) + } + + pub fn transition_from_suspended_to_started(&mut self) -> Result<(), Error> { + assert!(!self.is_dummy()); + assure!(self.lifecycle_state == HartLifecycleState::Suspended, Error::CannotStartNotSuspendedHart())?; + self.lifecycle_state = HartLifecycleState::Started; + Ok(()) + } +} + +// methods to inject information to a confidential VM. impl ConfidentialHart { pub fn apply(&mut self, transformation: ExposeToConfidentialVm) -> usize { match transformation { ExposeToConfidentialVm::SbiResult(v) => self.apply_sbi_result(v), ExposeToConfidentialVm::GuestLoadPageFaultResult(v) => self.apply_guest_load_page_fault_result(v), ExposeToConfidentialVm::GuestStorePageFaultResult(v) => self.apply_guest_store_page_fault_result(v), - ExposeToConfidentialVm::Resume() => {} - ExposeToConfidentialVm::SbiHsmHartStart(v) => self.apply_sbi_hart_start(v), ExposeToConfidentialVm::InterProcessorInterrupt(v) => self.apply_inter_processor_interrupt(v), ExposeToConfidentialVm::SbiRemoteFenceI(v) => self.apply_remote_fence_i(v), ExposeToConfidentialVm::SbiRemoteSfenceVma(v) => self.apply_remote_sfence_vma(v), ExposeToConfidentialVm::SbiRemoteSfenceVmaAsid(v) => self.apply_remote_sfence_vma_asid(v), + ExposeToConfidentialVm::SbiHsmHartStartPending() => self.transition_from_start_pending_to_started(), + ExposeToConfidentialVm::SbiHsmHartStart() => self.apply_sbi_result_success(), + ExposeToConfidentialVm::Resume() => {} } core::ptr::addr_of!(self.confidential_hart_state) as usize } @@ -131,18 +216,18 @@ impl ConfidentialHart { unsafe { core::arch::asm!("sfence.vma") }; } - fn apply_sbi_hart_start(&mut self, result: SbiHsmHartStart) { - self.confidential_hart_state.set_gpr(GpRegister::a1, self.confidential_hart_id()); - self.confidential_hart_state.set_gpr(GpRegister::a2, result.blob); - self.confidential_hart_state.mepc = result.boot_code_address; - } - fn apply_sbi_result(&mut self, result: SbiResult) { self.confidential_hart_state.set_gpr(GpRegister::a0, result.a0()); self.confidential_hart_state.set_gpr(GpRegister::a1, result.a1()); self.confidential_hart_state.mepc += result.pc_offset(); } + fn apply_sbi_result_success(&mut self) { + self.confidential_hart_state.set_gpr(GpRegister::a0, 0); + self.confidential_hart_state.set_gpr(GpRegister::a1, 0); + self.confidential_hart_state.mepc += 4; + } + fn apply_guest_load_page_fault_result(&mut self, result: GuestLoadPageFaultResult) { self.confidential_hart_state.set_gpr(result.result_gpr(), result.value()); self.confidential_hart_state.mepc += result.instruction_length(); @@ -211,6 +296,13 @@ impl ConfidentialHart { SbiHsmHartStart::new(confidential_hart_id, boot_code_address, blob) } + pub fn sbi_hsm_hart_suspend(&self) -> SbiHsmHartSuspend { + let suspend_type = self.confidential_hart_state.gpr(GpRegister::a0); + let resume_addr = self.confidential_hart_state.gpr(GpRegister::a1); + let opaque = self.confidential_hart_state.gpr(GpRegister::a2); + SbiHsmHartSuspend::new(suspend_type, resume_addr, opaque) + } + pub fn sbi_remote_fence_i(&self) -> InterHartRequest { let hart_mask = self.confidential_hart_state.gpr(GpRegister::a0); let hart_mask_base = self.confidential_hart_state.gpr(GpRegister::a1); diff --git a/security-monitor/src/core/control_data/confidential_vm.rs b/security-monitor/src/core/control_data/confidential_vm.rs index 91713f9..5833095 100644 --- a/security-monitor/src/core/control_data/confidential_vm.rs +++ b/security-monitor/src/core/control_data/confidential_vm.rs @@ -4,7 +4,7 @@ use crate::core::control_data::{ConfidentialHart, ConfidentialVmId, ConfidentialVmMeasurement, HardwareHart}; use crate::core::interrupt_controller::InterruptController; use crate::core::memory_protector::ConfidentialVmMemoryProtector; -use crate::core::transformations::InterHartRequest; +use crate::core::transformations::{InterHartRequest, SbiHsmHartStart}; use crate::error::Error; use alloc::collections::BTreeMap; use alloc::vec::Vec; @@ -52,7 +52,7 @@ impl ConfidentialVm { /// Assigns a confidential hart of the confidential VM to the hardware hart. The hardware memory isolation mechanism /// is reconfigured to enforce memory access control for the confidential VM. Returns error if the confidential VM's - /// virtual hart has been already stolen. + /// virtual hart has been already stolen or is in the `Stopped` state. /// /// Guarantees: /// * If confidential hart is assigned to the hardware hart, then the hardware hart is configured to enforce memory @@ -65,7 +65,9 @@ impl ConfidentialVm { // because after a confidential_hart is scheduled for the first time, its token is stolen and the // ConfidentialVM is left with a dummy confidential_hart. A dummy confidential hart is a hart not associated // with any confidential vm. - assure_not!(confidential_hart.is_dummy(), Error::RunningVHart())?; + assure_not!(confidential_hart.is_dummy(), Error::HartAlreadyRunning())?; + // The hypervisor might try to schedule a confidential hart that has never been started. This is forbidden. + assure!(confidential_hart.is_executable(), Error::HartAlreadyRunning())?; // We can now assign the confidential hart to the hardware hart. The code below this line must not throw an // error. core::mem::swap(&mut hardware_hart.confidential_hart, &mut self.confidential_harts[confidential_hart_id]); @@ -80,7 +82,7 @@ impl ConfidentialVm { /// Unassigns a confidential hart from the hardware hart. /// /// Safety: - /// A confidential hart belonging to this confidential VM is assigned to the hardware hart. + /// * A confidential hart belonging to this confidential VM is assigned to the hardware hart. pub fn return_confidential_hart(&mut self, hardware_hart: &mut HardwareHart) { assert!(!hardware_hart.confidential_hart.is_dummy()); assert!(Some(self.id) == hardware_hart.confidential_hart().confidential_vm_id()); @@ -97,6 +99,14 @@ impl ConfidentialVm { self.confidential_harts.iter().filter(|confidential_hart| confidential_hart.is_dummy()).count() > 0 } + /// Transits the confidential hart's lifecycle state to `StartPending`. Returns error if the confidential hart is + /// not in the `Stopped` state or a confidential hart with the requested id does not exist. + pub fn transit_confidential_hart_to_start_pending(&mut self, request: SbiHsmHartStart) -> Result<(), Error> { + let hart = self.confidential_harts.get_mut(request.confidential_hart_id).ok_or(Error::InvalidHartId())?; + hart.transition_from_stopped_to_start_pending(request)?; + Ok(()) + } + /// Queues a request from one confidential hart to another and emits a hardware interrupt to the physical hart that /// executes that confidential hart. If the confidential hart is not executing, then no hardware interrupt is /// emmited. Returns error when 1) a queue that stores the confidential hart's InterHartRequests is full, 2) when diff --git a/security-monitor/src/core/control_data/confidential_vm_id.rs b/security-monitor/src/core/control_data/confidential_vm_id.rs index 5aa7f18..893f316 100644 --- a/security-monitor/src/core/control_data/confidential_vm_id.rs +++ b/security-monitor/src/core/control_data/confidential_vm_id.rs @@ -17,6 +17,6 @@ impl ConfidentialVmId { impl core::fmt::Debug for ConfidentialVmId { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "[confidential_vm_id={:x}]", self.0) + write!(f, "confidential_vm_id={:x}", self.0) } } diff --git a/security-monitor/src/core/control_data/hardware_hart.rs b/security-monitor/src/core/control_data/hardware_hart.rs index 422b6ba..3e776d0 100644 --- a/security-monitor/src/core/control_data/hardware_hart.rs +++ b/security-monitor/src/core/control_data/hardware_hart.rs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -use crate::core::architecture::{GpRegister, HartState, TrapReason}; +use crate::core::architecture::{GpRegister, HartArchitecturalState, TrapReason}; use crate::core::control_data::ConfidentialHart; use crate::core::memory_protector::HypervisorMemoryProtector; use crate::core::page_allocator::{Allocated, Page, UnAllocated}; @@ -13,9 +13,9 @@ use crate::core::transformations::{ #[repr(C)] pub struct HardwareHart { - // Careful, HardwareHart and ConfidentialHart must both start with the HartState element because based on this we - // automatically calculate offsets of registers' and CSRs' for the asm code. - pub(super) non_confidential_hart_state: HartState, + // Careful, HardwareHart and ConfidentialHart must both start with the HartArchitecturalState element because based + // on this we automatically calculate offsets of registers' and CSRs' for the asm code. + pub(super) non_confidential_hart_state: HartArchitecturalState, // Memory protector that configures the hardware memory isolation component to allow only memory accesses // to the memory region owned by the hypervisor. hypervisor_memory_protector: HypervisorMemoryProtector, @@ -38,7 +38,7 @@ pub struct HardwareHart { impl HardwareHart { pub fn init(id: usize, stack: Page, hypervisor_memory_protector: HypervisorMemoryProtector) -> Self { Self { - non_confidential_hart_state: HartState::empty(id), + non_confidential_hart_state: HartArchitecturalState::empty(id), hypervisor_memory_protector, stack_address: stack.end_address(), stack: stack.zeroize(), diff --git a/security-monitor/src/core/control_data/mod.rs b/security-monitor/src/core/control_data/mod.rs index fe2759d..3215035 100644 --- a/security-monitor/src/core/control_data/mod.rs +++ b/security-monitor/src/core/control_data/mod.rs @@ -8,7 +8,7 @@ pub use confidential_vm_measurement::ConfidentialVmMeasurement; pub use hardware_hart::HardwareHart; pub use storage::{ControlData, CONTROL_DATA}; -use crate::core::architecture::{GpRegister, HartState}; +use crate::core::architecture::{GpRegister, HartArchitecturalState}; mod confidential_hart; mod confidential_vm; @@ -19,19 +19,20 @@ mod storage; const fn hart_gpr_offset(index: GpRegister) -> usize { memoffset::offset_of!(HardwareHart, non_confidential_hart_state) - + memoffset::offset_of!(HartState, gprs) + + memoffset::offset_of!(HartArchitecturalState, gprs) + (index as usize) * core::mem::size_of::() } const fn hart_fpr_offset(index: usize) -> usize { memoffset::offset_of!(HardwareHart, non_confidential_hart_state) - + memoffset::offset_of!(HartState, fprs) + + memoffset::offset_of!(HartArchitecturalState, fprs) + index * core::mem::size_of::() } macro_rules! hart_csr_offset { ($reg:tt) => { - memoffset::offset_of!(HardwareHart, non_confidential_hart_state) + memoffset::offset_of!(HartState, $reg) + memoffset::offset_of!(HardwareHart, non_confidential_hart_state) + + memoffset::offset_of!(HartArchitecturalState, $reg) }; } diff --git a/security-monitor/src/core/control_data/storage.rs b/security-monitor/src/core/control_data/storage.rs index 3e5d06f..afce23f 100644 --- a/security-monitor/src/core/control_data/storage.rs +++ b/security-monitor/src/core/control_data/storage.rs @@ -50,6 +50,7 @@ impl ControlData { pub fn remove_confidential_vm( &mut self, confidential_vm_id: ConfidentialVmId, ) -> Result, Error> { + assure_not!(self.confidential_vm(confidential_vm_id)?.is_running(), Error::HartAlreadyRunning())?; self.confidential_vms.remove(&confidential_vm_id).ok_or(Error::InvalidConfidentialVmId()) } diff --git a/security-monitor/src/core/memory_layout/confidential_vm_virtual_address.rs b/security-monitor/src/core/memory_layout/confidential_vm_virtual_address.rs index 7be165f..1f7bd92 100644 --- a/security-monitor/src/core/memory_layout/confidential_vm_virtual_address.rs +++ b/security-monitor/src/core/memory_layout/confidential_vm_virtual_address.rs @@ -17,6 +17,6 @@ impl ConfidentialVmVirtualAddress { impl core::fmt::Debug for ConfidentialVmVirtualAddress { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "[confidential_vm_virtual_address={:x}]", self.0) + write!(f, "confidential_vm_virtual_address={:x}", self.0) } } diff --git a/security-monitor/src/core/memory_protector/confidential_vm_memory_protector.rs b/security-monitor/src/core/memory_protector/confidential_vm_memory_protector.rs index c0feba5..dd1a3a9 100644 --- a/security-monitor/src/core/memory_protector/confidential_vm_memory_protector.rs +++ b/security-monitor/src/core/memory_protector/confidential_vm_memory_protector.rs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -use crate::core::architecture::HartState; +use crate::core::architecture::HartArchitecturalState; use crate::core::control_data::ConfidentialVmId; use crate::core::memory_layout::ConfidentialVmVirtualAddress; use crate::core::memory_protector::mmu::RootPageTable; @@ -23,7 +23,7 @@ impl ConfidentialVmMemoryProtector { /// Constructs the memory protector of a confidential VM from the dumped state of a hart that was running a /// non-confidential VM at the time it requested to be converted in a confidential VM. This function copies the /// entire configuration of the underlying hardware memory isolation component into the confidential memory. - pub fn from_vm_state(hart_state: &HartState) -> Result { + pub fn from_vm_state(hart_state: &HartArchitecturalState) -> Result { let hgatp = Hgatp::from(hart_state.hgatp); let root_page_table = mmu::copy_mmu_configuration_from_non_confidential_memory(hgatp)?; diff --git a/security-monitor/src/core/transformations/convert_to_confidential_vm_request.rs b/security-monitor/src/core/transformations/convert_to_confidential_vm_request.rs index 7b3f74b..a6d36fc 100644 --- a/security-monitor/src/core/transformations/convert_to_confidential_vm_request.rs +++ b/security-monitor/src/core/transformations/convert_to_confidential_vm_request.rs @@ -1,19 +1,19 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -use crate::core::architecture::HartState; +use crate::core::architecture::HartArchitecturalState; pub struct ConvertToConfidentialVm { - hart_state: HartState, + hart_state: HartArchitecturalState, } impl ConvertToConfidentialVm { - pub fn new(from_state: &HartState) -> Self { - let hart_state = HartState::from_existing(0, from_state); + pub fn new(from_state: &HartArchitecturalState) -> Self { + let hart_state = HartArchitecturalState::from_existing(0, from_state); Self { hart_state } } - pub fn into(self) -> HartState { + pub fn into(self) -> HartArchitecturalState { self.hart_state } } diff --git a/security-monitor/src/core/transformations/guest_load_page_fault_result.rs b/security-monitor/src/core/transformations/guest_load_page_fault_result.rs index 8bd431b..835f7aa 100644 --- a/security-monitor/src/core/transformations/guest_load_page_fault_result.rs +++ b/security-monitor/src/core/transformations/guest_load_page_fault_result.rs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -use crate::core::architecture::{GpRegister, HartState}; +use crate::core::architecture::{GpRegister, HartArchitecturalState}; use crate::core::transformations::GuestLoadPageFaultRequest; pub struct GuestLoadPageFaultResult { @@ -11,7 +11,7 @@ pub struct GuestLoadPageFaultResult { } impl GuestLoadPageFaultResult { - pub fn new(hart_state: &HartState, request: GuestLoadPageFaultRequest) -> Self { + pub fn new(hart_state: &HartArchitecturalState, request: GuestLoadPageFaultRequest) -> Self { Self { result_gpr: request.result_gpr(), value: hart_state.gpr(request.result_gpr()), diff --git a/security-monitor/src/core/transformations/mod.rs b/security-monitor/src/core/transformations/mod.rs index c3180a5..5680134 100644 --- a/security-monitor/src/core/transformations/mod.rs +++ b/security-monitor/src/core/transformations/mod.rs @@ -11,7 +11,7 @@ pub use mmio_load_request::MmioLoadRequest; pub use mmio_store_request::MmioStoreRequest; pub use opensbi_request::OpensbiRequest; pub use resume_request::ResumeRequest; -pub use sbi_hsm::SbiHsmHartStart; +pub use sbi_hsm::{SbiHsmHartStart, SbiHsmHartSuspend}; pub use sbi_ipi::SbiIpi; pub use sbi_request::SbiRequest; pub use sbi_result::SbiResult; @@ -61,7 +61,8 @@ pub enum ExposeToConfidentialVm { SbiRemoteFenceI(SbiRemoteFenceI), SbiRemoteSfenceVma(SbiRemoteSfenceVma), SbiRemoteSfenceVmaAsid(SbiRemoteSfenceVmaAsid), - SbiHsmHartStart(SbiHsmHartStart), + SbiHsmHartStart(), + SbiHsmHartStartPending(), } /// An intermediate confidential hart state that requested certain operation from the hypervisor and is waiting for the @@ -71,13 +72,14 @@ pub enum PendingRequest { SharePage(SharePageRequest), GuestLoadPageFault(GuestLoadPageFaultRequest), GuestStorePageFault(GuestStorePageFaultRequest), + SbiHsmHartStart(), + SbiHsmHartStartPending(), SbiRequest(), } /// A request send from one confidential hart to another confidential hart belonging to the same confidential VM. #[derive(Debug, PartialEq, Clone)] pub enum InterHartRequest { - SbiHsmHartStart(SbiHsmHartStart), InterProcessorInterrupt(SbiIpi), SbiRemoteFenceI(SbiRemoteFenceI), SbiRemoteSfenceVma(SbiRemoteSfenceVma), @@ -88,7 +90,6 @@ impl InterHartRequest { pub fn into_expose_to_confidential_vm(self) -> ExposeToConfidentialVm { match self { Self::InterProcessorInterrupt(v) => ExposeToConfidentialVm::InterProcessorInterrupt(v), - Self::SbiHsmHartStart(v) => ExposeToConfidentialVm::SbiHsmHartStart(v), Self::SbiRemoteFenceI(v) => ExposeToConfidentialVm::SbiRemoteFenceI(v), Self::SbiRemoteSfenceVma(v) => ExposeToConfidentialVm::SbiRemoteSfenceVma(v), Self::SbiRemoteSfenceVmaAsid(v) => ExposeToConfidentialVm::SbiRemoteSfenceVmaAsid(v), @@ -98,7 +99,6 @@ impl InterHartRequest { pub fn is_hart_selected(&self, hart_id: usize) -> bool { match self { Self::InterProcessorInterrupt(v) => Self::check_if_hart_selected(hart_id, v.hart_mask, v.hart_mask_base), - Self::SbiHsmHartStart(v) => hart_id == v.confidential_hart_id, Self::SbiRemoteFenceI(v) => Self::check_if_hart_selected(hart_id, v.hart_mask, v.hart_mask_base), Self::SbiRemoteSfenceVma(v) => Self::check_if_hart_selected(hart_id, v.hart_mask, v.hart_mask_base), Self::SbiRemoteSfenceVmaAsid(v) => Self::check_if_hart_selected(hart_id, v.hart_mask, v.hart_mask_base), diff --git a/security-monitor/src/core/transformations/opensbi_request.rs b/security-monitor/src/core/transformations/opensbi_request.rs index 8d9f064..b51609e 100644 --- a/security-monitor/src/core/transformations/opensbi_request.rs +++ b/security-monitor/src/core/transformations/opensbi_request.rs @@ -1,14 +1,14 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -use crate::core::architecture::{GpRegister, HartState}; +use crate::core::architecture::{GpRegister, HartArchitecturalState}; pub struct OpensbiRequest { pub regs: opensbi_sys::sbi_trap_regs, } impl OpensbiRequest { - pub fn new(hart_state: &HartState) -> Self { + pub fn new(hart_state: &HartArchitecturalState) -> Self { Self { regs: opensbi_sys::sbi_trap_regs { zero: 0, diff --git a/security-monitor/src/core/transformations/sbi_hsm.rs b/security-monitor/src/core/transformations/sbi_hsm.rs index 5a68550..03fb73d 100644 --- a/security-monitor/src/core/transformations/sbi_hsm.rs +++ b/security-monitor/src/core/transformations/sbi_hsm.rs @@ -5,12 +5,25 @@ #[derive(PartialEq, Debug, Clone)] pub struct SbiHsmHartStart { pub confidential_hart_id: usize, - pub boot_code_address: usize, - pub blob: usize, + pub start_address: usize, + pub opaque: usize, } impl SbiHsmHartStart { - pub fn new(confidential_hart_id: usize, boot_code_address: usize, blob: usize) -> Self { - Self { confidential_hart_id, boot_code_address, blob } + pub fn new(confidential_hart_id: usize, start_address: usize, opaque: usize) -> Self { + Self { confidential_hart_id, start_address, opaque } + } +} + +#[derive(PartialEq, Debug, Clone)] +pub struct SbiHsmHartSuspend { + pub suspend_type: usize, + pub resume_address: usize, + pub opaque: usize, +} + +impl SbiHsmHartSuspend { + pub fn new(suspend_type: usize, resume_address: usize, opaque: usize) -> Self { + Self { suspend_type, resume_address, opaque } } } diff --git a/security-monitor/src/core/transformations/sbi_request.rs b/security-monitor/src/core/transformations/sbi_request.rs index 90791c3..d62ef9a 100644 --- a/security-monitor/src/core/transformations/sbi_request.rs +++ b/security-monitor/src/core/transformations/sbi_request.rs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -use crate::core::architecture::{GpRegister, HartState}; +use crate::core::architecture::{GpRegister, HartArchitecturalState}; use crate::core::control_data::ConfidentialVmId; pub struct SbiRequest { @@ -42,9 +42,19 @@ impl SbiRequest { Self::new(HsmExtension::EXTID, HsmExtension::HART_START_FID, virtual_hart_id, 0, 0, 0, 0, 0) } - // only ConfidentialHart or HardwareHart can invoke this function because only they have access to the HartState - // storing confidential information - pub fn from_hart_state(hart_state: &HartState) -> Self { + pub fn kvm_hsm_hart_stop() -> Self { + use crate::core::architecture::HsmExtension; + Self::new(HsmExtension::EXTID, HsmExtension::HART_STOP_FID, 0, 0, 0, 0, 0, 0) + } + + pub fn kvm_hsm_hart_suspend() -> Self { + use crate::core::architecture::HsmExtension; + Self::new(HsmExtension::EXTID, HsmExtension::HART_SUSPEND_FID, 0, 0, 0, 0, 0, 0) + } + + // only ConfidentialHart or HardwareHart can invoke this function because only they have access to the + // HartArchitecturalState storing confidential information + pub fn from_hart_state(hart_state: &HartArchitecturalState) -> Self { Self::new( hart_state.gpr(GpRegister::a7), hart_state.gpr(GpRegister::a6), diff --git a/security-monitor/src/core/transformations/sbi_result.rs b/security-monitor/src/core/transformations/sbi_result.rs index ce07974..283a54c 100644 --- a/security-monitor/src/core/transformations/sbi_result.rs +++ b/security-monitor/src/core/transformations/sbi_result.rs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -use crate::core::architecture::{GpRegister, HartState}; +use crate::core::architecture::{GpRegister, HartArchitecturalState}; /// Sbi is a result of the SBI call from the Hypervisor to the SBI /// firmware or a result of the SBI call to the security monitor. @@ -19,7 +19,7 @@ impl SbiResult { Self { a0, a1, pc_offset } } - pub fn ecall(hart_state: &HartState) -> Self { + pub fn ecall(hart_state: &HartArchitecturalState) -> Self { Self::new(hart_state.gpr(GpRegister::a0), hart_state.gpr(GpRegister::a1), Self::ECALL_INSTRUCTION_LENGTH) } diff --git a/security-monitor/src/core/transformations/sbi_vm_request.rs b/security-monitor/src/core/transformations/sbi_vm_request.rs index 7f40329..29c463a 100644 --- a/security-monitor/src/core/transformations/sbi_vm_request.rs +++ b/security-monitor/src/core/transformations/sbi_vm_request.rs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -use crate::core::architecture::{GpRegister, HartState}; +use crate::core::architecture::{GpRegister, HartArchitecturalState}; use crate::core::transformations::SbiRequest; pub struct SbiVmRequest { @@ -10,7 +10,7 @@ pub struct SbiVmRequest { } impl SbiVmRequest { - pub fn from_hart_state(hart_state: &HartState) -> Self { + pub fn from_hart_state(hart_state: &HartArchitecturalState) -> Self { let sbi_request = SbiRequest::new( hart_state.gpr(GpRegister::a7), hart_state.gpr(GpRegister::a6), diff --git a/security-monitor/src/error.rs b/security-monitor/src/error.rs index 110a7f0..ba3b874 100644 --- a/security-monitor/src/error.rs +++ b/security-monitor/src/error.rs @@ -51,7 +51,7 @@ pub enum Error { #[error("Invalid confidential VM ID")] InvalidConfidentialVmId(), #[error("vHart is running")] - RunningVHart(), + HartAlreadyRunning(), #[error("Invalid riscv instruction: {0:x}")] InvalidRiscvInstruction(usize), #[error("Not supported interrupt")] @@ -64,6 +64,16 @@ pub enum Error { ReachedMaxNumberOfRemoteHartRequests(), #[error("Sending interrupt error")] InterruptSendingError(), + + // SBI HSM extension related errors + #[error("Cannot start a confidential hart because it is not in the Stopped state.")] + CannotStartNotStoppedHart(), + #[error("Cannot stop a confidential hart because it is not in the Started state.")] + CannotStopNotStartedHart(), + #[error("Cannot suspend a confidential hart because it is not in the Started state.")] + CannotSuspedNotStartedHart(), + #[error("Cannot start a confidential hart because it is not in the Suspended state.")] + CannotStartNotSuspendedHart(), } impl Error { diff --git a/security-monitor/src/non_confidential_flow/handlers/terminate.rs b/security-monitor/src/non_confidential_flow/handlers/terminate.rs index 7265f24..10a7c06 100644 --- a/security-monitor/src/non_confidential_flow/handlers/terminate.rs +++ b/security-monitor/src/non_confidential_flow/handlers/terminate.rs @@ -1,16 +1,14 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -use crate::core::control_data::{ConfidentialVmId, ControlData}; +use crate::core::control_data::ControlData; use crate::core::transformations::{ExposeToHypervisor, SbiResult, TerminateRequest}; -use crate::error::Error; use crate::non_confidential_flow::NonConfidentialFlow; /// The hypervisor command to terminate the confidential VM and remove it from the memory. pub fn handle(terminate_request: TerminateRequest, non_confidential_flow: NonConfidentialFlow) -> ! { let transformation = ControlData::try_write(|control_data| { let confidential_vm_id = terminate_request.confidential_vm_id(); - ensure_that_the_confidential_vm_can_be_terminated(control_data, confidential_vm_id)?; debug!("Terminating the confidential VM[id={:?}]", confidential_vm_id); control_data.remove_confidential_vm(confidential_vm_id) }) @@ -19,11 +17,3 @@ pub fn handle(terminate_request: TerminateRequest, non_confidential_flow: NonCon non_confidential_flow.exit_to_hypervisor(transformation) } - -fn ensure_that_the_confidential_vm_can_be_terminated( - control_data: &ControlData, confidential_vm_id: ConfidentialVmId, -) -> Result<(), Error> { - let confidential_vm = control_data.confidential_vm(confidential_vm_id)?; - assure_not!(confidential_vm.is_running(), Error::RunningVHart())?; - Ok(()) -}