Skip to content

Commit 06b2f70

Browse files
committed
kernel/task: Move ELF parsing into context of the new task
When loading a user ELF binary for execution the COCONUT kernel created a new Task object and mapped the ELF sections into the new tasks user-VMR. This all happened from the context of the old task. This behavior is problematic as it creates unnecessary complexity for cases when the kernel needs to write values into the user address space. Fix this behavior by moving the actual ELF loading into the context of the new task. Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
1 parent 535d7af commit 06b2f70

5 files changed

Lines changed: 105 additions & 49 deletions

File tree

kernel/src/cpu/idt/svsm_entry.S

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,11 @@ thread_entry_asm:
378378
movq %r15, %rsi
379379
movq %r14, %rdi
380380

381+
// Move pointer to X86ExceptionContext to %rdx - Only present and used
382+
// for user-tasks
383+
movq %rsp, %rdx
384+
addq $16, %rdx
385+
381386
// Fetch thread entry function pointer - do not touch stack pointer to
382387
// keep stack alignment
383388
movq (%rsp), %rax

kernel/src/task/exec.rs

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,30 @@ use super::TaskPointer;
1010
use crate::address::{Address, VirtAddr};
1111
use crate::error::SvsmError;
1212
use crate::fs::{Directory, open_read};
13-
use crate::mm::USER_MEM_END;
1413
use crate::mm::vm::VMFileMappingFlags;
14+
use crate::mm::{USER_MEM_END, mmap_user};
1515
use crate::task::{create_user_task, current_task, finish_user_task, schedule};
1616
use crate::types::PAGE_SIZE;
1717
use crate::utils::align_up;
18+
use alloc::boxed::Box;
1819
use alloc::sync::Arc;
1920
use elf::{Elf64File, Elf64PhdrFlags};
2021

2122
use alloc::string::String;
2223

24+
#[derive(Debug)]
25+
pub struct UserExecInfo {
26+
binary: String,
27+
}
28+
29+
impl UserExecInfo {
30+
pub fn new(b: &str) -> Self {
31+
Self {
32+
binary: String::from(b),
33+
}
34+
}
35+
}
36+
2337
fn convert_elf_phdr_flags(flags: Elf64PhdrFlags) -> VMFileMappingFlags {
2438
let mut vm_flags = VMFileMappingFlags::Fixed;
2539

@@ -44,27 +58,20 @@ fn task_name(binary: &str) -> String {
4458
}
4559
}
4660

47-
/// Loads and executes an ELF binary in user-mode.
48-
///
49-
/// # Arguments
50-
///
51-
/// * binary: Path to file in the file-system
52-
///
53-
/// # Returns
54-
///
55-
/// [`Ok(TaskPointer)`] on success, [`Err(SvsmError)`] on failure.
56-
pub fn exec_user(binary: &str, root: Arc<dyn Directory>) -> Result<TaskPointer, SvsmError> {
57-
let fh = open_read(binary)?;
61+
pub fn exec(info: UserExecInfo) -> Result<u64, SvsmError> {
62+
let fh = open_read(&info.binary)?;
5863
let file_size = fh.size();
5964

6065
let current_task = current_task();
66+
6167
let vstart = current_task.mmap_kernel_guard(
6268
VirtAddr::new(0),
6369
Some(&fh),
6470
0,
6571
file_size,
6672
VMFileMappingFlags::Read,
6773
)?;
74+
6875
// SAFETY: `vstart` has just been mapped using `file_size` as the size,
6976
// so it is safe to create a slice of the same size.
7077
let buf = unsafe { vstart.to_slice::<u8>(file_size) };
@@ -74,8 +81,6 @@ pub fn exec_user(binary: &str, root: Arc<dyn Directory>) -> Result<TaskPointer,
7481
let virt_base = alloc_info.range.vaddr_begin;
7582
let entry = elf_bin.get_entry(virt_base);
7683

77-
let new_task = create_user_task(entry.try_into().unwrap(), root, task_name(binary))?;
78-
7984
for seg in elf_bin.image_load_segment_iter(virt_base) {
8085
let virt_start = VirtAddr::from(seg.vaddr_range.vaddr_begin);
8186
let virt_end = VirtAddr::from(seg.vaddr_range.vaddr_end).align_up(PAGE_SIZE);
@@ -93,16 +98,16 @@ pub fn exec_user(binary: &str, root: Arc<dyn Directory>) -> Result<TaskPointer,
9398
let start_aligned = virt_start.page_align();
9499
let offset = file_offset - virt_start.page_offset();
95100
let size = file_size + virt_start.page_offset();
96-
new_task.mmap_user(start_aligned, Some(&fh), offset, size, flags)?;
101+
mmap_user(start_aligned, Some(&fh), offset, size, flags)?;
97102

98103
let size_aligned = align_up(size, PAGE_SIZE);
99104
if size_aligned < len {
100105
let start_anon = start_aligned.const_add(size_aligned);
101106
let remaining_len = len - size_aligned;
102-
new_task.mmap_user(start_anon, None, 0, remaining_len, flags)?;
107+
mmap_user(start_anon, None, 0, remaining_len, flags)?;
103108
}
104109
} else {
105-
new_task.mmap_user(virt_start, None, 0, len, flags)?;
110+
mmap_user(virt_start, None, 0, len, flags)?;
106111
}
107112
}
108113

@@ -113,7 +118,23 @@ pub fn exec_user(binary: &str, root: Arc<dyn Directory>) -> Result<TaskPointer,
113118
let user_stack_size: usize = 64 * 1024;
114119
let stack_flags: VMFileMappingFlags = VMFileMappingFlags::Fixed | VMFileMappingFlags::Write;
115120
let stack_addr = USER_MEM_END - user_stack_size;
116-
new_task.mmap_user(stack_addr, None, 0, user_stack_size, stack_flags)?;
121+
mmap_user(stack_addr, None, 0, user_stack_size, stack_flags)?;
122+
123+
Ok(entry)
124+
}
125+
126+
/// Loads and executes an ELF binary in user-mode.
127+
///
128+
/// # Arguments
129+
///
130+
/// * binary: Path to file in the file-system
131+
///
132+
/// # Returns
133+
///
134+
/// [`Ok(TaskPointer)`] on success, [`Err(SvsmError)`] on failure.
135+
pub fn exec_user(binary: &str, root: Arc<dyn Directory>) -> Result<TaskPointer, SvsmError> {
136+
let info = Box::new(UserExecInfo::new(binary));
137+
let new_task = create_user_task(info, root, task_name(binary))?;
117138

118139
finish_user_task(new_task.clone());
119140
schedule();

kernel/src/task/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@ pub use tasks::{
2121
TaskListAdapter, TaskPointer, TaskRunListAdapter, TaskState, is_task_fault,
2222
};
2323

24-
pub use exec::exec_user;
24+
pub use exec::{UserExecInfo, exec_user};
2525
pub use waiting::WaitQueue;

kernel/src/task/schedule.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ use super::tasks::TASK_ACTIVE_OFFSET;
3838
use super::tasks::TASK_CUR_CPU_OFFSET;
3939
use super::{
4040
INITIAL_TASK_ID, KernelThreadStartInfo, Task, TaskListAdapter, TaskPointer, TaskRunListAdapter,
41+
UserExecInfo,
4142
};
4243
use crate::address::{Address, VirtAddr};
4344
use crate::cpu::IrqGuard;
@@ -59,6 +60,7 @@ use crate::fs::Directory;
5960
use crate::locking::SpinLock;
6061
use crate::mm::SVSM_CONTEXT_SWITCH_SHADOW_STACK;
6162
use crate::platform::SVSM_PLATFORM;
63+
use alloc::boxed::Box;
6264
use alloc::string::String;
6365
use alloc::sync::Arc;
6466
use core::arch::global_asm;
@@ -365,12 +367,12 @@ pub fn start_kernel_thread(start_info: KernelThreadStartInfo) -> Result<TaskPoin
365367
///
366368
/// A new instance of [`TaskPointer`] on success, [`SvsmError`] on failure.
367369
pub fn create_user_task(
368-
user_entry: usize,
370+
info: Box<UserExecInfo>,
369371
root: Arc<dyn Directory>,
370372
name: String,
371373
) -> Result<TaskPointer, SvsmError> {
372374
let cpu = this_cpu();
373-
Task::create_user(cpu, user_entry, root, name)
375+
Task::create_user(cpu, info, root, name)
374376
}
375377

376378
/// Finished user-space task creation by putting the task on the global

kernel/src/task/tasks.rs

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

77
extern crate alloc;
88

9+
use alloc::boxed::Box;
910
use alloc::collections::btree_map::BTreeMap;
1011
use alloc::string::String;
1112
use alloc::sync::Arc;
@@ -50,10 +51,11 @@ use crate::types::{SVSM_USER_CS, SVSM_USER_DS};
5051
use crate::utils::{MemoryRegion, is_aligned};
5152
use intrusive_collections::{LinkedListAtomicLink, intrusive_adapter};
5253

53-
use super::WaitQueue;
54+
use super::exec::exec;
5455
use super::schedule::complete_task_switch;
5556
use super::schedule::terminate;
5657
use super::task_mm::{TaskKernelMapping, TaskMM};
58+
use super::{UserExecInfo, WaitQueue};
5759

5860
pub const INITIAL_TASK_ID: u32 = 1;
5961

@@ -129,7 +131,7 @@ impl KernelThreadStartInfo {
129131
#[derive(Debug)]
130132
enum ThreadStartInfo {
131133
Kernel(KernelThreadStartInfo),
132-
User(usize),
134+
User(*const UserExecInfo),
133135
}
134136

135137
#[derive(PartialEq, Debug, Copy, Clone, Default)]
@@ -491,7 +493,7 @@ impl Task {
491493

492494
// Call the correct stack creation routine for this task.
493495
let (stack, raw_bounds, rsp_offset) = match args.start_info {
494-
ThreadStartInfo::User(entry) => Self::allocate_utask_stack(cpu, entry, xsa_addr)?,
496+
ThreadStartInfo::User(info_ptr) => Self::allocate_utask_stack(cpu, info_ptr, xsa_addr)?,
495497
ThreadStartInfo::Kernel(ref info) => Self::allocate_ktask_stack(
496498
cpu,
497499
info.entry,
@@ -552,7 +554,7 @@ impl Task {
552554

553555
pub fn create_user(
554556
cpu: &PerCpu,
555-
user_entry: usize,
557+
info: Box<UserExecInfo>,
556558
root: Arc<dyn Directory>,
557559
name: String,
558560
) -> Result<TaskPointer, SvsmError> {
@@ -562,14 +564,23 @@ impl Task {
562564
unsafe {
563565
vm_user_range.initialize_lazy()?;
564566
}
567+
568+
// Destroy the Box and get the pointer to the raw data
569+
let info_ptr = Box::into_raw(info);
570+
565571
let create_args = CreateTaskArguments {
566-
start_info: ThreadStartInfo::User(user_entry),
572+
start_info: ThreadStartInfo::User(info_ptr),
567573
name,
568574
vm_user_range: Some(vm_user_range),
569575
rootdir: root,
570576
thread_of: None,
571577
};
572-
Self::create_common(cpu, create_args)
578+
Self::create_common(cpu, create_args).inspect_err(|_| {
579+
// In error case, make sure info gets freed
580+
// SAFETY: info_ptr was created above from a Box and not
581+
// dropped in the create_common call-path.
582+
drop(unsafe { Box::from_raw(info_ptr) });
583+
})
573584
}
574585

575586
/// Create a new thread for an existing task.
@@ -737,20 +748,14 @@ impl Task {
737748

738749
fn allocate_utask_stack(
739750
cpu: &PerCpu,
740-
user_entry: usize,
751+
info_ptr: *const UserExecInfo,
741752
xsa_addr: usize,
742753
) -> Result<(Mapping, MemoryRegion<VirtAddr>, usize), SvsmError> {
743-
// Do not run user-mode with IRQs enabled on platforms which are not
744-
// ready for it.
745-
let iret_rflags: usize = if SVSM_PLATFORM.use_interrupts() {
746-
2 | EFLAGS_IF
747-
} else {
748-
2
749-
};
750754
let task_context = UserTaskContext {
751755
task_context: TaskContext {
752756
regs: X86TaskSwitchRegs {
753-
rsi: xsa_addr, // XSAVE area addr
757+
rsi: xsa_addr, // XSAVE area addr
758+
r14: info_ptr as usize, // New task launch info
754759
..Default::default()
755760
},
756761
ret_addr: thread_entry_asm as *const () as u64,
@@ -762,20 +767,9 @@ impl Task {
762767
.unwrap(),
763768
// Setup IRQ return frame. User-mode tasks always run with
764769
// interrupts enabled.
765-
excp_context: X86ExceptionContext {
766-
frame: X86InterruptFrame {
767-
rip: user_entry,
768-
cs: (SVSM_USER_CS | 3).into(),
769-
flags: iret_rflags,
770-
rsp: (USER_MEM_END - 8).into(),
771-
ss: (SVSM_USER_DS | 3).into(),
772-
},
773-
..Default::default()
774-
},
770+
excp_context: X86ExceptionContext::default(),
775771
};
776772

777-
debug_assert!(is_aligned(task_context.excp_context.frame.rsp + 8, 16));
778-
779773
Self::allocate_stack_common(cpu, &task_context)
780774
}
781775

@@ -1038,7 +1032,41 @@ unsafe fn complete_new_thread(prev: usize, xsa_addr: u64) {
10381032
/// This is called from assembly code. The caller needs to ensure the
10391033
/// parameters are passed correctly.
10401034
#[unsafe(no_mangle)]
1041-
unsafe fn run_user_task() {
1035+
unsafe fn run_user_task(
1036+
info_ptr: *mut UserExecInfo,
1037+
_unsused: u64,
1038+
ctxt: &mut X86ExceptionContext,
1039+
) {
1040+
// SAFETY: The raw pointer was extracted from a Box in
1041+
// allocate_utask_stack() to move ownership to a new process.
1042+
let info = unsafe { Box::from_raw(info_ptr) };
1043+
1044+
let entry = match exec(*info) {
1045+
Ok(e) => e,
1046+
Err(e) => {
1047+
log::error!("Failed to load ELF binary: {e:?}");
1048+
terminate();
1049+
}
1050+
};
1051+
1052+
// Do not run user-mode with IRQs enabled on platforms which are not
1053+
// ready for it.
1054+
let iret_rflags: usize = if SVSM_PLATFORM.use_interrupts() {
1055+
2 | EFLAGS_IF
1056+
} else {
1057+
2
1058+
};
1059+
1060+
ctxt.frame = X86InterruptFrame {
1061+
rip: entry as usize,
1062+
cs: (SVSM_USER_CS | 3).into(),
1063+
flags: iret_rflags,
1064+
rsp: (USER_MEM_END - 8).into(),
1065+
ss: (SVSM_USER_DS | 3).into(),
1066+
};
1067+
1068+
debug_assert!(is_aligned(ctxt.frame.rsp + 8, 16));
1069+
10421070
task_attach_console();
10431071
}
10441072

0 commit comments

Comments
 (0)