Skip to content

Commit a448d45

Browse files
committed
schedule: switch to special stack during context switches
We need to guard against IRQs coming in after switching to the new page tables and before switching to the new stack. Signed-off-by: Tom Dohrmann <[email protected]>
1 parent 6029dce commit a448d45

File tree

3 files changed

+30
-4
lines changed

3 files changed

+30
-4
lines changed

Diff for: kernel/src/cpu/percpu.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ use crate::mm::vm::{
2525
VMReserved, VMR,
2626
};
2727
use crate::mm::{
28-
virt_to_phys, PageBox, SVSM_PERCPU_BASE, SVSM_PERCPU_CAA_BASE, SVSM_PERCPU_END,
29-
SVSM_PERCPU_TEMP_BASE_2M, SVSM_PERCPU_TEMP_BASE_4K, SVSM_PERCPU_TEMP_END_2M,
28+
virt_to_phys, PageBox, SVSM_CONTEXT_SWITCH_STACK, SVSM_PERCPU_BASE, SVSM_PERCPU_CAA_BASE,
29+
SVSM_PERCPU_END, SVSM_PERCPU_TEMP_BASE_2M, SVSM_PERCPU_TEMP_BASE_4K, SVSM_PERCPU_TEMP_END_2M,
3030
SVSM_PERCPU_TEMP_END_4K, SVSM_PERCPU_VMSA_BASE, SVSM_SHADOW_STACKS_INIT_TASK,
3131
SVSM_SHADOW_STACK_ISST_DF_BASE, SVSM_STACKS_INIT_TASK, SVSM_STACK_IST_DF_BASE,
3232
};
@@ -499,6 +499,11 @@ impl PerCpu {
499499
Ok(())
500500
}
501501

502+
fn allocate_context_switch_stack(&self) -> Result<(), SvsmError> {
503+
self.allocate_stack(SVSM_CONTEXT_SWITCH_STACK)?;
504+
Ok(())
505+
}
506+
502507
fn allocate_ist_stacks(&self) -> Result<(), SvsmError> {
503508
let double_fault_stack = self.allocate_stack(SVSM_STACK_IST_DF_BASE)?;
504509
self.ist.double_fault_stack.set(Some(double_fault_stack));
@@ -629,6 +634,9 @@ impl PerCpu {
629634
self.allocate_init_shadow_stack()?;
630635
}
631636

637+
// Allocate per-cpu context switch stack
638+
self.allocate_context_switch_stack()?;
639+
632640
// Allocate IST stacks
633641
self.allocate_ist_stacks()?;
634642

Diff for: kernel/src/mm/address_space.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,11 @@ pub const SVSM_STACKS_INIT_TASK: VirtAddr = SVSM_PERCPU_STACKS_BASE;
172172
pub const SVSM_SHADOW_STACKS_INIT_TASK: VirtAddr =
173173
SVSM_STACKS_INIT_TASK.const_add(STACK_TOTAL_SIZE);
174174

175+
/// Stack address to use during context switches
176+
pub const SVSM_CONTEXT_SWITCH_STACK: VirtAddr = SVSM_SHADOW_STACKS_INIT_TASK.const_add(PAGE_SIZE);
177+
175178
/// IST Stacks base address
176-
pub const SVSM_STACKS_IST_BASE: VirtAddr = SVSM_SHADOW_STACKS_INIT_TASK.const_add(PAGE_SIZE);
179+
pub const SVSM_STACKS_IST_BASE: VirtAddr = SVSM_CONTEXT_SWITCH_STACK.const_add(STACK_TOTAL_SIZE);
177180

178181
/// DoubleFault IST stack base address
179182
pub const SVSM_STACK_IST_DF_BASE: VirtAddr = SVSM_STACKS_IST_BASE;

Diff for: kernel/src/task/schedule.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ extern crate alloc;
3232

3333
use super::INITIAL_TASK_ID;
3434
use super::{Task, TaskListAdapter, TaskPointer, TaskRunListAdapter};
35-
use crate::address::Address;
35+
use crate::address::{Address, VirtAddr};
3636
use crate::cpu::msr::write_msr;
3737
use crate::cpu::percpu::{irq_nesting_count, this_cpu};
3838
use crate::cpu::shadow_stack::PL0_SSP;
@@ -41,6 +41,7 @@ use crate::cpu::sse::sse_save_context;
4141
use crate::cpu::IrqGuard;
4242
use crate::error::SvsmError;
4343
use crate::locking::SpinLock;
44+
use crate::mm::{STACK_TOTAL_SIZE, SVSM_CONTEXT_SWITCH_STACK};
4445
use alloc::sync::Arc;
4546
use core::arch::{asm, global_asm};
4647
use core::cell::OnceCell;
@@ -414,6 +415,9 @@ global_asm!(
414415
jz 1f
415416
movq %rsp, {TASK_RSP_OFFSET}(%rsi)
416417
418+
// Switch to a stack pointer that's valid in both the old and new page tables.
419+
mov ${CONTEXT_SWITCH_STACK}, %rsp
420+
417421
1:
418422
// Switch to the new task state
419423
mov %rdx, %cr3
@@ -445,5 +449,16 @@ global_asm!(
445449
ret
446450
"#,
447451
TASK_RSP_OFFSET = const offset_of!(Task, rsp),
452+
CONTEXT_SWITCH_STACK = const CONTEXT_SWITCH_STACK.as_usize(),
448453
options(att_syntax)
449454
);
455+
456+
/// The location of a cpu-local stack that's mapped into every set of page
457+
/// tables for use during context switches.
458+
///
459+
/// If an IRQ is raised after switching the page tables but before switching
460+
/// to the new stack, the CPU will try to access the old stack in the new page
461+
/// tables. To protect against this, we switch to another stack that's mapped
462+
/// into both the old and the new set of page tables. That way we always have a
463+
/// valid stack to handle exceptions on.
464+
const CONTEXT_SWITCH_STACK: VirtAddr = SVSM_CONTEXT_SWITCH_STACK.const_add(STACK_TOTAL_SIZE);

0 commit comments

Comments
 (0)