Skip to content

Commit

Permalink
task: Initialise syscall/sysret and add basic handlers
Browse files Browse the repository at this point in the history
For running tasks at CPL-3 we need some method to transition from
CPL-0 to CPL-3. The best way to achieve this transition is using
syscall/sysret.

This commit introduces a function which is used to setup the correct
configuration in syscall MSRs to allow the use of syscall/sysret for
switching between the different privilege levels.

In addition, a syscall handler has been implemented that adds support
for terminating a user-mode task, for allowing a context switch via the
kernel schedule() function and for a simple log function. These handlers
are only an initial implementation and likely to be reworked or replaced
in a subsequent commit.

Signed-off-by: Roy Hopkins <rhopkins@suse.de>
  • Loading branch information
roy-hopkins committed Nov 22, 2023
1 parent 9e207e6 commit 20c9a1b
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 1 deletion.
4 changes: 3 additions & 1 deletion src/svsm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ use svsm::sev::sev_status_init;
use svsm::sev::utils::{rmp_adjust, RMPFlags};
use svsm::svsm_console::SVSMIOPort;
use svsm::svsm_paging::{init_page_table, invalidate_stage2};
use svsm::task::{create_task, TASK_FLAG_SHARE_PT};
use svsm::task::{create_task, init_syscall, TASK_FLAG_SHARE_PT};
use svsm::types::{PageSize, GUEST_VMPL, PAGE_SIZE};
use svsm::utils::{halt, immut_after_init::ImmutAfterInitCell, zero_mem_region, MemoryRegion};

Expand Down Expand Up @@ -436,6 +436,8 @@ pub extern "C" fn svsm_main(_param: u64) {
populate_ram_fs(LAUNCH_INFO.kernel_fs_start, LAUNCH_INFO.kernel_fs_end)
.expect("Failed to unpack FS archive");

init_syscall();

ModuleLoader::enumerate().expect("Failed to initialise loadable modules");

let cpus = load_acpi_cpu_info(&fw_cfg).expect("Failed to load ACPI tables");
Expand Down
2 changes: 2 additions & 0 deletions src/task/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
// Author: Roy Hopkins <rhopkins@suse.de>

mod schedule;
mod syscall;
mod tasks;

pub use schedule::{
create_task, create_task_for_module, is_current_task, schedule, RunQueue, TaskNode,
TaskPointer, TASKLIST,
};
pub use syscall::init_syscall;
pub use tasks::{Task, TaskContext, TaskError, TaskState, INITIAL_TASK_ID, TASK_FLAG_SHARE_PT};
127 changes: 127 additions & 0 deletions src/task/syscall.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2022-2023 SUSE LLC
//
// Author: Roy Hopkins <rhopkins@suse.de>

use core::{
arch::global_asm,
ffi::{c_char, CStr},
};

use crate::cpu::{
efer::{read_efer, write_efer, EFERFlags},
msr::write_msr,
percpu::this_cpu,
};
use crate::types::{SVSM_CS, SVSM_USER_CS32};

use super::schedule;

pub enum SyscallRet {
Ok,
Unknown,
Terminate,
}

extern "C" {
static syscall_entry: u64;
}

const MSR_STAR: u32 = 0xc000_0081;
const MSR_LSTAR: u32 = 0xc000_0082;
const MSR_SFMASK: u32 = 0xc000_0084;

#[no_mangle]
extern "C" fn syscall_handler(index: u32, param1: u64) -> u64 {
let ret = match index {
// exit
0 => SyscallRet::Terminate,
// log
1 => {
unsafe {
let str = CStr::from_ptr(param1 as *const c_char);
log::info!("{}", str.to_str().unwrap());
}
SyscallRet::Ok
}
// sleep
2 => SyscallRet::Ok,
_ => {
log::info!("Invalid syscall received: {}", index);
SyscallRet::Unknown
}
};
schedule();
ret as u64
}

pub fn init_syscall() {
let mut efer = read_efer();
efer.insert(EFERFlags::SCE);
write_efer(efer);

let sysret_cs = SVSM_USER_CS32 as u64;
let syscall_cs = SVSM_CS as u64;
write_msr(MSR_STAR, sysret_cs << 48 | syscall_cs << 32);
unsafe {
write_msr(MSR_LSTAR, (&syscall_entry as *const u64) as u64);
}
// FIXME: Find correct mask for flags
write_msr(MSR_SFMASK, 0);
}

#[no_mangle]
extern "C" fn get_kernel_rsp() -> u64 {
let task_node = this_cpu()
.runqueue()
.lock_read()
.current_task()
.expect("Invalid current task");
let rsp = task_node
.task
.lock_read()
.user
.as_ref()
.expect("Syscall from kernel task")
.kernel_rsp;
rsp
}

global_asm!(
r#"
.text
syscall_entry:
// Switch to the task kernel stack
push %rcx // User-mode return address
// Syscall arguments
push %rsi
push %rdi
call get_kernel_rsp
pop %rdi
pop %rsi
subq $8, %rax
movq %rsp, (%rax)
mov %rax, %rsp
call syscall_handler
// Check to see if the task requested termination
cmp $2, %rax // SyscallRet::Terminate
jne ret_user
addq $8, %rsp // Skip user mode return address
// Kernel stack frame should now be within launch_user_entry()
ret
ret_user:
pop %rsp
pop %rcx
movq $0x202, %r11
sysretq
"#,
options(att_syntax)
);

0 comments on commit 20c9a1b

Please sign in to comment.