Skip to content

Commit 2abb196

Browse files
authored
Performance optimizations (#95)
* fixed bug when running multi vCPU CVMs, optimized IPI code and context switch * improve performance by reducing cost of operation in critical section * add fine grained locking mechanism * optimize access to memory protector --------- Signed-off-by: Wojciech Ozga <[email protected]>
1 parent d9065ef commit 2abb196

File tree

32 files changed

+360
-343
lines changed

32 files changed

+360
-343
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#!/usr/bin/env bash
2+
# SPDX-FileCopyrightText: 2023 IBM Corporation
3+
# SPDX-FileContributor: Wojciech Ozga <[email protected]>, IBM Research - Zurich
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
if [ ! -f "/root/linux_vm2" ]; then
7+
cp -rf /root/linux_vm /root/linux_vm2
8+
fi
9+
10+
QEMU_CMD=qemu-system-riscv64
11+
KERNEL=/root/linux_vm2/Image
12+
DRIVE=/root/linux_vm2/rootfs.ext2
13+
INITRAMFS=/root/linux_vm2/rootfs.cpio
14+
TAP=/root/linux_vm2/cove_tap_qemu
15+
16+
HOST_PORT="$((3000 + RANDOM % 3000))"
17+
INTERACTIVE="-nographic"
18+
SMP=2
19+
MEMORY=1G
20+
21+
for i in "$@"; do
22+
case $i in
23+
-e=*|--debug-port=*)
24+
DEBUG_PORT="${i#*=}"
25+
DEBUG_OPTIONS="-gdb tcp::${DEBUG_PORT} -S -d in_asm -D debug.log"
26+
echo ${DEBUG_OPTIONS}
27+
shift
28+
;;
29+
--host-port=*)
30+
HOST_PORT="${i#*=}"
31+
shift
32+
;;
33+
-s=*|--smp=*)
34+
SMP="${i#*=}"
35+
shift
36+
;;
37+
-m=*|--memory=*)
38+
MEMORY="${i#*=}"
39+
shift
40+
;;
41+
--daemonize*)
42+
INTERACTIVE="-daemonize"
43+
shift
44+
;;
45+
-*|--*)
46+
echo "Unknown option $i"
47+
exit 1
48+
;;
49+
*)
50+
;;
51+
esac
52+
done
53+
54+
echo "SSH port: ${HOST_PORT}"
55+
echo "Number of cores assigned to the guest: ${SMP}"
56+
57+
${QEMU_CMD} ${DEBUG_OPTIONS} \
58+
${INTERACTIVE} \
59+
--enable-kvm \
60+
-machine virt,cove=true,cove-tap-filename=${TAP} -cpu rv64,f=true -smp ${SMP} -m ${MEMORY} \
61+
-kernel ${KERNEL} \
62+
-seed 0 \
63+
-global virtio-mmio.force-legacy=false \
64+
-append "console=ttyS0 ro root=/dev/vda swiotlb=mmnn,force" \
65+
-device virtio-blk-pci,drive=hd0,iommu_platform=on,disable-legacy=on,disable-modern=off \
66+
-drive if=none,format=raw,file=${DRIVE},id=hd0 \
67+
-device virtio-net-pci,netdev=net0,iommu_platform=on,disable-legacy=on,disable-modern=off \
68+
-netdev user,id=net0,net=192.168.100.1/24,dhcpstart=192.168.100.128,hostfwd=tcp::${HOST_PORT}-:22 \
69+
-nographic

security-monitor/src/confidential_flow/finite_state_machine.rs

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use crate::core::control_data::{
3131
ConfidentialHart, ConfidentialHartRemoteCommand, ConfidentialVm, ConfidentialVmId, ControlDataStorage, HardwareHart, HypervisorHart,
3232
ResumableOperation,
3333
};
34+
use crate::core::interrupt_controller::InterruptController;
3435
use crate::error::Error;
3536
use crate::non_confidential_flow::{DeclassifyToHypervisor, NonConfidentialFlow};
3637

@@ -118,11 +119,18 @@ impl<'a> ConfidentialFlow<'a> {
118119
hardware_hart: &'a mut HardwareHart, confidential_vm_id: ConfidentialVmId, confidential_hart_id: usize,
119120
) -> Result<(usize, Self), (&'a mut HardwareHart, Error)> {
120121
assert!(hardware_hart.confidential_hart().is_dummy());
121-
match ControlDataStorage::try_confidential_vm(confidential_vm_id, |mut confidential_vm| {
122+
// Now, we are going to change the context between security domains.
123+
// 1) Store the hypervisor hart state that executed on this physical hart to the main memory.
124+
hardware_hart.hypervisor_hart_mut().save_in_main_memory();
125+
match ControlDataStorage::try_confidential_vm(confidential_vm_id, |confidential_vm| {
122126
confidential_vm.steal_confidential_hart(confidential_hart_id, hardware_hart)?;
123127
Ok(confidential_vm.allowed_external_interrupts())
124128
}) {
125-
Ok(allowed_external_interrupts) => Ok((allowed_external_interrupts, Self { hardware_hart })),
129+
Ok(allowed_external_interrupts) => {
130+
// 2) Load confidential hart state from main memory to the physical hart executing this code.
131+
hardware_hart.confidential_hart_mut().restore_from_main_memory();
132+
Ok((allowed_external_interrupts, Self { hardware_hart }))
133+
}
126134
Err(error) => Err((hardware_hart, error)),
127135
}
128136
}
@@ -134,14 +142,20 @@ impl<'a> ConfidentialFlow<'a> {
134142
let declassifier =
135143
DeclassifyToHypervisor::EnabledInterrupts(ExposeEnabledInterrupts::from_confidential_hart(self.confidential_hart()));
136144

137-
ControlDataStorage::try_confidential_vm(self.confidential_vm_id(), |mut confidential_vm| {
138-
// Run heavy context switch when giving back the confidential hart to the confidential VM.
139-
confidential_vm.return_confidential_hart(self.hardware_hart);
140-
Ok(NonConfidentialFlow::create(self.hardware_hart).declassify_to_hypervisor_hart(declassifier))
145+
// Now, we are going to change the context between security domains.
146+
// 1) Store the confidential hart state that executed on this physical hart to the main memory.
147+
self.hardware_hart.confidential_hart_mut().save_in_main_memory();
148+
let _ = ControlDataStorage::try_confidential_vm(self.confidential_vm_id(), |confidential_vm| {
149+
Ok(confidential_vm.return_confidential_hart(self.hardware_hart))
141150
})
142151
// Below unwrap is safe because we are in the confidential flow that guarantees that the confidential VM with
143152
// the given id exists in the control data.
144-
.unwrap()
153+
.unwrap();
154+
// Enable memory access control for the hypervisor
155+
unsafe { self.hardware_hart.hypervisor_hart().enable_hypervisor_memory_protector() };
156+
// 2) Load hypervisor hart state from main memory to the physical hart executing this code.
157+
self.hardware_hart.hypervisor_hart_mut().restore_from_main_memory();
158+
NonConfidentialFlow::create(self.hardware_hart).declassify_to_hypervisor_hart(declassifier)
145159
}
146160

147161
/// Resumes execution of the confidential hart after the confidential hart was not running on any physical hart.
@@ -162,7 +176,7 @@ impl<'a> ConfidentialFlow<'a> {
162176
// load) to the hypervisor. We must handle the response or resume confidential hart's execution.
163177
use crate::core::control_data::ResumableOperation::*;
164178
match self.confidential_hart_mut().take_resumable_operation() {
165-
Some(SbiRequest()) => SbiResponse::from_hypervisor_hart(self.hypervisor_hart()).handle(self),
179+
Some(SbiRequest()) => SbiResponse::success().handle(self),
166180
Some(ResumeHart(v)) => v.handle(self),
167181
Some(MmioLoad(v)) => MmioLoadResponse::from_hypervisor_hart(self.hypervisor_hart(), v).handle(self),
168182
Some(MmioStore(v)) => MmioStoreResponse::from_hypervisor_hart(self.hypervisor_hart(), v).handle(self),
@@ -200,8 +214,7 @@ impl<'a> ConfidentialFlow<'a> {
200214
// We must restore the control and status registers (CSRs) that might have changed during execution of the security monitor.
201215
// We call it here because it is just before exiting to the assembly context switch, so we are sure that these CSRs have their
202216
// final values.
203-
let interrupts =
204-
self.confidential_hart().csrs().hvip.read_from_main_memory() | self.confidential_hart().csrs().vsip.read_from_main_memory();
217+
let interrupts = self.confidential_hart().csrs().hvip.read_from_main_memory() | self.confidential_hart().csrs().pending_vssip_irqs;
205218
let address = self.confidential_hart_mut().address();
206219
self.confidential_hart().csrs().hvip.write(interrupts);
207220
self.confidential_hart().csrs().sscratch.write(address);
@@ -214,32 +227,44 @@ impl<'a> ConfidentialFlow<'a> {
214227
/// Broadcasts the inter hart request to confidential harts of the currently executing confidential VM. Returns error if sending an IPI
215228
/// to other confidential hart failed or if there is too many pending IPI queued.
216229
pub fn broadcast_remote_command(
217-
&mut self, confidential_vm: &mut ConfidentialVm, confidential_hart_remote_command: ConfidentialHartRemoteCommand,
230+
&mut self, confidential_vm: &ConfidentialVm, confidential_hart_remote_command: ConfidentialHartRemoteCommand,
218231
) -> Result<(), Error> {
219232
let sender_confidential_hart_id = self.hardware_hart.confidential_hart().confidential_hart_id();
220233
// check if the remote command is also dedicated for the currently executing confidential hart
221234
if confidential_hart_remote_command.is_hart_selected(sender_confidential_hart_id) {
222235
self.hardware_hart.confidential_hart_mut().execute(&confidential_hart_remote_command);
223236
}
224237
// For the time-being, we rely on the OpenSBI's implementation of broadcasting IPIs to hardware harts.
225-
self.hardware_hart
226-
.opensbi_context(|| confidential_vm.broadcast_remote_command(sender_confidential_hart_id, confidential_hart_remote_command))
238+
let hart_mask = confidential_vm.broadcast_remote_command(sender_confidential_hart_id, confidential_hart_remote_command)?;
239+
if hart_mask != 0 {
240+
// Confidential harts that should receive an ConfidentialHartRemoteCommand are currently running on a hardware
241+
// harts. We interrupt such hardware harts with IPIs. Consequently, hardware harts running target confidential
242+
// harts will trap into the security monitor, which will execute ConfidentialHartRemoteCommands on the target harts.
243+
self.hardware_hart.opensbi_context(|| {
244+
InterruptController::try_read(|controller| controller.send_ipis(hart_mask, 0))?;
245+
Ok(())
246+
})?;
247+
}
248+
249+
Ok(())
227250
}
228251

229252
/// Processes pending requests from other confidential harts by applying the corresponding state transformation to
230253
/// this confidential hart.
231254
///
232255
/// This function must only be called when the hypervisor requested resume of confidential hart's execution or when
233256
/// a hardware hart executing a confidential hart is interrupted with the inter-processor-interrupt (IPI).
234-
fn process_confidential_hart_remote_commands(&mut self) {
235-
ControlDataStorage::try_confidential_vm(self.confidential_vm_id(), |mut confidential_vm| {
257+
pub fn process_confidential_hart_remote_commands(&mut self) -> bool {
258+
let mut requests_processed = false;
259+
ControlDataStorage::try_confidential_vm(self.confidential_vm_id(), |confidential_vm| {
236260
confidential_vm.try_confidential_hart_remote_commands(
237261
self.confidential_hart_id(),
238262
|ref mut confidential_hart_remote_commands| {
239263
confidential_hart_remote_commands.drain(..).for_each(|confidential_hart_remote_command| {
240264
// The confidential flow has an ownership of the confidential hart because the confidential hart
241265
// is assigned to the hardware hart.
242266
self.confidential_hart_mut().execute(&confidential_hart_remote_command);
267+
requests_processed = true;
243268
});
244269
Ok(())
245270
},
@@ -249,6 +274,7 @@ impl<'a> ConfidentialFlow<'a> {
249274
// confidential flow of the finite state machine (FSM) that guarantees it and 2) the processing of inter hart
250275
// requests always succeeds.
251276
.unwrap();
277+
requests_processed
252278
}
253279
}
254280

security-monitor/src/confidential_flow/handlers/attestation/retrieve_secret.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ impl RetrieveSecretRequest {
2424
}
2525

2626
pub fn handle(self, confidential_flow: ConfidentialFlow) -> ! {
27-
let transformation = ControlDataStorage::try_confidential_vm(confidential_flow.confidential_vm_id(), |ref mut confidential_vm| {
27+
let transformation = ControlDataStorage::try_confidential_vm(confidential_flow.confidential_vm_id(), |ref confidential_vm| {
2828
// ensure!(self.output_buffer_address.is_aligned_to(PageSize::Size4KiB.in_bytes()), Error::AddressNotAligned())?;
2929
ensure!(self.output_buffer_size <= PageSize::Size4KiB.in_bytes(), Error::AddressNotAligned())?;
3030
let secret = confidential_vm.secret(0)?;

security-monitor/src/confidential_flow/handlers/interrupts/allow_external_interrupt.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ impl AllowExternalInterrupt {
1818
}
1919

2020
pub fn handle(self, confidential_flow: ConfidentialFlow) -> ! {
21-
match ControlDataStorage::try_confidential_vm(confidential_flow.confidential_vm_id(), |mut confidential_vm| {
21+
match ControlDataStorage::try_confidential_vm_mut(confidential_flow.confidential_vm_id(), |mut confidential_vm| {
2222
Ok(confidential_vm.allow_external_interrupt(self.interrupt_id))
2323
}) {
2424
Ok(_) => confidential_flow

security-monitor/src/confidential_flow/handlers/interrupts/expose_enabled_interrupts.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// SPDX-FileCopyrightText: 2023 IBM Corporation
22
// SPDX-FileContributor: Wojciech Ozga <[email protected]>, IBM Research - Zurich
33
// SPDX-License-Identifier: Apache-2.0
4-
use crate::core::architecture::specification::{CSR_VSIE, CSR_VSTIMECMP};
4+
use crate::core::architecture::specification::{CSR_HVIP, CSR_VSIE, CSR_VSTIMECMP};
55
use crate::core::control_data::{ConfidentialHart, HypervisorHart};
66

77
pub struct ExposeEnabledInterrupts {
@@ -16,6 +16,7 @@ impl ExposeEnabledInterrupts {
1616

1717
pub fn declassify_to_hypervisor_hart(&self, hypervisor_hart: &mut HypervisorHart) {
1818
hypervisor_hart.shared_memory_mut().write_csr(CSR_VSIE.into(), self.vsie);
19+
hypervisor_hart.shared_memory_mut().write_csr(CSR_HVIP.into(), 0);
1920
hypervisor_hart.shared_memory_mut().write_csr(CSR_VSTIMECMP.into(), self.vstimecmp);
2021
}
2122
}

security-monitor/src/confidential_flow/handlers/interrupts/handle_interrupt.rs

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,33 @@
33
// SPDX-License-Identifier: Apache-2.0
44
use crate::confidential_flow::handlers::sbi::SbiResponse;
55
use crate::confidential_flow::ConfidentialFlow;
6-
use crate::core::architecture::specification::{MIE_SSIP_MASK, SCAUSE_INTERRUPT_MASK};
6+
use crate::core::architecture::specification::{MIE_MSIP_MASK, MIE_SSIP_MASK, SCAUSE_INTERRUPT_MASK};
7+
use crate::core::architecture::CSR;
78
use crate::core::control_data::{ConfidentialHart, HypervisorHart};
89
use crate::non_confidential_flow::DeclassifyToHypervisor;
910

1011
/// Handles an interrupt that occured during the execution of a confidential hart. The control flows (a) to the hypervisor when an interrupt
1112
/// comes from a hardware device, or (b) to the confidential hart in case of an IPI from other confidential hart.
12-
pub struct HandleInterrupt {
13-
pending_interrupts: usize,
14-
}
13+
pub struct HandleInterrupt();
1514

1615
impl HandleInterrupt {
17-
pub fn from_confidential_hart(confidential_hart: &ConfidentialHart) -> Self {
18-
Self { pending_interrupts: confidential_hart.csrs().mip.read() }
16+
pub fn from_confidential_hart(_confidential_hart: &ConfidentialHart) -> Self {
17+
Self {}
1918
}
2019

21-
pub fn handle(self, confidential_flow: ConfidentialFlow) -> ! {
22-
if self.pending_interrupts & MIE_SSIP_MASK > 0 {
23-
// One of the reasons why the confidential hart was interrupted with SSIP is that it got an `ConfidentialHartRemoteCommand` from
24-
// another confidential hart. If this is the case, we must process all queued requests before resuming confidential
25-
// hart's execution. This is done as part of the procedure that resumes confidential hart execution.
26-
confidential_flow.resume_confidential_hart_execution();
27-
} else {
28-
// The only interrupts that we can see here are:
29-
// * M-mode timer that the security monitor set to preemt execution of a confidential VM
30-
// * M-mode software or external interrupt
31-
confidential_flow.into_non_confidential_flow().declassify_and_exit_to_hypervisor(DeclassifyToHypervisor::Interrupt(self))
20+
pub fn handle(self, mut confidential_flow: ConfidentialFlow) -> ! {
21+
if CSR.mip.read() & MIE_MSIP_MASK > 0 {
22+
if confidential_flow.process_confidential_hart_remote_commands() {
23+
CSR.mip.read_and_clear_bits(MIE_SSIP_MASK);
24+
confidential_flow.resume_confidential_hart_execution();
25+
}
3226
}
27+
28+
confidential_flow.into_non_confidential_flow().declassify_and_exit_to_hypervisor(DeclassifyToHypervisor::Interrupt(self))
3329
}
3430

3531
pub fn declassify_to_hypervisor_hart(&self, hypervisor_hart: &mut HypervisorHart) {
36-
hypervisor_hart.csrs_mut().scause.write(self.pending_interrupts | SCAUSE_INTERRUPT_MASK);
32+
hypervisor_hart.csrs_mut().scause.read_and_set_bits(SCAUSE_INTERRUPT_MASK);
3733
SbiResponse::success().declassify_to_hypervisor_hart(hypervisor_hart);
3834
}
3935
}

security-monitor/src/confidential_flow/handlers/mmio/add_mmio_region.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ impl AddMmioRegion {
2323
}
2424

2525
pub fn handle(self, confidential_flow: ConfidentialFlow) -> ! {
26-
match ControlDataStorage::try_confidential_vm(confidential_flow.confidential_vm_id(), |mut confidential_vm| {
26+
match ControlDataStorage::try_confidential_vm_mut(confidential_flow.confidential_vm_id(), |mut confidential_vm| {
2727
ensure!(self.region_start_address % PageSize::Size4KiB.in_bytes() == 0, Error::AddressNotAligned())?;
2828
ensure!(self.region_length % PageSize::Size4KiB.in_bytes() == 0, Error::AddressNotAligned())?;
2929
Ok(confidential_vm.add_mmio_region(ConfidentialVmMmioRegion::new(self.region_start_address, self.region_length))?)

security-monitor/src/confidential_flow/handlers/mmio/mmio_access_fault.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,11 @@ impl MmioAccessFault {
2626
}
2727

2828
pub fn handle(self, mut confidential_flow: ConfidentialFlow) -> ! {
29-
match ControlDataStorage::try_confidential_vm_mut(confidential_flow.confidential_vm_id(), |mut confidential_vm| {
29+
match ControlDataStorage::try_confidential_vm(confidential_flow.confidential_vm_id(), |confidential_vm| {
3030
let confidential_vm_physical_address = ConfidentialVmPhysicalAddress::new(self.fault_address);
3131
let page_size = confidential_vm.memory_protector_mut().map_empty_page(confidential_vm_physical_address, PageSize::Size4KiB)?;
3232
let request = RemoteHfenceGvmaVmid::all_harts(None, page_size, confidential_flow.confidential_vm_id());
33-
confidential_flow
34-
.broadcast_remote_command(&mut confidential_vm, ConfidentialHartRemoteCommand::RemoteHfenceGvmaVmid(request))?;
33+
confidential_flow.broadcast_remote_command(&confidential_vm, ConfidentialHartRemoteCommand::RemoteHfenceGvmaVmid(request))?;
3534
Ok(())
3635
}) {
3736
Ok(_) => confidential_flow.resume_confidential_hart_execution(),

security-monitor/src/confidential_flow/handlers/mmio/remove_mmio_region.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ impl RemoveMmioRegion {
2323
}
2424

2525
pub fn handle(self, confidential_flow: ConfidentialFlow) -> ! {
26-
match ControlDataStorage::try_confidential_vm(confidential_flow.confidential_vm_id(), |mut confidential_vm| {
26+
match ControlDataStorage::try_confidential_vm_mut(confidential_flow.confidential_vm_id(), |mut confidential_vm| {
2727
ensure!(self.region_start_address % PageSize::Size4KiB.in_bytes() == 0, Error::AddressNotAligned())?;
2828
ensure!(self.region_length % PageSize::Size4KiB.in_bytes() == 0, Error::AddressNotAligned())?;
2929
Ok(confidential_vm.remove_mmio_region(&ConfidentialVmMmioRegion::new(self.region_start_address, self.region_length)))

security-monitor/src/confidential_flow/handlers/sbi/response.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@ pub struct SbiResponse {
1414
}
1515

1616
impl SbiResponse {
17-
pub fn from_hypervisor_hart(hypervisor_hart: &HypervisorHart) -> Self {
18-
Self { a0: hypervisor_hart.gprs().read(GeneralPurposeRegister::a0), a1: hypervisor_hart.gprs().read(GeneralPurposeRegister::a1) }
19-
}
20-
2117
pub fn handle(self, confidential_flow: ConfidentialFlow) -> ! {
2218
confidential_flow.declassify_and_exit_to_confidential_hart(DeclassifyToConfidentialVm::SbiResponse(self))
2319
}

0 commit comments

Comments
 (0)