From 20c9a1be3d95714ed224d266c88e9382d38e6d7f Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Fri, 4 Aug 2023 13:40:53 +0100 Subject: [PATCH] task: Initialise syscall/sysret and add basic handlers 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 --- src/svsm.rs | 4 +- src/task/mod.rs | 2 + src/task/syscall.rs | 127 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 src/task/syscall.rs diff --git a/src/svsm.rs b/src/svsm.rs index 413ff5f76..e7e7c177a 100755 --- a/src/svsm.rs +++ b/src/svsm.rs @@ -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}; @@ -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"); diff --git a/src/task/mod.rs b/src/task/mod.rs index 221bc92bd..f7cad1a9a 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -5,10 +5,12 @@ // Author: Roy Hopkins 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}; diff --git a/src/task/syscall.rs b/src/task/syscall.rs new file mode 100644 index 000000000..69230b546 --- /dev/null +++ b/src/task/syscall.rs @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2022-2023 SUSE LLC +// +// Author: Roy Hopkins + +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) +);