diff --git a/security-monitor/src/confidential_flow/control_flow/mod.rs b/security-monitor/src/confidential_flow/control_flow/mod.rs index 0134d17..a2972e2 100644 --- a/security-monitor/src/confidential_flow/control_flow/mod.rs +++ b/security-monitor/src/confidential_flow/control_flow/mod.rs @@ -2,7 +2,6 @@ // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 use crate::core::control_data::{ConfidentialVmId, ControlData, HardwareHart}; -use crate::core::memory_partitioner::MemoryPartitioner; use crate::core::transformations::{ExposeToConfidentialVm, PendingRequest, TrapReason}; use crate::non_confidential_flow::NonConfidentialFlow; @@ -55,7 +54,6 @@ impl<'a> ConfidentialFlow<'a> { use crate::confidential_flow::handlers::{ guest_load_page_fault_result, guest_store_page_fault_result, hypercall_result, share_page_result, }; - match self.hart.confidential_hart_mut().take_request() { Some(PendingRequest::SbiRequest()) => hypercall_result::handle(self.hart.hypercall_result(), self), Some(PendingRequest::GuestLoadPageFault(request)) => { @@ -72,12 +70,7 @@ impl<'a> ConfidentialFlow<'a> { pub fn into_non_confidential_flow(self) -> NonConfidentialFlow<'a> { let id = self.confidential_vm_id(); match ControlData::try_confidential_vm(id, |mut cvm| Ok(cvm.return_confidential_hart(self.hart))) { - Ok(_) => { - // it is safe to close access to confidential memory here because we transition in the - // finite state machine to nodes that will lead to the execution of a hypervisor. - unsafe { MemoryPartitioner::enable_hypervisor_memory_view() }; - NonConfidentialFlow::create(self.hart) - } + Ok(_) => NonConfidentialFlow::create(self.hart), Err(error) => self.exit_to_confidential_vm(error.into_confidential_transformation()), } } @@ -88,7 +81,10 @@ impl<'a> ConfidentialFlow<'a> { } pub fn confidential_vm_id(&'a self) -> ConfidentialVmId { - self.hart.confidential_hart().confidential_vm_id() + self.hart + .confidential_hart() + .confidential_vm_id() + .expect("Bug: found dummy hart instead of a confidential hart") } pub fn set_pending_request(self, request: PendingRequest) -> Self { 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 8008686..261010d 100644 --- a/security-monitor/src/confidential_flow/handlers/share_page_result.rs +++ b/security-monitor/src/confidential_flow/handlers/share_page_result.rs @@ -26,7 +26,7 @@ pub fn handle(share_page_result: SharePageResult, confidential_flow: Confidentia let confidential_vm_id = confidential_flow.confidential_vm_id(); let transformation = ControlData::try_confidential_vm_mut(confidential_vm_id, |mut cvm| { - cvm.root_page_table_mut().map_shared_page(shared_page) + cvm.memory_protector_mut().map_shared_page(shared_page) }) .and_then(|_| Ok(ExposeToConfidentialVm::SbiResult(SbiResult::success(0)))) .unwrap_or_else(|error| error.into_confidential_transformation()); diff --git a/security-monitor/src/core/control_data/confidential_hart.rs b/security-monitor/src/core/control_data/confidential_hart.rs index a86ceff..cf2d8c5 100644 --- a/security-monitor/src/core/control_data/confidential_hart.rs +++ b/security-monitor/src/core/control_data/confidential_hart.rs @@ -15,18 +15,20 @@ use crate::error::Error; /// (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 + 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, pending_request: Option, - // a dummy virtual hart means that the confidential_hart is not associated with any confidential VM - dummy: bool, } impl ConfidentialHart { pub fn dummy(id: usize) -> Self { let confidential_hart_state = HartState::empty(id); - Self { confidential_hart_state, pending_request: None, dummy: true } + Self { confidential_hart_state, pending_request: None, confidential_vm_id: None } } pub fn from_vm_hart_reset(id: usize, from: &HartState) -> Self { @@ -41,7 +43,7 @@ impl ConfidentialHart { confidential_hart_state.medeleg = 0b1011001111111111; confidential_hart_state.hedeleg = confidential_hart_state.medeleg; - Self { confidential_hart_state, pending_request: None, dummy: false } + Self { confidential_hart_state, pending_request: None, confidential_vm_id: None } } pub fn from_vm_hart(id: usize, from: &HartState) -> Self { @@ -63,8 +65,12 @@ impl ConfidentialHart { confidential_hart } - pub fn confidential_vm_id(&self) -> ConfidentialVmId { - ConfidentialVmId::new(riscv::register::hgatp::Hgatp::from(self.confidential_hart_state.hgatp).vmid()) + pub fn set_confidential_vm_id(&mut self, confidential_vm_id: ConfidentialVmId) { + self.confidential_vm_id = Some(confidential_vm_id); + } + + pub fn confidential_vm_id(&self) -> Option { + self.confidential_vm_id } pub(super) fn confidential_hart_id(&self) -> usize { @@ -76,7 +82,7 @@ impl ConfidentialHart { } pub fn is_dummy(&self) -> bool { - self.dummy + self.confidential_vm_id.is_none() } pub fn set_pending_request(&mut self, request: PendingRequest) -> Result<(), Error> { @@ -88,14 +94,6 @@ impl ConfidentialHart { // functions to inject information to a confidential VM. impl ConfidentialHart { - pub fn hgatp(&self) -> usize { - self.confidential_hart_state.hgatp - } - - pub fn set_hgatp(&mut self, hgatp: usize) { - self.confidential_hart_state.hgatp = hgatp; - } - pub fn apply(&mut self, transformation: ExposeToConfidentialVm) -> usize { match transformation { ExposeToConfidentialVm::SbiResult(v) => self.apply_sbi_result(v), diff --git a/security-monitor/src/core/control_data/confidential_vm.rs b/security-monitor/src/core/control_data/confidential_vm.rs index c5142ea..006d218 100644 --- a/security-monitor/src/core/control_data/confidential_vm.rs +++ b/security-monitor/src/core/control_data/confidential_vm.rs @@ -2,10 +2,9 @@ // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 use crate::core::control_data::{ConfidentialHart, HardwareHart}; -use crate::core::memory_partitioner::RootPageTable; +use crate::core::memory_protector::ConfidentialVmMemoryProtector; use crate::error::Error; use alloc::vec::Vec; -use riscv::register::hgatp::Hgatp; const MAX_HASH_SIZE: usize = 512; // 512b for SHA-512 @@ -25,39 +24,57 @@ pub struct ConfidentialVm { id: ConfidentialVmId, _measurements: [Measurement; 4], confidential_harts: Vec, - root_page_table: RootPageTable, + memory_protector: ConfidentialVmMemoryProtector, } impl ConfidentialVm { pub fn new( - id: ConfidentialVmId, mut confidential_harts: Vec, root_page_table: RootPageTable, + id: ConfidentialVmId, mut confidential_harts: Vec, + mut memory_protector: ConfidentialVmMemoryProtector, ) -> Self { - let hgatp = Hgatp::new(root_page_table.address(), root_page_table.paging_system().hgatp_mode(), id.0); - confidential_harts.iter_mut().for_each(|confidential_hart| confidential_hart.set_hgatp(hgatp.bits())); - Self { id, _measurements: [Measurement::empty(); 4], confidential_harts, root_page_table } + memory_protector.set_confidential_vm_id(id); + confidential_harts.iter_mut().for_each(|confidential_hart| confidential_hart.set_confidential_vm_id(id)); + Self { id, _measurements: [Measurement::empty(); 4], confidential_harts, memory_protector } } - pub fn root_page_table_mut(&mut self) -> &mut RootPageTable { - &mut self.root_page_table + pub fn memory_protector_mut(&mut self) -> &mut ConfidentialVmMemoryProtector { + &mut self.memory_protector } + /// Steals a confidential hart from the confidential VM and assigns it to the hardware hart. The + /// hardware memory isolation mechanism is reconfigured to enforce memory access control for the + /// confidential VM. Error is returns if the confidential VM's virtual hart has been already stolen. + /// + /// Guarantees: + /// * If confidential hart is assigned to the hardware hart, then the hardware hart is configured to enforce memory + /// access control of the confidential VM. pub fn steal_confidential_hart( &mut self, confidential_hart_id: usize, hardware_hart: &mut HardwareHart, ) -> Result<(), Error> { let confidential_hart = self.confidential_harts.get(confidential_hart_id).ok_or(Error::InvalidHartId())?; - // The hypervisor might try to schedule the same confidential_hart on different harts. We detect it 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. + // The hypervisor might try to schedule the same confidential hart on different physical harts. We detect it + // 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())?; core::mem::swap(&mut hardware_hart.confidential_hart, &mut self.confidential_harts[confidential_hart_id]); + // It is safe to invoke below unsafe code because at this point we are in the confidential flow part of the + // finite state machine and the virtual hart is assigned to the hardware hart. We must reconfigure the hardware + // memory isolation mechanism to enforce that the confidential virtual machine has access only to the memory + // regions it owns. + unsafe { self.memory_protector.enable() }; Ok(()) } pub fn return_confidential_hart(&mut self, hardware_hart: &mut HardwareHart) { assert!(!hardware_hart.confidential_hart.is_dummy()); - assert!(self.id == hardware_hart.confidential_hart().confidential_vm_id()); + assert!(Some(self.id) == hardware_hart.confidential_hart().confidential_vm_id()); let confidential_hart_id = hardware_hart.confidential_hart.confidential_hart_id(); core::mem::swap(&mut hardware_hart.confidential_hart, &mut self.confidential_harts[confidential_hart_id]); + // It is safe to reconfigure the memory access control configuration to enable access to memory regions owned + // by the hypervisor because we are now transitioning into the non-confidential flow part of the finite state + // machine where the hardware hart is associated with a dummy virtual hart. + unsafe { hardware_hart.enable_hypervisor_memory_protector() }; } pub fn is_running(&self) -> bool { diff --git a/security-monitor/src/core/control_data/hardware_hart.rs b/security-monitor/src/core/control_data/hardware_hart.rs index f906b97..cefb93a 100644 --- a/security-monitor/src/core/control_data/hardware_hart.rs +++ b/security-monitor/src/core/control_data/hardware_hart.rs @@ -3,9 +3,10 @@ // SPDX-License-Identifier: Apache-2.0 use crate::core::control_data::ConfidentialHart; use crate::core::hart::{GpRegister, HartState}; +use crate::core::memory_protector::HypervisorMemoryProtector; use crate::core::memory_tracker::{Allocated, Page, UnAllocated}; use crate::core::transformations::{ - EsmRequest, ExposeToHypervisor, GuestLoadPageFaultRequest, GuestLoadPageFaultResult, InterruptRequest, + ConvertToConfidentialVm, ExposeToHypervisor, GuestLoadPageFaultRequest, GuestLoadPageFaultResult, InterruptRequest, MmioLoadRequest, MmioStoreRequest, OpensbiRequest, ResumeRequest, SbiRequest, SbiResult, SbiVmRequest, SharePageResult, TerminateRequest, TrapReason, }; @@ -15,16 +16,19 @@ 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, + // 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, // A page containing the stack of the code executing within the given hart. pub(super) stack: Page, - // the stack_address is redundant (we can learn the stack_address from the page assigned to the stack) but we need + // The stack_address is redundant (we can learn the stack_address from the page assigned to the stack) but we need // it because this is the way to expose it to assembly pub(super) stack_address: usize, - // we need to store the OpenSBI's mscratch value because OpenSBI uses mscratch to track some of its internal + // We need to store the OpenSBI's mscratch value because OpenSBI uses mscratch to track some of its internal // data structures and our security monitor also uses mscratch to keep track of the address of the hart state // in memory. previous_mscratch: usize, - // we keep the virtual hart that is associated with this hardware hart. The virtual hart can be 1) a dummy hart + // We keep the virtual hart that is associated with this hardware hart. The virtual hart can be 1) a dummy hart // in case there is any confidential VM's virtual hart associated to it, or 2) an confidential VM's virtual hart. // In the latter case, the hardware hart and confidential VM's control data swap their virtual harts (a dummy // hart with the confidential VM's virtual hart) @@ -32,9 +36,10 @@ pub struct HardwareHart { } impl HardwareHart { - pub fn init(id: usize, stack: Page) -> Self { + pub fn init(id: usize, stack: Page, hypervisor_memory_protector: HypervisorMemoryProtector) -> Self { Self { non_confidential_hart_state: HartState::empty(id), + hypervisor_memory_protector, stack_address: stack.end_address(), stack: stack.zeroize(), previous_mscratch: 0, @@ -46,7 +51,7 @@ impl HardwareHart { core::ptr::addr_of!(self.non_confidential_hart_state) as usize } - /// calling OpenSBI handler to process the SBI call requires setting the mscratch register to a specific value which + /// Calling OpenSBI handler to process the SBI call requires setting the mscratch register to a specific value which /// we replaced during the system initialization. We store the original mscratch value expected by the OpenSBI in /// the previous_mscratch field. pub fn swap_mscratch(&mut self) { @@ -62,6 +67,14 @@ impl HardwareHart { pub fn confidential_hart_mut(&mut self) -> &mut ConfidentialHart { &mut self.confidential_hart } + + pub fn hypervisor_memory_protector(&self) -> &HypervisorMemoryProtector { + &self.hypervisor_memory_protector + } + + pub unsafe fn enable_hypervisor_memory_protector(&self) { + self.hypervisor_memory_protector.enable(self.non_confidential_hart_state.hgatp) + } } impl HardwareHart { @@ -207,8 +220,8 @@ impl HardwareHart { self.non_confidential_hart_state.trap_reason() } - pub fn esm_request(&self) -> EsmRequest { - EsmRequest::new(&self.non_confidential_hart_state) + pub fn convert_to_confidential_vm_request(&self) -> ConvertToConfidentialVm { + ConvertToConfidentialVm::new(&self.non_confidential_hart_state) } pub fn hypercall_result(&self) -> SbiResult { diff --git a/security-monitor/src/core/control_data/storage.rs b/security-monitor/src/core/control_data/storage.rs index d568305..60801e1 100644 --- a/security-monitor/src/core/control_data/storage.rs +++ b/security-monitor/src/core/control_data/storage.rs @@ -2,7 +2,7 @@ // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 use crate::core::control_data::{ConfidentialHart, ConfidentialVm, ConfidentialVmId}; -use crate::core::memory_partitioner::RootPageTable; +use crate::core::memory_protector::ConfidentialVmMemoryProtector; use crate::error::{Error, NOT_INITIALIZED_CONTROL_DATA}; use alloc::collections::BTreeMap; use alloc::vec::Vec; @@ -28,7 +28,7 @@ impl ControlData { /// to it. This identifier is not secret and reflects the number of confidential VMs that have been created up to /// now. The maximum allowed number of confidential VMs created is limited by the size of the `usize` type. pub fn store_confidential_vm( - confidential_harts: Vec, root_page_table: RootPageTable, + confidential_harts: Vec, memory_protector: ConfidentialVmMemoryProtector, ) -> Result { Self::try_write(|control_data| { control_data @@ -39,7 +39,7 @@ impl ControlData { .unwrap_or(Some(0)) .and_then(|max_id| { let id = ConfidentialVmId::new(max_id); - let confidential_vm = ConfidentialVm::new(id, confidential_harts, root_page_table); + let confidential_vm = ConfidentialVm::new(id, confidential_harts, memory_protector); control_data.confidential_vms.insert(id, Mutex::new(confidential_vm)); Some(id) }) diff --git a/security-monitor/src/core/heap/mod.rs b/security-monitor/src/core/heap/mod.rs index 17e7873..49f9ec1 100644 --- a/security-monitor/src/core/heap/mod.rs +++ b/security-monitor/src/core/heap/mod.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::memory_partitioner::ConfidentialMemoryAddress; +use crate::core::memory_layout::ConfidentialMemoryAddress; use allocator::MemoryAllocator; mod allocator; diff --git a/security-monitor/src/core/initialization/mod.rs b/security-monitor/src/core/initialization/mod.rs index ab7c3db..b3ca504 100644 --- a/security-monitor/src/core/initialization/mod.rs +++ b/security-monitor/src/core/initialization/mod.rs @@ -2,7 +2,8 @@ // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 use crate::core::control_data::{ControlData, HardwareHart, CONTROL_DATA}; -use crate::core::memory_partitioner::{ConfidentialMemoryAddress, MemoryPartitioner, PageSize}; +use crate::core::memory_layout::{ConfidentialMemoryAddress, MemoryLayout}; +use crate::core::memory_protector::{HypervisorMemoryProtector, PageSize}; use crate::core::memory_tracker::{MemoryTracker, Page, UnAllocated, MEMORY_TRACKER}; use crate::error::{Error, HardwareFeatures, InitType, NOT_INITIALIZED_HART, NOT_INITIALIZED_HARTS}; use alloc::vec::Vec; @@ -41,17 +42,21 @@ extern "C" fn init_security_monitor_asm(flattened_device_tree_address: *const u8 fn init_security_monitor(flattened_device_tree_address: *const u8) -> Result<(), Error> { let fdt = unsafe { Fdt::from_ptr(flattened_device_tree_address)? }; - let number_of_harts = read_number_of_cpus(&fdt)?; - // TODO: make sure the system has enough physical memory - let (confidential_memory_start, confidential_memory_end) = initialize_memory_partitioner(&fdt)?; + let (confidential_memory_start, confidential_memory_end) = initialize_memory_layout(&fdt)?; // Create page tokens, heap, memory tracker - initalize_security_monitor_state(confidential_memory_start, confidential_memory_end, number_of_harts)?; + initalize_security_monitor_state(confidential_memory_start, confidential_memory_end)?; + + // Parses FDT to read number of harts and verify that they support required extensions + let number_of_harts = read_number_of_cpus(&fdt)?; + + // Prepares memory required to store physical hart state + prepare_harts(number_of_harts)?; // Enable entry points to the security monitor by taking control over - // specific interrupts. - set_delegation()?; + // specific interrupts and controls memory isolation mechanisms + setup_this_hart()?; // if we reached this line, then the security monitor has been correctly // initialized. @@ -84,7 +89,7 @@ fn read_number_of_cpus(fdt: &Fdt) -> Result { Ok(fdt.cpus().count()) } -fn initialize_memory_partitioner(fdt: &Fdt) -> Result<(ConfidentialMemoryAddress, *const usize), Error> { +fn initialize_memory_layout(fdt: &Fdt) -> Result<(ConfidentialMemoryAddress, *const usize), Error> { // TODO: FDT may contain multiple regions. For now, we assume there is only one region in the FDT. // This assumption is fine for the emulated environment (QEMU). let fdt_memory_region = fdt.memory().regions().next().ok_or(Error::Init(InitType::FdtMemory))?; @@ -113,7 +118,7 @@ fn initialize_memory_partitioner(fdt: &Fdt) -> Result<(ConfidentialMemoryAddress let confidential_memory_end = memory_end; debug!("Confidential memory {:#?}-{:#?}", confidential_memory_start, confidential_memory_end); - let (confidential_memory_address_start, confidential_memory_address_end) = MemoryPartitioner::init( + let (confidential_memory_address_start, confidential_memory_address_end) = MemoryLayout::init( non_confidential_memory_start, non_confidential_memory_end, confidential_memory_start, @@ -127,7 +132,7 @@ fn initialize_memory_partitioner(fdt: &Fdt) -> Result<(ConfidentialMemoryAddress /// monitor during the boot process. This function initializes secure monitor's /// memory management like allocators. fn initalize_security_monitor_state( - confidential_memory_start: ConfidentialMemoryAddress, confidential_memory_end: *const usize, number_of_harts: usize, + confidential_memory_start: ConfidentialMemoryAddress, confidential_memory_end: *const usize, ) -> Result<(), Error> { const NUMBER_OF_HEAP_PAGES: usize = 4 * 1024; // Safety: initialization order is crucial for safety because at some point we @@ -147,7 +152,7 @@ fn initalize_security_monitor_state( let heap_size_in_bytes = heap_pages * PageSize::smallest().in_bytes(); let mut heap_start_address = confidential_memory_start; let heap_end_address = - MemoryPartitioner::get().confidential_address_at_offset(&mut heap_start_address, heap_size_in_bytes)?; + MemoryLayout::read().confidential_address_at_offset(&mut heap_start_address, heap_size_in_bytes)?; crate::core::heap::init_heap(heap_start_address, heap_size_in_bytes); // Memory tracker starts directly after the heap @@ -159,21 +164,26 @@ fn initalize_security_monitor_state( // ownership to the memory tracker. let memory_tracker = unsafe { MemoryTracker::new(memory_tracker_start_address, memory_tracker_end_address)? }; MEMORY_TRACKER.call_once(|| RwLock::new(memory_tracker)); + CONTROL_DATA.call_once(|| RwLock::new(ControlData::new())); + Ok(()) +} + +fn prepare_harts(number_of_harts: usize) -> Result<(), Error> { // we need to allocate stack for the dumped state of each physical HART. let mut harts_states = Vec::with_capacity(number_of_harts); for hart_id in 0..number_of_harts { let stack = MemoryTracker::acquire_continous_pages(1, PageSize::Size2MiB)?.remove(0); + let hypervisor_memory_protector = HypervisorMemoryProtector::create(); debug!("HART[{}] stack {:x}-{:x}", hart_id, stack.start_address(), stack.end_address()); - harts_states.insert(hart_id, HardwareHart::init(hart_id, stack)); + harts_states.insert(hart_id, HardwareHart::init(hart_id, stack, hypervisor_memory_protector)); } - CONTROL_DATA.call_once(|| RwLock::new(ControlData::new())); HARTS_STATES.call_once(|| Mutex::new(harts_states)); Ok(()) } -fn set_delegation() -> Result<(), Error> { +fn setup_this_hart() -> Result<(), Error> { // let the hypervisor handle all traps except for two exceptions // that carry potentially SM-calls. These exceptions will be trapped in the // security monitor. The security monitor trap handler will delegate these @@ -207,5 +217,8 @@ fn set_delegation() -> Result<(), Error> { hart.swap_mscratch(); // this will store opensbi_mscratch in the internal hart state riscv::register::mscratch::write(hart.address()); + // configure memory isolation to limit memory view to the hypervisor owned memory regions + hart.hypervisor_memory_protector().setup()?; + Ok(()) } diff --git a/security-monitor/src/core/memory_partitioner/confidential_memory_address.rs b/security-monitor/src/core/memory_layout/confidential_memory_address.rs similarity index 100% rename from security-monitor/src/core/memory_partitioner/confidential_memory_address.rs rename to security-monitor/src/core/memory_layout/confidential_memory_address.rs diff --git a/security-monitor/src/core/memory_partitioner/mod.rs b/security-monitor/src/core/memory_layout/mod.rs similarity index 60% rename from security-monitor/src/core/memory_partitioner/mod.rs rename to security-monitor/src/core/memory_layout/mod.rs index dc9771e..fcbafb8 100644 --- a/security-monitor/src/core/memory_partitioner/mod.rs +++ b/security-monitor/src/core/memory_layout/mod.rs @@ -2,47 +2,43 @@ // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 pub use confidential_memory_address::ConfidentialMemoryAddress; -pub use mmu::{PageSize, PagingSystem, RootPageTable}; pub use non_confidential_memory_address::NonConfidentialMemoryAddress; -use crate::error::{Error, InitType, NOT_INITIALIZED_CONFIDENTIAL_MEMORY}; +use crate::core::memory_protector::PageSize; +use crate::error::{Error, InitType}; use pointers_utility::{ptr_align, ptr_byte_add_mut, ptr_byte_offset}; use spin::Once; mod confidential_memory_address; -mod iopmp; -mod mmu; mod non_confidential_memory_address; -mod pmp; -/// MEMORY_PARTITIONER is a global variable that is set during the boot process by the initialization function -/// and never changes later -- this is guaranteed by Once<>. -static MEMORY_PARTITIONER: Once = Once::new(); +const NOT_INITIALIZED_MEMORY_LAYOUT: &str = "Bug. Could not access MemoryLayout because is has not been initialized"; -/// Partitions memory in the confidential and non-confidential memory regions using hardware mechanisms. -/// Exposes safe interface to reconfigure access permissions to memory regions for access initiated from the -/// physical hart to memory regions owned by the hypervisor or a confidential VM. -/// -/// It also provides an interface to offset addresses while ensuring that the returned address remains inside +/// MEMORY_LAYOUT is a global static and private to this module variable that is set during the system boot +/// and never changes later -- this is guaranteed by Once<>. It stores an instance of the `MemoryLayout`. +/// The only way to get a shared access to this instance is by calling `MemoryLayout::read()` function. +static MEMORY_LAYOUT: Once = Once::new(); + +/// Provides an interface to offset addresses while ensuring that the returned address remains inside /// the same memory region, i.e., confidential or non-confidential memory. -pub struct MemoryPartitioner { +pub struct MemoryLayout { non_confidential_memory_start: *mut usize, non_confidential_memory_end: *const usize, confidential_memory_start: *mut usize, confidential_memory_end: *const usize, } -/// We need to declare Send+Sync on the `MemoryPartitioner` because MemoryPartitioner stores internally +/// We need to declare Send+Sync on the `MemoryLayout` because MemoryLayout stores internally /// raw pointers that are not safe to pass in a multi-threaded program. This is not a case -/// for MemoryPartitioner because we never expose raw pointers outside the MemoryPartitioner except for the +/// for MemoryLayout because we never expose raw pointers outside the MemoryLayout except for the /// constructor when we return the initial address of the confidential memory. The constructor /// is invoked only once by the initialization procedure during the boot of the system. -unsafe impl Send for MemoryPartitioner {} -unsafe impl Sync for MemoryPartitioner {} +unsafe impl Send for MemoryLayout {} +unsafe impl Sync for MemoryLayout {} -impl MemoryPartitioner { - /// Constructs the `MemoryPartitioner` where the confidential memory is within the memory range defined - /// by `confidential_memory_start` and `confidential_memory_end`. Returns the `MemoryPartitioner` and +impl MemoryLayout { + /// Constructs the `MemoryLayout` where the confidential memory is within the memory range defined + /// by `confidential_memory_start` and `confidential_memory_end`. Returns the `MemoryLayout` and /// the first alligned address in the confidential memory. /// /// # Safety @@ -74,7 +70,7 @@ impl MemoryPartitioner { ptr_byte_add_mut(confidential_memory_start, memory_size_in_bytes, confidential_memory_end)?; } - MEMORY_PARTITIONER.call_once(|| MemoryPartitioner { + MEMORY_LAYOUT.call_once(|| MemoryLayout { non_confidential_memory_start, non_confidential_memory_end, confidential_memory_start, @@ -83,8 +79,6 @@ impl MemoryPartitioner { // Reconfigure hardware to isolate the confidential memory region let confidential_memory_start = ConfidentialMemoryAddress::new(confidential_memory_start); - pmp::split_memory_into_confidential_and_non_confidential(&confidential_memory_start, confidential_memory_end)?; - iopmp::protect_confidential_memory_from_io_devices(&confidential_memory_start, confidential_memory_end)?; Ok((confidential_memory_start, confidential_memory_end)) } @@ -131,60 +125,21 @@ impl MemoryPartitioner { /// Caller must guarantee that there is no other thread that can write to confidential memory during execution /// of this function. pub unsafe fn clear_confidential_memory(&self) { - // we can safely cast below offset to usize because the constructor guarantees that the confidential memory + // We can safely cast below offset to usize because the constructor guarantees that the confidential memory // range is valid, and so the memory size must be a valid usize let memory_size = ptr_byte_offset(self.confidential_memory_end, self.confidential_memory_start) as usize; let usize_alligned_offsets = (0..memory_size).step_by(core::mem::size_of::()); usize_alligned_offsets.for_each(|offset_in_bytes| { - if let Ok(ptr) = - ptr_byte_add_mut(self.confidential_memory_start, offset_in_bytes, self.confidential_memory_end) - { - unsafe { ptr.write_volatile(0) }; - } + let _ = ptr_byte_add_mut(self.confidential_memory_start, offset_in_bytes, self.confidential_memory_end) + .and_then(|ptr| Ok(ptr.write_volatile(0))); }); } - /// Reconfigures hardware to enable access initiated from this physical hart to memory regions - /// owned by the confidential VM and deny access to all other memory regions. - /// - /// # Arguments - /// - /// `hgatp` must be a valid value of the RISC-V hgatp register that contains CVM's id and the address - /// of the root page table for the 2nd level addres translation. - /// - /// # Safety - /// - /// Caller must guarantee that the security monitor will transition in the finite state machine - /// to the `confidential flow` and that the hgatp argument contains the correct id and the root - /// page table address of the confidential VM that will be executed next. - pub unsafe fn enable_confidential_vm_memory_view(hgatp: usize) { - pmp::open_access_to_confidential_memory(); - // Enable MMU for HS,VS,VS,U modes. It is safe to invoke below code - // because we have access to this register (run in the M-mode) and - // hgatp is the content of the HGATP register calculated by the security monitor - // when recreating page tables of a confidential virtual machine that will - // get executed. - unsafe { - riscv::register::hgatp::write(hgatp); - core::arch::asm!("hfence.gvma"); - core::arch::asm!("hfence.vvma"); - }; - } - - /// Reconfigures hardware to enable access initiated from this physical hart to memory regions - /// owned by the hypervisor and denies accesses to all other memory regions. - /// - /// # Safety - /// - /// Caller must guarantee that the security monitor will transition in the finite state machine - /// to the `non-confidential flow` and eventually to a hypervisor code. - pub unsafe fn enable_hypervisor_memory_view() { - pmp::close_access_to_confidential_memory(); - // mmu will be reconfigured by the context switch code (assembly) that restores the previous - // hgatp value that the hypervisor used when it invoked a security monitor's call. + pub fn read() -> &'static MemoryLayout { + MEMORY_LAYOUT.get().expect(NOT_INITIALIZED_MEMORY_LAYOUT) } - pub fn get() -> &'static MemoryPartitioner { - MEMORY_PARTITIONER.get().expect(NOT_INITIALIZED_CONFIDENTIAL_MEMORY) + pub fn confidential_memory_boundary(&self) -> (usize, usize) { + (self.confidential_memory_start as usize, self.confidential_memory_end as usize) } } diff --git a/security-monitor/src/core/memory_partitioner/non_confidential_memory_address.rs b/security-monitor/src/core/memory_layout/non_confidential_memory_address.rs similarity index 90% rename from security-monitor/src/core/memory_partitioner/non_confidential_memory_address.rs rename to security-monitor/src/core/memory_layout/non_confidential_memory_address.rs index f0a7a2c..996e0aa 100644 --- a/security-monitor/src/core/memory_partitioner/non_confidential_memory_address.rs +++ b/security-monitor/src/core/memory_layout/non_confidential_memory_address.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::memory_partitioner::MemoryPartitioner; +use crate::core::memory_layout::MemoryLayout; use crate::error::Error; use pointers_utility::ptr_byte_add_mut; @@ -19,9 +19,9 @@ impl NonConfidentialMemoryAddress { // Safety: We check that the address is within the non-confidential memory range. // This is equavalent to ensure that it does not point to confidential memory as long // as memory is correctly splitted during the initialization procedure. We rely here on - // the guarantees from the `MemoryPartitioner` that cannot be constructed if confidential and + // the guarantees from the `MemoryLayout` that cannot be constructed if confidential and // non-confidential memory regions overlap. - match MemoryPartitioner::get().is_in_non_confidential_range(address) { + match MemoryLayout::read().is_in_non_confidential_range(address) { false => Err(Error::MemoryAccessAuthorization()), true => Ok(Self(address)), } diff --git a/security-monitor/src/core/memory_partitioner/mmu/mod.rs b/security-monitor/src/core/memory_partitioner/mmu/mod.rs deleted file mode 100644 index d674617..0000000 --- a/security-monitor/src/core/memory_partitioner/mmu/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-FileCopyrightText: 2023 IBM Corporation -// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich -// SPDX-License-Identifier: Apache-2.0 -pub use page_size::PageSize; -pub use page_table::RootPageTable; -pub use paging_system::PagingSystem; - -mod page_size; -mod page_table; -mod page_table_entry; -mod page_table_memory; -mod paging_system; 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 new file mode 100644 index 0000000..4fed18d --- /dev/null +++ b/security-monitor/src/core/memory_protector/confidential_vm_memory_protector.rs @@ -0,0 +1,70 @@ +// SPDX-FileCopyrightText: 2023 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 +use crate::core::control_data::ConfidentialVmId; +use crate::core::hart::HartState; +use crate::core::memory_protector::mmu::RootPageTable; +use crate::core::memory_protector::{mmu, pmp}; +use crate::core::memory_tracker::SharedPage; +use crate::error::Error; +use riscv::register::hgatp::Hgatp; + +/// Exposes an interface to configure the hardware memory isolation component in a way that +/// it protects accesses to the memory which the ConfidentialVM does not own. +pub struct ConfidentialVmMemoryProtector { + // stores the page table configuration of the ConfidentialVM. + root_page_table: RootPageTable, + // stores the value of the hypervisor G-stage address translation protocol register. + hgatp: usize, +} + +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 { + let hgatp = Hgatp::from(hart_state.hgatp); + let root_page_table = mmu::copy_mmu_configuration_from_non_confidential_memory(hgatp)?; + Ok(Self { root_page_table, hgatp: 0 }) + } + + pub fn set_confidential_vm_id(&mut self, id: ConfidentialVmId) { + let hgatp = + Hgatp::new(self.root_page_table.address(), self.root_page_table.paging_system().hgatp_mode(), id.usize()); + self.hgatp = hgatp.bits(); + } + + /// Modifies the configuration of the underlying hardware memory isolation component (e.g., MMU) + /// in a way that a shared page is mapped into the address space of the confidential VM. + pub fn map_shared_page(&mut self, shared_page: SharedPage) -> Result<(), Error> { + self.root_page_table.map_shared_page(shared_page) + } + + /// Reconfigures hardware to enable access initiated from this physical hart to memory regions + /// owned by the confidential VM and deny access to all other memory regions. + /// + /// # Arguments + /// + /// `hgatp` must be a valid value of the RISC-V hgatp register that contains CVM's id and the address + /// of the root page table for the 2nd level addres translation. + /// + /// # Safety + /// + /// Caller must guarantee that the security monitor will transition in the finite state machine + /// to the `confidential flow` and that the hgatp argument contains the correct id and the root + /// page table address of the confidential VM that will be executed next. + pub unsafe fn enable(&self) { + pmp::open_access_to_confidential_memory(); + // Enable MMU for HS,VS,VS,U modes. It is safe to invoke below code + // because we have access to this register (run in the M-mode) and + // hgatp is the content of the HGATP register calculated by the security monitor + // when recreating page tables of a confidential virtual machine that will + // get executed. + unsafe { + riscv::register::hgatp::write(self.hgatp); + core::arch::asm!("hfence.gvma"); + core::arch::asm!("hfence.vvma"); + }; + } +} diff --git a/security-monitor/src/core/memory_protector/hypervisor_memory_protector.rs b/security-monitor/src/core/memory_protector/hypervisor_memory_protector.rs new file mode 100644 index 0000000..5616e2e --- /dev/null +++ b/security-monitor/src/core/memory_protector/hypervisor_memory_protector.rs @@ -0,0 +1,46 @@ +// SPDX-FileCopyrightText: 2023 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 +use crate::core::memory_layout::MemoryLayout; +use crate::core::memory_protector::{iopmp, pmp}; +use crate::error::Error; + +/// Exposes an interface to configure the hardware memory isolation component to +/// set memory access protection preventing the hypervisor from accessing memory it does not own +pub struct HypervisorMemoryProtector {} + +impl HypervisorMemoryProtector { + pub fn create() -> Self { + Self {} + } + + /// Configures the memory protection mechanism on the hart which executes this function. + pub fn setup(&self) -> Result<(), Error> { + // We use RISC-V PMP mechanism to define that the confidential memory region is not accessible. + // We use RISC-V IOPMP mechanism to ensure that no IO devices can access confidential memory region. + let (confidential_memory_start, confidential_memory_end) = MemoryLayout::read().confidential_memory_boundary(); + pmp::split_memory_into_confidential_and_non_confidential(confidential_memory_start, confidential_memory_end)?; + iopmp::protect_confidential_memory_from_io_devices(confidential_memory_start, confidential_memory_end)?; + + Ok(()) + } + + /// Reconfigures hardware to enable access initiated from this physical hart to memory regions + /// owned by the hypervisor and denies accesses to all other memory regions. + /// + /// # Safety + /// + /// Caller must guarantee that the security monitor will transition in the finite state machine + /// to the `non-confidential flow` and eventually to the hypervisor code. + pub unsafe fn enable(&self, hgatp: usize) { + pmp::close_access_to_confidential_memory(); + // Enable MMU for HS,VS,VS,U modes. It is safe to invoke below code because we have access + // to this register (run in the M-mode) and hgatp is the content of the HGATP register + // that the hypervisor used when it invoked the security monitor. + unsafe { + riscv::register::hgatp::write(hgatp); + core::arch::asm!("hfence.gvma"); + core::arch::asm!("hfence.vvma"); + }; + } +} diff --git a/security-monitor/src/core/memory_partitioner/iopmp/mod.rs b/security-monitor/src/core/memory_protector/iopmp/mod.rs similarity index 67% rename from security-monitor/src/core/memory_partitioner/iopmp/mod.rs rename to security-monitor/src/core/memory_protector/iopmp/mod.rs index 4416d99..d7c2dad 100644 --- a/security-monitor/src/core/memory_partitioner/iopmp/mod.rs +++ b/security-monitor/src/core/memory_protector/iopmp/mod.rs @@ -1,11 +1,10 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -use crate::core::memory_partitioner::ConfidentialMemoryAddress; use crate::error::Error; pub(super) fn protect_confidential_memory_from_io_devices( - _confidential_memory_start: &ConfidentialMemoryAddress, _confidential_memory_end: *const usize, + _confidential_memory_start: usize, _confidential_memory_end: usize, ) -> Result<(), Error> { // TODO: implement IOPMP configuration Ok(()) diff --git a/security-monitor/src/core/memory_protector/mmu/mod.rs b/security-monitor/src/core/memory_protector/mmu/mod.rs new file mode 100644 index 0000000..0b5215a --- /dev/null +++ b/security-monitor/src/core/memory_protector/mmu/mod.rs @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: 2023 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 +pub use page_size::PageSize; +pub use page_table::RootPageTable; +pub use paging_system::PagingSystem; + +use crate::core::memory_layout::NonConfidentialMemoryAddress; +use crate::error::Error; +use riscv::register::hgatp::Hgatp; + +mod page_size; +mod page_table; +mod page_table_entry; +mod page_table_memory; +mod paging_system; + +pub fn copy_mmu_configuration_from_non_confidential_memory(hgatp: Hgatp) -> Result { + let paging_mode = hgatp.mode().ok_or_else(|| Error::UnsupportedPagingMode())?; + let paging_system = PagingSystem::from(&paging_mode).ok_or_else(|| Error::UnsupportedPagingMode())?; + + let root_page_address = NonConfidentialMemoryAddress::new(hgatp.address() as *mut usize)?; + let root_page_table = RootPageTable::copy_from_non_confidential_memory(root_page_address, paging_system)?; + + Ok(root_page_table) +} diff --git a/security-monitor/src/core/memory_partitioner/mmu/page_size.rs b/security-monitor/src/core/memory_protector/mmu/page_size.rs similarity index 100% rename from security-monitor/src/core/memory_partitioner/mmu/page_size.rs rename to security-monitor/src/core/memory_protector/mmu/page_size.rs diff --git a/security-monitor/src/core/memory_partitioner/mmu/page_table.rs b/security-monitor/src/core/memory_protector/mmu/page_table.rs similarity index 96% rename from security-monitor/src/core/memory_partitioner/mmu/page_table.rs rename to security-monitor/src/core/memory_protector/mmu/page_table.rs index e61d726..0ce4b96 100644 --- a/security-monitor/src/core/memory_partitioner/mmu/page_table.rs +++ b/security-monitor/src/core/memory_protector/mmu/page_table.rs @@ -1,12 +1,12 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -use crate::core::memory_partitioner::mmu::page_table_entry::{ +use crate::core::memory_layout::NonConfidentialMemoryAddress; +use crate::core::memory_protector::mmu::page_table_entry::{ PageTableAddress, PageTableBits, PageTableConfiguration, PageTableEntry, PageTablePermission, }; -use crate::core::memory_partitioner::mmu::page_table_memory::PageTableMemory; -use crate::core::memory_partitioner::mmu::paging_system::PageTableLevel; -use crate::core::memory_partitioner::{NonConfidentialMemoryAddress, PagingSystem}; +use crate::core::memory_protector::mmu::page_table_memory::PageTableMemory; +use crate::core::memory_protector::mmu::paging_system::{PageTableLevel, PagingSystem}; use crate::core::memory_tracker::{MemoryTracker, SharedPage}; use crate::error::Error; use alloc::boxed::Box; diff --git a/security-monitor/src/core/memory_partitioner/mmu/page_table_entry.rs b/security-monitor/src/core/memory_protector/mmu/page_table_entry.rs similarity index 98% rename from security-monitor/src/core/memory_partitioner/mmu/page_table_entry.rs rename to security-monitor/src/core/memory_protector/mmu/page_table_entry.rs index 03e1dee..22fa381 100644 --- a/security-monitor/src/core/memory_partitioner/mmu/page_table_entry.rs +++ b/security-monitor/src/core/memory_protector/mmu/page_table_entry.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::memory_partitioner::mmu::page_table::PageTable; +use crate::core::memory_protector::mmu::page_table::PageTable; use crate::core::memory_tracker::{Allocated, Page, SharedPage}; use alloc::boxed::Box; diff --git a/security-monitor/src/core/memory_partitioner/mmu/page_table_memory.rs b/security-monitor/src/core/memory_protector/mmu/page_table_memory.rs similarity index 91% rename from security-monitor/src/core/memory_partitioner/mmu/page_table_memory.rs rename to security-monitor/src/core/memory_protector/mmu/page_table_memory.rs index 5685da7..370eff6 100644 --- a/security-monitor/src/core/memory_partitioner/mmu/page_table_memory.rs +++ b/security-monitor/src/core/memory_protector/mmu/page_table_memory.rs @@ -2,10 +2,10 @@ // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 use super::PagingSystem; -use crate::core::memory_partitioner::mmu::page_table_entry::PageTableEntry; -use crate::core::memory_partitioner::mmu::paging_system::PageTableLevel; -use crate::core::memory_partitioner::mmu::PageSize; -use crate::core::memory_partitioner::{MemoryPartitioner, NonConfidentialMemoryAddress}; +use crate::core::memory_layout::{MemoryLayout, NonConfidentialMemoryAddress}; +use crate::core::memory_protector::mmu::page_table_entry::PageTableEntry; +use crate::core::memory_protector::mmu::paging_system::PageTableLevel; +use crate::core::memory_protector::mmu::PageSize; use crate::core::memory_tracker::{Allocated, MemoryTracker, Page}; use crate::error::Error; use alloc::vec::Vec; @@ -32,7 +32,7 @@ impl PageTableMemory { .map(|(i, page)| { let offset_in_bytes = i * page.size().in_bytes(); let page_address = - MemoryPartitioner::get().non_confidential_address_at_offset(&address, offset_in_bytes)?; + MemoryLayout::read().non_confidential_address_at_offset(&address, offset_in_bytes)?; page.copy_from_non_confidential_memory(page_address) }) .collect::>, Error>>()?; diff --git a/security-monitor/src/core/memory_partitioner/mmu/paging_system.rs b/security-monitor/src/core/memory_protector/mmu/paging_system.rs similarity index 98% rename from security-monitor/src/core/memory_partitioner/mmu/paging_system.rs rename to security-monitor/src/core/memory_protector/mmu/paging_system.rs index 3643b6f..c98798a 100644 --- a/security-monitor/src/core/memory_partitioner/mmu/paging_system.rs +++ b/security-monitor/src/core/memory_protector/mmu/paging_system.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::memory_partitioner::PageSize; +use crate::core::memory_protector::PageSize; use crate::core::transformations::ConfidentialVmVirtualAddress; use riscv::register::hgatp::HgatpMode; diff --git a/security-monitor/src/core/memory_protector/mod.rs b/security-monitor/src/core/memory_protector/mod.rs new file mode 100644 index 0000000..a90b7a5 --- /dev/null +++ b/security-monitor/src/core/memory_protector/mod.rs @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2023 IBM Corporation +// SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich +// SPDX-License-Identifier: Apache-2.0 +pub use confidential_vm_memory_protector::ConfidentialVmMemoryProtector; +pub use hypervisor_memory_protector::HypervisorMemoryProtector; +pub use mmu::{PageSize, RootPageTable}; + +mod confidential_vm_memory_protector; +mod hypervisor_memory_protector; +mod iopmp; +mod mmu; +mod pmp; diff --git a/security-monitor/src/core/memory_partitioner/pmp/mod.rs b/security-monitor/src/core/memory_protector/pmp/mod.rs similarity index 86% rename from security-monitor/src/core/memory_partitioner/pmp/mod.rs rename to security-monitor/src/core/memory_protector/pmp/mod.rs index dab040a..2b9014d 100644 --- a/security-monitor/src/core/memory_partitioner/pmp/mod.rs +++ b/security-monitor/src/core/memory_protector/pmp/mod.rs @@ -1,7 +1,6 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -use crate::core::memory_partitioner::ConfidentialMemoryAddress; use crate::error::{Error, HardwareFeatures}; // OpenSBI set already PMPs to isolate OpenSBI firmware from the rest of the @@ -9,21 +8,14 @@ use crate::error::{Error, HardwareFeatures}; // range We will use PMP0 and PMP1 to protect the confidential memory region, // PMP2 to protect the OpenSBI, and PMP3 to define the system range. pub(super) fn split_memory_into_confidential_and_non_confidential( - confidential_memory_start: &ConfidentialMemoryAddress, confidential_memory_end: *const usize, + confidential_memory_start: usize, confidential_memory_end: usize, ) -> Result<(), Error> { use riscv::register::{Permission, Range}; - const MINIMUM_NUMBER_OF_PMP_REQUIRED: usize = 4; - const PMP_SHIFT: u16 = 2; - // TODO: read how many PMPs are supported - let number_of_pmps = 64; - debug!("Number of PMPs={}", number_of_pmps); - assure!( - number_of_pmps >= MINIMUM_NUMBER_OF_PMP_REQUIRED, - Error::NotSupportedHardware(HardwareFeatures::NotEnoughPmps) - )?; + const PMP_SHIFT: u16 = 2; // TODO: read how many PMPs are supported + const MINIMUM_NUMBER_OF_PMP_REQUIRED: usize = 4; let number_of_pmps = 64; debug!("Number of PMPs={}", number_of_pmps); assure!( @@ -49,10 +41,10 @@ pub(super) fn split_memory_into_confidential_and_non_confidential( // now set up PMP0 and PMP1 to define the range of the confidential memory unsafe { - riscv::register::pmpaddr0::write(confidential_memory_start.as_usize() >> PMP_SHIFT); + riscv::register::pmpaddr0::write(confidential_memory_start >> PMP_SHIFT); riscv::register::pmpcfg0::set_pmp(0, Range::OFF, Permission::NONE, false); - riscv::register::pmpaddr1::write(confidential_memory_end as usize >> PMP_SHIFT); + riscv::register::pmpaddr1::write(confidential_memory_end >> PMP_SHIFT); riscv::register::pmpcfg0::set_pmp(1, Range::TOR, Permission::NONE, false); riscv::asm::sfence_vma_all(); } diff --git a/security-monitor/src/core/memory_tracker/memory_tracker.rs b/security-monitor/src/core/memory_tracker/memory_tracker.rs index c1124bb..6eed82e 100644 --- a/security-monitor/src/core/memory_tracker/memory_tracker.rs +++ b/security-monitor/src/core/memory_tracker/memory_tracker.rs @@ -2,7 +2,8 @@ // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 use super::page::{Page, UnAllocated}; -use crate::core::memory_partitioner::{ConfidentialMemoryAddress, MemoryPartitioner, PageSize}; +use crate::core::memory_layout::{ConfidentialMemoryAddress, MemoryLayout}; +use crate::core::memory_protector::PageSize; use crate::error::Error; use alloc::collections::BTreeMap; use alloc::vec; @@ -10,6 +11,8 @@ use alloc::vec::Vec; use core::ops::Range; use spin::{Once, RwLock, RwLockWriteGuard}; +const NOT_INITIALIZED_MEMORY_TRACKER: &str = "Bug. Could not access memory tracker because it is not initialized"; + /// A static global structure containing unallocated pages. Once<> guarantees /// that it the memory tracker can only be initialized once. pub static MEMORY_TRACKER: Once> = Once::new(); @@ -42,7 +45,7 @@ impl<'a> MemoryTracker { assert!(memory_start.offset_from(memory_end) as usize % PageSize::smallest().in_bytes() == 0); let mut map = BTreeMap::new(); - let memory_partitioner = MemoryPartitioner::get(); + let memory_layout = MemoryLayout::read(); let mut page_address = memory_start; for page_size in &[PageSize::Size1GiB, PageSize::Size2MiB, PageSize::Size4KiB] { let free_memory_in_bytes = usize::try_from(page_address.offset_from(memory_end))?; @@ -50,7 +53,7 @@ impl<'a> MemoryTracker { let new_pages = (0..number_of_new_pages) .map(|i| { let page_offset_in_bytes = i * page_size.in_bytes(); - let address = memory_partitioner.confidential_address_at_offset_bounded( + let address = memory_layout.confidential_address_at_offset_bounded( &mut page_address, page_offset_in_bytes, memory_end, @@ -67,7 +70,7 @@ impl<'a> MemoryTracker { let pages_size_in_bytes = new_pages.len() * page_size.in_bytes(); map.insert(page_size.clone(), new_pages); - match memory_partitioner.confidential_address_at_offset_bounded( + match memory_layout.confidential_address_at_offset_bounded( &mut page_address, pages_size_in_bytes, memory_end, @@ -176,7 +179,6 @@ impl<'a> MemoryTracker { fn try_write(op: O) -> Result where O: FnOnce(&mut RwLockWriteGuard<'static, MemoryTracker>) -> Result { - use crate::error::NOT_INITIALIZED_MEMORY_TRACKER; MEMORY_TRACKER .get() .expect(NOT_INITIALIZED_MEMORY_TRACKER) diff --git a/security-monitor/src/core/memory_tracker/page.rs b/security-monitor/src/core/memory_tracker/page.rs index 924cdea..dbcf97b 100644 --- a/security-monitor/src/core/memory_tracker/page.rs +++ b/security-monitor/src/core/memory_tracker/page.rs @@ -1,9 +1,8 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -use crate::core::memory_partitioner::{ - ConfidentialMemoryAddress, MemoryPartitioner, NonConfidentialMemoryAddress, PageSize, -}; +use crate::core::memory_layout::{ConfidentialMemoryAddress, MemoryLayout, NonConfidentialMemoryAddress}; +use crate::core::memory_protector::PageSize; use crate::error::Error; use alloc::vec::Vec; use core::marker::PhantomData; @@ -55,7 +54,7 @@ impl Page { ) -> Result, Error> { self.offsets().into_iter().try_for_each(|offset_in_bytes| { let non_confidential_address = - MemoryPartitioner::get().non_confidential_address_at_offset(&mut address, offset_in_bytes)?; + MemoryLayout::read().non_confidential_address_at_offset(&mut address, offset_in_bytes)?; // TODO: describe why below unsafe block is safe in this invocation. let data_to_copy = unsafe { non_confidential_address.read() }; self.write(offset_in_bytes, data_to_copy)?; @@ -68,7 +67,7 @@ impl Page { /// are correctly alligned. If this page is the smallest page (4KiB for RISC-V), then /// the same page is returned. pub fn divide(mut self) -> Vec> { - let memory_partitioner = MemoryPartitioner::get(); + let memory_layout = MemoryLayout::read(); let smaller_page_size = self.size.smaller().unwrap_or(self.size); let number_of_smaller_pages = self.size.in_bytes() / smaller_page_size.in_bytes(); let page_end = self.end_address_ptr(); @@ -77,7 +76,7 @@ impl Page { let offset_in_bytes = i * smaller_page_size.in_bytes(); // Safety: below unwrap is safe because a size of a larger page is a // multiply of a smaller page size, thus we will never exceed the outer page boundary. - let smaller_page_start = memory_partitioner + let smaller_page_start = memory_layout .confidential_address_at_offset_bounded(&mut self.address, offset_in_bytes, page_end) .unwrap(); // Safety: The below token creation is safe because the current page owns the entire memory diff --git a/security-monitor/src/core/memory_tracker/shared_page.rs b/security-monitor/src/core/memory_tracker/shared_page.rs index 225e64c..efac29d 100644 --- a/security-monitor/src/core/memory_tracker/shared_page.rs +++ b/security-monitor/src/core/memory_tracker/shared_page.rs @@ -1,7 +1,8 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -use crate::core::memory_partitioner::{MemoryPartitioner, NonConfidentialMemoryAddress, PageSize}; +use crate::core::memory_layout::{MemoryLayout, NonConfidentialMemoryAddress}; +use crate::core::memory_protector::PageSize; use crate::core::transformations::{ConfidentialVmVirtualAddress, SharePageRequest}; use crate::error::Error; @@ -29,7 +30,7 @@ impl SharedPage { // Security: check that the start address is located in the non-confidential memory let hypervisor_address = NonConfidentialMemoryAddress::new(hypervisor_address as *mut usize)?; // Security: check that the end address is located in the non-confidential memory - MemoryPartitioner::get().non_confidential_address_at_offset(&hypervisor_address, page_size.in_bytes() - 1)?; + MemoryLayout::read().non_confidential_address_at_offset(&hypervisor_address, page_size.in_bytes() - 1)?; let confidential_vm_virtual_address = request.confidential_vm_virtual_address(); diff --git a/security-monitor/src/core/mod.rs b/security-monitor/src/core/mod.rs index 75d0490..3feb691 100644 --- a/security-monitor/src/core/mod.rs +++ b/security-monitor/src/core/mod.rs @@ -2,11 +2,12 @@ // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 pub mod control_data; -pub mod memory_partitioner; +pub mod memory_layout; +pub mod memory_protector; pub mod memory_tracker; pub mod transformations; mod hart; mod heap; mod initialization; -mod panic; \ No newline at end of file +mod panic; diff --git a/security-monitor/src/core/panic.rs b/security-monitor/src/core/panic.rs index 46414bd..36b9f4d 100644 --- a/security-monitor/src/core/panic.rs +++ b/security-monitor/src/core/panic.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::memory_partitioner::MemoryPartitioner; +use crate::core::memory_layout::MemoryLayout; /// This piece of code executes on a panic. Panic is a runtime error that /// indicates an implementation bug from which we cannot recover. Examples are @@ -27,7 +27,7 @@ fn panic(info: &core::panic::PanicInfo) -> ! { // outside the confidential memory region. // 2) TODO: we must guarantee that only one hardware thread calls this method. Specifically // that there is no panic! executed on two different harts at the same time. - unsafe { MemoryPartitioner::get().clear_confidential_memory() }; + unsafe { MemoryLayout::read().clear_confidential_memory() }; // sleep or loop forever since there is nothing else we can do loop { diff --git a/security-monitor/src/core/transformations/esm_request.rs b/security-monitor/src/core/transformations/convert_to_confidential_vm_request.rs similarity index 57% rename from security-monitor/src/core/transformations/esm_request.rs rename to security-monitor/src/core/transformations/convert_to_confidential_vm_request.rs index a7b8cb3..cef6bb4 100644 --- a/security-monitor/src/core/transformations/esm_request.rs +++ b/security-monitor/src/core/transformations/convert_to_confidential_vm_request.rs @@ -2,21 +2,18 @@ // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 use crate::core::hart::HartState; -use riscv::register::hgatp::Hgatp; -pub struct EsmRequest { - hgatp: Hgatp, +pub struct ConvertToConfidentialVm { hart_state: HartState, } -impl EsmRequest { +impl ConvertToConfidentialVm { pub fn new(from_state: &HartState) -> Self { let hart_state = HartState::from_existing(0, from_state); - let hgatp = Hgatp::from(from_state.hgatp); - Self { hgatp, hart_state } + Self { hart_state } } - pub fn into(self) -> (Hgatp, HartState) { - (self.hgatp, self.hart_state) + pub fn into(self) -> HartState { + self.hart_state } } diff --git a/security-monitor/src/core/transformations/mod.rs b/security-monitor/src/core/transformations/mod.rs index 97b0ac9..cde3e9b 100644 --- a/security-monitor/src/core/transformations/mod.rs +++ b/security-monitor/src/core/transformations/mod.rs @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2023 IBM Corporation // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -pub use esm_request::EsmRequest; +pub use convert_to_confidential_vm_request::ConvertToConfidentialVm; pub use guest_load_page_fault_request::GuestLoadPageFaultRequest; pub use guest_load_page_fault_result::GuestLoadPageFaultResult; pub use guest_store_page_fault_request::GuestStorePageFaultRequest; @@ -19,7 +19,7 @@ pub use share_page_result::SharePageResult; pub use terminate_request::TerminateRequest; pub use trap_reason::TrapReason; -mod esm_request; +mod convert_to_confidential_vm_request; mod guest_load_page_fault_request; mod guest_load_page_fault_result; mod guest_store_page_fault_request; diff --git a/security-monitor/src/core/transformations/share_page_request.rs b/security-monitor/src/core/transformations/share_page_request.rs index e2d317e..bc7626d 100644 --- a/security-monitor/src/core/transformations/share_page_request.rs +++ b/security-monitor/src/core/transformations/share_page_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::memory_partitioner::PageSize; +use crate::core::memory_protector::PageSize; use crate::error::Error; #[derive(PartialEq)] diff --git a/security-monitor/src/error.rs b/security-monitor/src/error.rs index 2b6e9ae..d0a2e2b 100644 --- a/security-monitor/src/error.rs +++ b/security-monitor/src/error.rs @@ -18,10 +18,6 @@ pub const NOT_INITIALIZED_HARTS: &str = pub const NOT_INITIALIZED_CONTROL_DATA: &str = "Bug. Could not access the control data static variable because it is not initialized"; -pub const NOT_INITIALIZED_MEMORY_TRACKER: &str = "Bug. Could not access memory tracker because it is not initialized"; -pub const NOT_INITIALIZED_CONFIDENTIAL_MEMORY: &str = - "Bug. Could not access confidential memory start/end addresses because they were not initialized"; - #[derive(Error, Debug)] pub enum Error { #[error("security monitor initialization error")] diff --git a/security-monitor/src/non_confidential_flow/control_flow/mod.rs b/security-monitor/src/non_confidential_flow/control_flow/mod.rs index 570d229..cc41f08 100644 --- a/security-monitor/src/non_confidential_flow/control_flow/mod.rs +++ b/security-monitor/src/non_confidential_flow/control_flow/mod.rs @@ -3,7 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 use crate::confidential_flow::ConfidentialFlow; use crate::core::control_data::{ControlData, HardwareHart}; -use crate::core::memory_partitioner::MemoryPartitioner; use crate::core::transformations::{ExposeToHypervisor, ResumeRequest}; use crate::error::Error; @@ -31,23 +30,16 @@ impl<'a> NonConfidentialFlow<'a> { match ControlData::try_confidential_vm(confidential_vm_id, |mut cvm| { cvm.steal_confidential_hart(confidential_hart_id, self.hardware_hart) }) { - Ok(()) => { - // It is safe to invoke below unsafe code because at this point - // we are in the confidential flow part of the finite state machine - // and virtual hart is assigned to the hardware hart. We must - // reconfigure hardware the memory isolation mechanism to enforce - // correct memory access permissions. - let hgatp = self.hardware_hart.confidential_hart().hgatp(); - unsafe { MemoryPartitioner::enable_confidential_vm_memory_view(hgatp) }; - Ok(ConfidentialFlow::create(self.hardware_hart)) - } + Ok(()) => Ok(ConfidentialFlow::create(self.hardware_hart)), Err(error) => Err((self, error)), } } pub fn route(self) -> ! { use crate::core::transformations::TrapReason; - use crate::non_confidential_flow::handlers::{esm, invalid_call, opensbi, resume, terminate, vm_hypercall}; + use crate::non_confidential_flow::handlers::{ + convert_to_confidential_vm, invalid_call, opensbi, resume, terminate, vm_hypercall, + }; use crate::ACE_EXT_ID; const ESM_FID: usize = 1000; const RESUME_FID: usize = 1010; @@ -55,7 +47,9 @@ impl<'a> NonConfidentialFlow<'a> { match self.hardware_hart.trap_reason() { TrapReason::Interrupt => opensbi::handle(self.hardware_hart.opensbi_request(), self), - TrapReason::VsEcall(ACE_EXT_ID, ESM_FID) => esm::handle(self.hardware_hart.esm_request(), self), + TrapReason::VsEcall(ACE_EXT_ID, ESM_FID) => { + convert_to_confidential_vm::handle(self.hardware_hart.convert_to_confidential_vm_request(), self) + } TrapReason::VsEcall(ACE_EXT_ID, function_id) => invalid_call::handle(self, ACE_EXT_ID, function_id), TrapReason::VsEcall(_, _) => vm_hypercall::handle(self.hardware_hart.sbi_vm_request(), self), TrapReason::HsEcall(ACE_EXT_ID, RESUME_FID) => resume::handle(self.hardware_hart.resume_request(), self), diff --git a/security-monitor/src/non_confidential_flow/handlers/esm.rs b/security-monitor/src/non_confidential_flow/handlers/convert_to_confidential_vm.rs similarity index 60% rename from security-monitor/src/non_confidential_flow/handlers/esm.rs rename to security-monitor/src/non_confidential_flow/handlers/convert_to_confidential_vm.rs index 9cc4f70..c7a4bd5 100644 --- a/security-monitor/src/non_confidential_flow/handlers/esm.rs +++ b/security-monitor/src/non_confidential_flow/handlers/convert_to_confidential_vm.rs @@ -2,32 +2,34 @@ // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 use crate::core::control_data::{ConfidentialHart, ConfidentialVmId, ControlData}; -use crate::core::memory_partitioner::{NonConfidentialMemoryAddress, PagingSystem, RootPageTable}; -use crate::core::transformations::{EsmRequest, ExposeToHypervisor, SbiRequest}; +use crate::core::memory_protector::ConfidentialVmMemoryProtector; +use crate::core::transformations::{ConvertToConfidentialVm, ExposeToHypervisor, SbiRequest}; use crate::error::Error; use crate::non_confidential_flow::NonConfidentialFlow; const BOOT_HART_ID: usize = 0; -pub fn handle(esm_request: EsmRequest, non_confidential_flow: NonConfidentialFlow) -> ! { +pub fn handle( + convert_to_confidential_vm_request: ConvertToConfidentialVm, non_confidential_flow: NonConfidentialFlow, +) -> ! { debug!("Handling enter secure mode (ESM) SM-call"); - let transformation = match create_confidential_vm(esm_request) { + let transformation = match create_confidential_vm(convert_to_confidential_vm_request) { Ok(id) => ExposeToHypervisor::SbiRequest(SbiRequest::kvm_ace_register(id, BOOT_HART_ID)), Err(error) => error.into_non_confidential_transformation(), }; non_confidential_flow.exit_to_hypervisor(transformation) } -fn create_confidential_vm(esm_request: EsmRequest) -> Result { - let (hgatp, hart_state) = esm_request.into(); - let paging_mode = hgatp.mode().ok_or_else(|| Error::UnsupportedPagingMode())?; - let paging_system = PagingSystem::from(&paging_mode).ok_or_else(|| Error::UnsupportedPagingMode())?; - let root_page_address = NonConfidentialMemoryAddress::new(hgatp.address() as *mut usize)?; +fn create_confidential_vm( + convert_to_confidential_vm_request: ConvertToConfidentialVm, +) -> Result { + let hart_state = convert_to_confidential_vm_request.into(); + + let memory_protector = ConfidentialVmMemoryProtector::from_vm_state(&hart_state)?; + // TODO: read number of harts from fdt let confidential_harts_count = 1; - let root_page_table = RootPageTable::copy_from_non_confidential_memory(root_page_address, paging_system)?; - // create virtual processor for this confidential VM let confidential_harts = (0..confidential_harts_count) .map(|confidential_hart_id| match confidential_hart_id { @@ -40,7 +42,7 @@ fn create_confidential_vm(esm_request: EsmRequest) -> Result, IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 -pub mod esm; +pub mod convert_to_confidential_vm; pub mod invalid_call; pub mod opensbi; pub mod resume;