Skip to content

Commit

Permalink
Support for CVM's untrusted MMIO regions (#60)
Browse files Browse the repository at this point in the history
Added support for CoVE COVG ABI that enables CVM to add and remove untrusted MMIO regions.

---------

Signed-off-by: Wojciech Ozga <woz@zurich.ibm.com>
  • Loading branch information
wojciechozga authored May 30, 2024
1 parent 46fb836 commit 3a44d06
Show file tree
Hide file tree
Showing 13 changed files with 847 additions and 29 deletions.
657 changes: 657 additions & 0 deletions security-monitor/Cargo.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// SPDX-FileCopyrightText: 2023 IBM Corporation
// SPDX-FileContributor: Wojciech Ozga <woz@zurich.ibm.com>, IBM Research - Zurich
// SPDX-License-Identifier: Apache-2.0
use crate::confidential_flow::handlers::mmio::MmioAccessFault;
use crate::confidential_flow::handlers::sbi::SbiResponse;
use crate::confidential_flow::handlers::virtual_instructions::VirtualInstruction;

/// Transformation of the confidential hart state in a response to processing of a confidential hart call.
pub enum ApplyToConfidentialHart {
MmioAccessFault(MmioAccessFault),
SbiResponse(SbiResponse),
VirtualInstruction(VirtualInstruction),
}
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ impl<'a> ConfidentialFlow<'a> {
/// execute the confidential hart on the hardware hart.
pub fn apply_and_exit_to_confidential_hart(mut self, transformation: ApplyToConfidentialHart) -> ! {
match transformation {
ApplyToConfidentialHart::MmioAccessFault(v) => v.apply_to_confidential_hart(self.confidential_hart_mut()),
ApplyToConfidentialHart::SbiResponse(v) => v.apply_to_confidential_hart(self.confidential_hart_mut()),
ApplyToConfidentialHart::VirtualInstruction(v) => v.apply_to_confidential_hart(self.confidential_hart_mut()),
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// SPDX-FileCopyrightText: 2023 IBM Corporation
// SPDX-FileContributor: Wojciech Ozga <woz@zurich.ibm.com>, IBM Research - Zurich
// SPDX-License-Identifier: Apache-2.0
use crate::confidential_flow::handlers::sbi::SbiResponse;
use crate::confidential_flow::handlers::sbi::{SbiRequest, SbiResponse};
use crate::confidential_flow::{ApplyToConfidentialHart, ConfidentialFlow};
use crate::core::architecture::sbi::CovgExtension;
use crate::core::architecture::GeneralPurposeRegister;
use crate::core::control_data::ConfidentialHart;
use crate::core::control_data::{ConfidentialHart, ConfidentialVmMmioRegion, ControlData, PendingRequest};
use crate::non_confidential_flow::DeclassifyToHypervisor;

pub struct AddMmioRegion {
region_start_address: usize,
Expand All @@ -20,10 +22,20 @@ impl AddMmioRegion {
}

pub fn handle(self, confidential_flow: ConfidentialFlow) -> ! {
// TODO: make sure region_start_address is aligned to 4KiB
// TODO: make sure the region_start_address is a valid guest address
// TODO: make sure this region does not overlap with any other previously defined region
let transformation = ApplyToConfidentialHart::SbiResponse(SbiResponse::success());
confidential_flow.apply_and_exit_to_confidential_hart(transformation)
match ControlData::try_confidential_vm(confidential_flow.confidential_vm_id(), |mut confidential_vm| {
Ok(confidential_vm.add_mmio_region(ConfidentialVmMmioRegion::new(self.region_start_address, self.region_length)?)?)
}) {
Ok(_) => confidential_flow
.set_pending_request(PendingRequest::SbiRequest())
.into_non_confidential_flow()
.declassify_and_exit_to_hypervisor(DeclassifyToHypervisor::SbiRequest(self.sbi_add_mmio_region())),
Err(error) => {
confidential_flow.apply_and_exit_to_confidential_hart(ApplyToConfidentialHart::SbiResponse(SbiResponse::error(error)))
}
}
}

fn sbi_add_mmio_region(&self) -> SbiRequest {
SbiRequest::new(CovgExtension::EXTID, CovgExtension::SBI_EXT_COVG_ADD_MMIO_REGION, self.region_start_address, self.region_length)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: 2023 IBM Corporation
// SPDX-FileContributor: Wojciech Ozga <woz@zurich.ibm.com>, IBM Research - Zurich
// SPDX-License-Identifier: Apache-2.0
use crate::core::control_data::{ConfidentialHart, ConfidentialVmId, ConfidentialVmMmioRegion, ControlData};
use core::mem;

pub struct MmioAccessFault {
cause: usize,
mtval: usize,
instruction_length: usize,
}

impl MmioAccessFault {
pub const ADDRESS_ALIGNMENT: usize = mem::size_of::<usize>();

pub fn new(cause: usize, mtval: usize, instruction_length: usize) -> Self {
Self { cause, mtval, instruction_length }
}

pub fn apply_to_confidential_hart(&self, confidential_hart: &mut ConfidentialHart) {
let mepc = confidential_hart.csrs().mepc.read_value() + self.instruction_length;
confidential_hart.csrs_mut().vsepc.set(mepc);
let trap_vector_address = confidential_hart.csrs().vstvec.read();
confidential_hart.csrs_mut().mepc.save_value(trap_vector_address);
confidential_hart.csrs_mut().vscause.set(self.cause);
confidential_hart.csrs_mut().vstval.set(self.mtval);
}

pub fn tried_to_access_valid_mmio_region(confidential_vm_id: ConfidentialVmId, fault_address: usize) -> bool {
ControlData::try_confidential_vm(confidential_vm_id, |confidential_vm| {
Ok(confidential_vm.is_mmio_region_defined(&ConfidentialVmMmioRegion::new(fault_address, Self::ADDRESS_ALIGNMENT)?))
})
.unwrap_or(false)
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// SPDX-FileCopyrightText: 2023 IBM Corporation
// SPDX-FileContributor: Wojciech Ozga <woz@zurich.ibm.com>, IBM Research - Zurich
// SPDX-License-Identifier: Apache-2.0
use crate::confidential_flow::handlers::mmio::MmioLoadPending;
use crate::confidential_flow::handlers::mmio::{MmioAccessFault, MmioLoadPending};
use crate::confidential_flow::handlers::sbi::SbiResponse;
use crate::confidential_flow::ConfidentialFlow;
use crate::confidential_flow::{ApplyToConfidentialHart, ConfidentialFlow};
use crate::core::architecture::is_bit_enabled;
use crate::core::architecture::specification::CAUSE_LOAD_ACCESS;
use crate::core::control_data::{ConfidentialHart, HypervisorHart, PendingRequest};
use crate::non_confidential_flow::DeclassifyToHypervisor;

Expand Down Expand Up @@ -32,6 +33,12 @@ impl MmioLoadRequest {
let instruction = self.mtinst | 0x3;
let instruction_length = if is_bit_enabled(self.mtinst, 1) { riscv_decode::instruction_length(instruction as u16) } else { 2 };

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

match crate::core::architecture::decode_result_register(instruction) {
Ok(gpr) => confidential_flow
.set_pending_request(PendingRequest::MmioLoad(MmioLoadPending::new(instruction_length, gpr)))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// SPDX-FileCopyrightText: 2023 IBM Corporation
// SPDX-FileContributor: Wojciech Ozga <woz@zurich.ibm.com>, IBM Research - Zurich
// SPDX-License-Identifier: Apache-2.0
use crate::confidential_flow::handlers::mmio::MmioStorePending;
use crate::confidential_flow::handlers::mmio::{MmioAccessFault, MmioStorePending};
use crate::confidential_flow::handlers::sbi::SbiResponse;
use crate::confidential_flow::ConfidentialFlow;
use crate::confidential_flow::{ApplyToConfidentialHart, ConfidentialFlow};
use crate::core::architecture::specification::CAUSE_STORE_ACCESS;
use crate::core::architecture::{is_bit_enabled, GeneralPurposeRegister};
use crate::core::control_data::{ConfidentialHart, HypervisorHart, PendingRequest};
use crate::error::Error;
Expand Down Expand Up @@ -33,10 +34,17 @@ impl MmioStoreRequest {
let instruction_length = if is_bit_enabled(mtinst, 1) { riscv_decode::instruction_length(instruction as u16) } else { 2 };
let gpr = crate::core::architecture::decode_result_register(instruction);
let gpr_value = gpr.as_ref().and_then(|ref gpr| Ok(confidential_hart.gprs().read(**gpr))).unwrap_or(0);

Self { mcause, mtval, mtval2, mtinst, instruction_length, gpr, gpr_value }
}

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

match self.gpr {
Ok(_) => confidential_flow
.set_pending_request(PendingRequest::MmioStore(MmioStorePending::new(self.instruction_length)))
Expand Down
2 changes: 2 additions & 0 deletions security-monitor/src/confidential_flow/handlers/mmio/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-FileContributor: Wojciech Ozga <woz@zurich.ibm.com>, IBM Research - Zurich
// SPDX-License-Identifier: Apache-2.0
pub use crate::confidential_flow::handlers::mmio::add_mmio_region::AddMmioRegion;
pub use crate::confidential_flow::handlers::mmio::mmio_access_fault::MmioAccessFault;
pub use crate::confidential_flow::handlers::mmio::mmio_load_pending::MmioLoadPending;
pub use crate::confidential_flow::handlers::mmio::mmio_load_request::MmioLoadRequest;
pub use crate::confidential_flow::handlers::mmio::mmio_load_response::MmioLoadResponse;
Expand All @@ -11,6 +12,7 @@ pub use crate::confidential_flow::handlers::mmio::mmio_store_response::MmioStore
pub use crate::confidential_flow::handlers::mmio::remove_mmio_region::RemoveMmioRegion;

mod add_mmio_region;
mod mmio_access_fault;
mod mmio_load_pending;
mod mmio_load_request;
mod mmio_load_response;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// SPDX-FileCopyrightText: 2023 IBM Corporation
// SPDX-FileContributor: Wojciech Ozga <woz@zurich.ibm.com>, IBM Research - Zurich
// SPDX-License-Identifier: Apache-2.0
use crate::confidential_flow::handlers::sbi::SbiResponse;
use crate::confidential_flow::handlers::sbi::{SbiRequest, SbiResponse};
use crate::confidential_flow::{ApplyToConfidentialHart, ConfidentialFlow};
use crate::core::architecture::sbi::CovgExtension;
use crate::core::architecture::GeneralPurposeRegister;
use crate::core::control_data::ConfidentialHart;
use crate::core::control_data::{ConfidentialHart, ConfidentialVmMmioRegion, ControlData, PendingRequest};
use crate::non_confidential_flow::DeclassifyToHypervisor;

pub struct RemoveMmioRegion {
region_start_address: usize,
Expand All @@ -20,7 +22,20 @@ impl RemoveMmioRegion {
}

pub fn handle(self, confidential_flow: ConfidentialFlow) -> ! {
let transformation = ApplyToConfidentialHart::SbiResponse(SbiResponse::success());
confidential_flow.apply_and_exit_to_confidential_hart(transformation)
match ControlData::try_confidential_vm(confidential_flow.confidential_vm_id(), |mut confidential_vm| {
Ok(confidential_vm.remove_mmio_region(&ConfidentialVmMmioRegion::new(self.region_start_address, self.region_length)?))
}) {
Ok(_) => confidential_flow
.set_pending_request(PendingRequest::SbiRequest())
.into_non_confidential_flow()
.declassify_and_exit_to_hypervisor(DeclassifyToHypervisor::SbiRequest(self.sbi_remove_mmio_region())),
Err(error) => {
confidential_flow.apply_and_exit_to_confidential_hart(ApplyToConfidentialHart::SbiResponse(SbiResponse::error(error)))
}
}
}

fn sbi_remove_mmio_region(&self) -> SbiRequest {
SbiRequest::new(CovgExtension::EXTID, CovgExtension::SBI_EXT_COVG_REMOVE_MMIO_REGION, self.region_start_address, self.region_length)
}
}
73 changes: 59 additions & 14 deletions security-monitor/src/core/control_data/confidential_vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// SPDX-License-Identifier: Apache-2.0
use crate::core::architecture::HartLifecycleState;
use crate::core::control_data::{
ConfidentialHart, ConfidentialHartRemoteCommand, ConfidentialVmId, ConfidentialVmMeasurement, HardwareHart,
ConfidentialHart, ConfidentialHartRemoteCommand, ConfidentialVmId, ConfidentialVmMeasurement, ConfidentialVmMmioRegion, HardwareHart,
};
use crate::core::interrupt_controller::InterruptController;
use crate::core::memory_protector::ConfidentialVmMemoryProtector;
Expand All @@ -19,15 +19,21 @@ pub struct ConfidentialVm {
confidential_hart_remote_commands: BTreeMap<usize, Mutex<Vec<ConfidentialHartRemoteCommand>>>,
memory_protector: ConfidentialVmMemoryProtector,
allowed_external_interrupts: usize,
mmio_regions: Vec<ConfidentialVmMmioRegion>,
}

impl ConfidentialVm {
pub const MAX_NUMBER_OF_HARTS_PER_VM: usize = 1024;

/// An average number of inter hart requests that can be buffered before being processed.
const AVG_NUMBER_OF_REMOTE_HART_REQUESTS: usize = 3;

/// A maximum number of inter hart requests that can be buffered.
const MAX_NUMBER_OF_REMOTE_HART_REQUESTS: usize = 64;

/// A maximum number of MMIO regions that a confidential VM can register
const MAX_NUMBER_OF_MMIO_REGIONS: usize = 1024;

/// Constructs a new confidential VM.
///
/// # Safety
Expand All @@ -45,9 +51,33 @@ impl ConfidentialVm {
(confidential_hart.confidential_hart_id(), Mutex::new(Vec::with_capacity(Self::AVG_NUMBER_OF_REMOTE_HART_REQUESTS)))
})
.collect();
Self { id, measurements, confidential_harts, memory_protector, confidential_hart_remote_commands, allowed_external_interrupts: 0 }
let mmio_regions = Vec::with_capacity(8);
Self {
id,
measurements,
confidential_harts,
memory_protector,
confidential_hart_remote_commands,
allowed_external_interrupts: 0,
mmio_regions,
}
}

pub fn confidential_vm_id(&self) -> ConfidentialVmId {
self.id
}

pub fn memory_protector_mut(&mut self) -> &mut ConfidentialVmMemoryProtector {
&mut self.memory_protector
}

pub(super) fn deallocate(self) {
self.memory_protector.into_root_page_table().deallocate();
}
}

/* Heavy context switches */
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 or is in the `Stopped` state.
Expand Down Expand Up @@ -111,23 +141,39 @@ impl ConfidentialVm {
// finite state machine to the non-confidential part and the virtual hart is still assigned to the hardware hart.
unsafe { hardware_hart.hypervisor_hart().enable_hypervisor_memory_protector() };
}
}

pub fn confidential_vm_id(&self) -> ConfidentialVmId {
self.id
}

pub fn memory_protector_mut(&mut self) -> &mut ConfidentialVmMemoryProtector {
&mut self.memory_protector
}

/* Interrupt related */
impl ConfidentialVm {
pub fn allowed_external_interrupts(&self) -> usize {
self.allowed_external_interrupts
}

pub fn set_allowed_external_interrupts(&mut self, allowed_external_interrupts: usize) {
self.allowed_external_interrupts |= allowed_external_interrupts;
}
}

/* Management of untrusted MMIO regions */
impl ConfidentialVm {
pub fn add_mmio_region(&mut self, region: ConfidentialVmMmioRegion) -> Result<(), Error> {
ensure!(self.mmio_regions.len() < Self::MAX_NUMBER_OF_MMIO_REGIONS, Error::ReachedMaxNumberOfMmioRegions())?;
ensure!(!self.mmio_regions.iter().any(|x| x.overlaps(&region)), Error::OverlappingMmioRegion())?;
self.mmio_regions.push(region);
Ok(())
}

pub fn remove_mmio_region(&mut self, region: &ConfidentialVmMmioRegion) {
self.mmio_regions.retain(|x| !x.overlaps(region));
}

pub fn is_mmio_region_defined(&self, region: &ConfidentialVmMmioRegion) -> bool {
self.mmio_regions.iter().any(|x| x.contains(region))
}
}

/* Lifecycle related */
impl ConfidentialVm {
pub fn are_all_harts_shutdown(&self) -> bool {
self.confidential_harts.iter().filter(|hart| hart.lifecycle_state() != &HartLifecycleState::PoweredOff).count() == 0
}
Expand All @@ -144,7 +190,10 @@ impl ConfidentialVm {
let hart = self.confidential_harts.get_mut(confidential_hart_id).ok_or(Error::InvalidHartId())?;
hart.transition_from_stopped_to_started(start_address, opaque)
}
}

/* Remote commands */
impl ConfidentialVm {
/// 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.
Expand Down Expand Up @@ -184,10 +233,6 @@ impl ConfidentialVm {
})
}

pub(super) fn deallocate(self) {
self.memory_protector.into_root_page_table().deallocate();
}

pub fn try_confidential_hart_remote_commands<F, O>(&mut self, confidential_hart_id: usize, op: O) -> Result<F, Error>
where O: FnOnce(MutexGuard<'_, Vec<ConfidentialHartRemoteCommand>>) -> Result<F, Error> {
op(self.confidential_hart_remote_commands.get(&confidential_hart_id).ok_or(Error::InvalidHartId())?.lock())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: 2023 IBM Corporation
// SPDX-FileContributor: Wojciech Ozga <woz@zurich.ibm.com>, IBM Research - Zurich
// SPDX-License-Identifier: Apache-2.0
use crate::error::Error;

#[derive(Debug)]
pub struct ConfidentialVmMmioRegion {
pub region_start_address: usize,
pub region_length: usize,
}

impl ConfidentialVmMmioRegion {
pub fn new(region_start_address: usize, region_length: usize) -> Result<Self, Error> {
// TODO: make sure region_start_address is aligned to 4KiB
// TODO: make sure the region_start_address is a valid guest address
Ok(Self { region_start_address, region_length })
}

pub fn overlaps(&self, other: &Self) -> bool {
self.region_start_address < other.region_start_address + other.region_length
&& other.region_start_address < self.region_start_address + self.region_length
}

pub fn contains(&self, other: &Self) -> bool {
self.region_start_address <= other.region_start_address
&& other.region_start_address + other.region_length < self.region_start_address + self.region_length
}
}
2 changes: 2 additions & 0 deletions security-monitor/src/core/control_data/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub use confidential_hart_remote_command::{ConfidentialHartRemoteCommand, Confid
pub use confidential_vm::ConfidentialVm;
pub use confidential_vm_id::ConfidentialVmId;
pub use confidential_vm_measurement::ConfidentialVmMeasurement;
pub use confidential_vm_mmio_region::ConfidentialVmMmioRegion;
pub use hardware_hart::{HardwareHart, HART_STACK_ADDRESS_OFFSET};
pub use hypervisor_hart::HypervisorHart;
pub use pending_request::PendingRequest;
Expand All @@ -16,6 +17,7 @@ mod confidential_hart_remote_command;
mod confidential_vm;
mod confidential_vm_id;
mod confidential_vm_measurement;
mod confidential_vm_mmio_region;
mod hardware_hart;
mod hypervisor_hart;
mod pending_request;
Expand Down
Loading

0 comments on commit 3a44d06

Please sign in to comment.