Skip to content

Commit dd0907a

Browse files
authored
Merge pull request coconut-svsm#890 from 00xc/cpu/percpu/sync
cpu/percpu: (partially) harden against reentrancy issues
2 parents 261419a + 7de1ff8 commit dd0907a

File tree

4 files changed

+144
-74
lines changed

4 files changed

+144
-74
lines changed

kernel/src/cpu/percpu.rs

Lines changed: 60 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,19 @@ use crate::task::{schedule, schedule_task, KernelThreadStartInfo, RunQueue, Task
4545
use crate::types::{
4646
PAGE_SHIFT, PAGE_SHIFT_2M, PAGE_SIZE, PAGE_SIZE_2M, SVSM_TR_ATTRIBUTES, SVSM_TSS,
4747
};
48+
use crate::utils::immut_after_init::ImmutAfterInitCell;
4849
use crate::utils::MemoryRegion;
4950
use alloc::boxed::Box;
5051
use alloc::string::String;
5152
use alloc::sync::Arc;
5253
use alloc::vec::Vec;
5354
use core::arch::asm;
54-
use core::cell::{Cell, OnceCell, Ref, RefCell, RefMut, UnsafeCell};
55+
use core::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell};
5556
use core::mem::size_of;
5657
use core::ops::Deref;
5758
use core::ptr;
5859
use core::slice::Iter;
59-
use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
60+
use core::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering};
6061
use cpuarch::vmsa::VMSA;
6162

6263
// PERCPU areas virtual addresses into shared memory
@@ -163,15 +164,15 @@ impl PerCpuAreas {
163164

164165
#[derive(Debug)]
165166
struct IstStacks {
166-
double_fault_stack: Cell<Option<VirtAddr>>,
167-
double_fault_shadow_stack: Cell<Option<VirtAddr>>,
167+
double_fault_stack: ImmutAfterInitCell<VirtAddr>,
168+
double_fault_shadow_stack: ImmutAfterInitCell<VirtAddr>,
168169
}
169170

170171
impl IstStacks {
171172
const fn new() -> Self {
172173
IstStacks {
173-
double_fault_stack: Cell::new(None),
174-
double_fault_shadow_stack: Cell::new(None),
174+
double_fault_stack: ImmutAfterInitCell::uninit(),
175+
double_fault_shadow_stack: ImmutAfterInitCell::uninit(),
175176
}
176177
}
177178
}
@@ -368,8 +369,8 @@ pub struct PerCpu {
368369
pgtbl: RefCell<Option<&'static mut PageTable>>,
369370
tss: X86Tss,
370371
isst: Cell<Isst>,
371-
svsm_vmsa: OnceCell<VmsaPage>,
372-
reset_ip: Cell<u64>,
372+
svsm_vmsa: ImmutAfterInitCell<VmsaPage>,
373+
reset_ip: AtomicU64,
373374
/// PerCpu Virtual Memory Range
374375
vm_range: VMR,
375376
/// Address allocator for per-cpu 4k temporary mappings
@@ -382,16 +383,16 @@ pub struct PerCpu {
382383
guest_apic: RefCell<Option<LocalApic>>,
383384

384385
/// GHCB page for this CPU.
385-
ghcb: OnceCell<GhcbPage>,
386+
ghcb: ImmutAfterInitCell<GhcbPage>,
386387

387388
/// Hypercall input/output pages for this CPU if running under Hyper-V.
388389
hypercall_pages: RefCell<Option<(HypercallPage, HypercallPage)>>,
389390

390391
/// `#HV` doorbell page for this CPU.
391-
hv_doorbell: OnceCell<SharedBox<HVDoorbell>>,
392+
hv_doorbell: ImmutAfterInitCell<SharedBox<HVDoorbell>>,
392393

393-
init_shadow_stack: Cell<Option<VirtAddr>>,
394-
context_switch_stack: Cell<Option<VirtAddr>>,
394+
init_shadow_stack: ImmutAfterInitCell<VirtAddr>,
395+
context_switch_stack: ImmutAfterInitCell<VirtAddr>,
395396
ist: IstStacks,
396397

397398
/// Stack boundaries of the currently running task.
@@ -407,8 +408,8 @@ impl PerCpu {
407408
irq_state: IrqState::new(),
408409
tss: X86Tss::new(),
409410
isst: Cell::new(Isst::default()),
410-
svsm_vmsa: OnceCell::new(),
411-
reset_ip: Cell::new(0xffff_fff0),
411+
svsm_vmsa: ImmutAfterInitCell::uninit(),
412+
reset_ip: AtomicU64::new(0xffff_fff0),
412413
vm_range: {
413414
let mut vmr = VMR::new(SVSM_PERCPU_BASE, SVSM_PERCPU_END, PTEntryFlags::GLOBAL);
414415
vmr.set_per_cpu(true);
@@ -421,11 +422,11 @@ impl PerCpu {
421422
guest_apic: RefCell::new(None),
422423

423424
shared,
424-
ghcb: OnceCell::new(),
425+
ghcb: ImmutAfterInitCell::uninit(),
425426
hypercall_pages: RefCell::new(None),
426-
hv_doorbell: OnceCell::new(),
427-
init_shadow_stack: Cell::new(None),
428-
context_switch_stack: Cell::new(None),
427+
hv_doorbell: ImmutAfterInitCell::uninit(),
428+
init_shadow_stack: ImmutAfterInitCell::uninit(),
429+
context_switch_stack: ImmutAfterInitCell::uninit(),
429430
ist: IstStacks::new(),
430431
current_stack: Cell::new(MemoryRegion::new(VirtAddr::null(), 0)),
431432
}
@@ -532,15 +533,12 @@ impl PerCpu {
532533

533534
/// Sets up the CPU-local GHCB page.
534535
pub fn setup_ghcb(&self) -> Result<(), SvsmError> {
535-
let page = GhcbPage::new()?;
536-
self.ghcb
537-
.set(page)
538-
.expect("Attempted to reinitialize the GHCB");
536+
self.ghcb.try_init_from_fn(GhcbPage::new)?;
539537
Ok(())
540538
}
541539

542540
fn ghcb(&self) -> Option<&GhcbPage> {
543-
self.ghcb.get()
541+
self.ghcb.try_get_inner().ok()
544542
}
545543

546544
/// Allocates hypercall input/output pages for this CPU.
@@ -560,11 +558,11 @@ impl PerCpu {
560558
}
561559

562560
pub fn hv_doorbell(&self) -> Option<&HVDoorbell> {
563-
self.hv_doorbell.get().map(Deref::deref)
561+
self.hv_doorbell.try_get_inner().ok().map(Deref::deref)
564562
}
565563

566564
pub fn process_hv_events_if_required(&self) {
567-
if let Some(doorbell) = self.hv_doorbell.get() {
565+
if let Ok(doorbell) = self.hv_doorbell.try_get_inner() {
568566
doorbell.process_if_required(&self.irq_state);
569567
}
570568
}
@@ -573,25 +571,30 @@ impl PerCpu {
573571
/// PerCpu structure.
574572
pub fn hv_doorbell_addr(&self) -> *const *const HVDoorbell {
575573
self.hv_doorbell
576-
.get()
574+
.try_get_inner()
575+
.ok()
577576
.map(SharedBox::ptr_ref)
578577
.unwrap_or(ptr::null())
579578
}
580579

581580
pub fn get_top_of_shadow_stack(&self) -> Option<VirtAddr> {
582-
self.init_shadow_stack.get()
581+
self.init_shadow_stack.try_get_inner().ok().copied()
583582
}
584583

585584
pub fn get_top_of_context_switch_stack(&self) -> Option<VirtAddr> {
586-
self.context_switch_stack.get()
585+
self.context_switch_stack.try_get_inner().ok().copied()
587586
}
588587

589588
pub fn get_top_of_df_stack(&self) -> Option<VirtAddr> {
590-
self.ist.double_fault_stack.get()
589+
self.ist.double_fault_stack.try_get_inner().ok().copied()
591590
}
592591

593592
pub fn get_top_of_df_shadow_stack(&self) -> Option<VirtAddr> {
594-
self.ist.double_fault_shadow_stack.get()
593+
self.ist
594+
.double_fault_shadow_stack
595+
.try_get_inner()
596+
.ok()
597+
.copied()
595598
}
596599

597600
pub fn get_current_stack(&self) -> MemoryRegion<VirtAddr> {
@@ -651,15 +654,15 @@ impl PerCpu {
651654
}
652655

653656
fn allocate_init_shadow_stack(&self) -> Result<(), SvsmError> {
654-
let init_stack =
655-
Some(self.allocate_shadow_stack(SVSM_SHADOW_STACKS_INIT_TASK, ShadowStackInit::Init)?);
656-
self.init_shadow_stack.set(init_stack);
657+
self.init_shadow_stack.try_init_from_fn(|| {
658+
self.allocate_shadow_stack(SVSM_SHADOW_STACKS_INIT_TASK, ShadowStackInit::Init)
659+
})?;
657660
Ok(())
658661
}
659662

660663
fn allocate_context_switch_stack(&self) -> Result<(), SvsmError> {
661-
let cs_stack = Some(self.allocate_stack(SVSM_CONTEXT_SWITCH_STACK)?);
662-
self.context_switch_stack.set(cs_stack);
664+
self.context_switch_stack
665+
.try_init_from_fn(|| self.allocate_stack(SVSM_CONTEXT_SWITCH_STACK))?;
663666
Ok(())
664667
}
665668

@@ -672,18 +675,16 @@ impl PerCpu {
672675
}
673676

674677
fn allocate_ist_stacks(&self) -> Result<(), SvsmError> {
675-
let double_fault_stack = self.allocate_stack(SVSM_STACK_IST_DF_BASE)?;
676-
self.ist.double_fault_stack.set(Some(double_fault_stack));
677-
678+
self.ist
679+
.double_fault_stack
680+
.try_init_from_fn(|| self.allocate_stack(SVSM_STACK_IST_DF_BASE))?;
678681
Ok(())
679682
}
680683

681684
fn allocate_isst_shadow_stacks(&self) -> Result<(), SvsmError> {
682-
let double_fault_shadow_stack =
683-
self.allocate_shadow_stack(SVSM_SHADOW_STACK_ISST_DF_BASE, ShadowStackInit::Exception)?;
684-
self.ist
685-
.double_fault_shadow_stack
686-
.set(Some(double_fault_shadow_stack));
685+
self.ist.double_fault_shadow_stack.try_init_from_fn(|| {
686+
self.allocate_shadow_stack(SVSM_SHADOW_STACK_ISST_DF_BASE, ShadowStackInit::Exception)
687+
})?;
687688

688689
Ok(())
689690
}
@@ -705,10 +706,8 @@ impl PerCpu {
705706
}
706707

707708
pub fn setup_hv_doorbell(&self) -> Result<(), SvsmError> {
708-
let doorbell = allocate_hv_doorbell_page(current_ghcb())?;
709709
self.hv_doorbell
710-
.set(doorbell)
711-
.expect("Attempted to reinitialize HV doorbell page");
710+
.try_init_from_fn(|| allocate_hv_doorbell_page(current_ghcb()))?;
712711
Ok(())
713712
}
714713

@@ -865,7 +864,7 @@ impl PerCpu {
865864
}
866865

867866
pub fn set_reset_ip(&self, reset_ip: u64) {
868-
self.reset_ip.set(reset_ip);
867+
self.reset_ip.store(reset_ip, Ordering::Relaxed);
869868
}
870869

871870
/// Fill in the initial context structure for the SVSM.
@@ -908,25 +907,17 @@ impl PerCpu {
908907
/// physical address and SEV features. Returns an error if allocation
909908
/// fails of this CPU's VMSA was already initialized.
910909
pub fn alloc_svsm_vmsa(&self, vtom: u64, start_rip: u64) -> Result<(PhysAddr, u64), SvsmError> {
911-
if self.svsm_vmsa.get().is_some() {
912-
// FIXME: add a more explicit error variant for this condition
913-
return Err(SvsmError::Mem);
914-
}
915-
916-
let mut vmsa = VmsaPage::new(RMPFlags::VMPL1)?;
917-
let paddr = vmsa.paddr();
918-
919-
// Initialize VMSA
920-
let context = self.get_initial_context(start_rip);
921-
init_svsm_vmsa(&mut vmsa, vtom, &context);
922-
vmsa.enable();
923-
924-
let sev_features = vmsa.sev_features;
910+
let vmsa = self.svsm_vmsa.try_init_from_fn(|| {
911+
let mut vmsa = VmsaPage::new(RMPFlags::VMPL1)?;
925912

926-
// We already checked that the VMSA is unset
927-
self.svsm_vmsa.set(vmsa).unwrap();
913+
// Initialize VMSA
914+
let context = self.get_initial_context(start_rip);
915+
init_svsm_vmsa(&mut vmsa, vtom, &context);
916+
vmsa.enable();
917+
Ok::<_, SvsmError>(vmsa)
918+
})?;
928919

929-
Ok((paddr, sev_features))
920+
Ok((vmsa.paddr(), vmsa.sev_features))
930921
}
931922

932923
fn unmap_guest_vmsa(&self) {
@@ -962,7 +953,11 @@ impl PerCpu {
962953
let mut vmsa = VmsaPage::new(RMPFlags::GUEST_VMPL)?;
963954
let paddr = vmsa.paddr();
964955

965-
init_guest_vmsa(&mut vmsa, self.reset_ip.get(), use_alternate_injection);
956+
init_guest_vmsa(
957+
&mut vmsa,
958+
self.reset_ip.load(Ordering::Relaxed),
959+
use_alternate_injection,
960+
);
966961

967962
self.shared().update_guest_vmsa(paddr);
968963
let _ = VmsaPage::leak(vmsa);

kernel/src/cpu/x86/apic.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use crate::cpu::percpu::this_cpu;
88
use crate::error::SvsmError;
9-
use core::cell::OnceCell;
9+
use crate::utils::immut_after_init::ImmutAfterInitCell;
1010

1111
pub trait ApicAccess: core::fmt::Debug {
1212
/// Updates the APIC_BASE MSR by reading the current value, applying the
@@ -92,7 +92,7 @@ fn apic_register_bit(vector: usize) -> (usize, u32) {
9292

9393
#[derive(Debug, Default)]
9494
pub struct X86Apic {
95-
access: OnceCell<&'static dyn ApicAccess>,
95+
access: ImmutAfterInitCell<&'static dyn ApicAccess>,
9696
}
9797

9898
// APIC enable masks
@@ -102,7 +102,7 @@ const APIC_X2_ENABLE_MASK: u64 = 0x400;
102102
impl X86Apic {
103103
/// Returns the ApicAccess object.
104104
fn regs(&self) -> &'static dyn ApicAccess {
105-
*self.access.get().expect("ApicAccessor not set!")
105+
*self.access
106106
}
107107

108108
/// Initialize the ApicAccessor - Must be called before X86APIC can be used.
@@ -116,14 +116,14 @@ impl X86Apic {
116116
/// This function panics when the `ApicAccessor` has already been set.
117117
pub fn set_accessor(&self, accessor: &'static dyn ApicAccess) {
118118
self.access
119-
.set(accessor)
119+
.init(accessor)
120120
.expect("ApicAccessor already set!");
121121
}
122122

123123
/// Creates a new instance of [`X86Apic`]
124-
pub fn new() -> Self {
124+
pub const fn new() -> Self {
125125
Self {
126-
access: OnceCell::new(),
126+
access: ImmutAfterInitCell::uninit(),
127127
}
128128
}
129129

kernel/src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use crate::sev::SevSnpError;
3131
use crate::syscall::ObjError;
3232
use crate::task::TaskError;
3333
use crate::tdx::TdxError;
34+
use crate::utils::immut_after_init::ImmutAfterInitError;
3435
#[cfg(feature = "virtio-drivers")]
3536
use crate::virtio::VirtioError;
3637
use elf::ElfError;
@@ -129,6 +130,8 @@ pub enum SvsmError {
129130
/// Errors related to attesting SVSM's launch evidence.
130131
#[cfg(feature = "attest")]
131132
TeeAttestation(AttestationError),
133+
/// Errors related to ImmutAfterInitCell
134+
ImmutAfterInit(ImmutAfterInitError),
132135
}
133136

134137
impl From<ElfError> for SvsmError {

0 commit comments

Comments
 (0)