diff --git a/kernel/src/error.rs b/kernel/src/error.rs index 51f794a1c..1675acd7c 100644 --- a/kernel/src/error.rs +++ b/kernel/src/error.rs @@ -25,6 +25,7 @@ use crate::mm::alloc::AllocError; use crate::sev::ghcb::GhcbError; use crate::sev::msr_protocol::GhcbMsrError; use crate::sev::SevSnpError; +use crate::syscall::ObjError; use crate::task::TaskError; use elf::ElfError; @@ -83,6 +84,8 @@ pub enum SvsmError { Acpi, /// Errors from the filesystem. FileSystem(FsError), + /// Obj related error + Obj(ObjError), /// Task management errors, Task(TaskError), /// Errors from #VC handler @@ -104,3 +107,9 @@ impl From for SvsmError { Self::Apic(err) } } + +impl From for SvsmError { + fn from(err: ObjError) -> Self { + Self::Obj(err) + } +} diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index a95368dac..26e5a51bc 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -5,5 +5,7 @@ // Author: Joerg Roedel mod handlers; +mod obj; pub use handlers::*; +pub use obj::{Obj, ObjError, ObjHandle}; diff --git a/kernel/src/syscall/obj.rs b/kernel/src/syscall/obj.rs new file mode 100644 index 000000000..624d2f809 --- /dev/null +++ b/kernel/src/syscall/obj.rs @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2024 Intel Corporation. +// +// Author: Chuanxiao Dong + +extern crate alloc; + +use crate::cpu::percpu::current_task; +use crate::error::SvsmError; +use alloc::sync::Arc; + +#[derive(Clone, Copy, Debug)] +pub enum ObjError { + InvalidHandle, + NotFound, +} + +/// An object represents the type of resource like file, VM, vCPU in the +/// COCONUT-SVSM kernel which can be accessible by the user mode. The Obj +/// trait is defined for such type of resource, which can be used to define +/// the common functionalities of the objects. With the trait bounds of Send +/// and Sync, the objects implementing Obj trait could be sent to another +/// thread and shared between threads safely. +pub trait Obj: Send + Sync + core::fmt::Debug {} + +/// ObjHandle is a unique identifier for an object in the current process. +/// An ObjHandle can be converted to a u32 id which can be used by the user +/// mode to access this object. The passed id from the user mode by syscalls +/// can be converted to an `ObjHandle`, which is used to access the object in +/// the COCONUT-SVSM kernel. +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct ObjHandle(u32); + +impl ObjHandle { + pub fn new(id: u32) -> Self { + Self(id) + } +} + +impl From for ObjHandle { + #[inline] + fn from(id: u32) -> Self { + Self(id) + } +} + +impl From for u32 { + #[inline] + fn from(obj_handle: ObjHandle) -> Self { + obj_handle.0 + } +} + +/// Add an object to the current process and assigns it an `ObjHandle`. +/// +/// # Arguments +/// +/// * `obj` - An `Arc` representing the object to be added. +/// +/// # Returns +/// +/// * `Result` - Returns the object handle of the +/// added object if successful, or an `SvsmError` on failure. +/// +/// # Errors +/// +/// This function will return an error if adding the object to the +/// current task fails. +#[allow(dead_code)] +pub fn obj_add(obj: Arc) -> Result { + current_task().add_obj(obj) +} + +/// Closes an object identified by its ObjHandle. +/// +/// # Arguments +/// +/// * `id` - The ObjHandle for the object to be closed. +/// +/// # Returns +/// +/// * `Result>, SvsmError>` - Returns the `Arc` +/// on success, or an `SvsmError` on failure. +/// +/// # Errors +/// +/// This function will return an error if removing the object from the +/// current task fails. +#[allow(dead_code)] +pub fn obj_close(id: ObjHandle) -> Result, SvsmError> { + current_task().remove_obj(id) +} + +/// Retrieves an object by its ObjHandle. +/// +/// # Arguments +/// +/// * `id` - The ObjHandle for the object to be retrieved. +/// +/// # Returns +/// +/// * `Result>, SvsmError>` - Returns the `Arc` on +/// success, or an `SvsmError` on failure. +/// +/// # Errors +/// +/// This function will return an error if retrieving the object from the +/// current task fails. +#[allow(dead_code)] +pub fn obj_get(id: ObjHandle) -> Result, SvsmError> { + current_task().get_obj(id) +} diff --git a/kernel/src/task/tasks.rs b/kernel/src/task/tasks.rs index f651c5a02..cd5f88084 100644 --- a/kernel/src/task/tasks.rs +++ b/kernel/src/task/tasks.rs @@ -6,6 +6,7 @@ extern crate alloc; +use alloc::collections::btree_map::BTreeMap; use alloc::sync::Arc; use core::fmt; use core::mem::size_of; @@ -27,6 +28,7 @@ use crate::mm::{ mappings::create_anon_mapping, mappings::create_file_mapping, VMMappingGuard, SVSM_PERTASK_BASE, SVSM_PERTASK_END, SVSM_PERTASK_STACK_BASE, USER_MEM_END, USER_MEM_START, }; +use crate::syscall::{Obj, ObjError, ObjHandle}; use crate::types::{SVSM_USER_CS, SVSM_USER_DS}; use crate::utils::MemoryRegion; use intrusive_collections::{intrusive_adapter, LinkedListAtomicLink}; @@ -139,6 +141,9 @@ pub struct Task { /// Link to scheduler run queue runlist_link: LinkedListAtomicLink, + + /// Objects shared among threads within the same process + objs: Arc>>>, } // SAFETY: Send + Sync is required for Arc to implement Send. All members @@ -206,6 +211,7 @@ impl Task { id: TASK_ID_ALLOCATOR.next_id(), list_link: LinkedListAtomicLink::default(), runlist_link: LinkedListAtomicLink::default(), + objs: Arc::new(RWLock::new(BTreeMap::new())), })) } @@ -249,6 +255,7 @@ impl Task { id: TASK_ID_ALLOCATOR.next_id(), list_link: LinkedListAtomicLink::default(), runlist_link: LinkedListAtomicLink::default(), + objs: Arc::new(RWLock::new(BTreeMap::new())), })) } @@ -475,6 +482,87 @@ impl Task { self.vm_user_range.as_ref().unwrap().remove(addr)?; Ok(()) } + + /// Adds an object to the current task. + /// + /// # Arguments + /// + /// * `obj` - The object to be added. + /// + /// # Returns + /// + /// * `Result` - Returns the object handle for the object + /// to be added if successful, or an `SvsmError` on failure. + /// + /// # Errors + /// + /// This function will return an error if allocating the object handle fails. + pub fn add_obj(&self, obj: Arc) -> Result { + let mut objs = self.objs.lock_write(); + let last_key = objs + .keys() + .last() + .map_or(Some(0), |k| u32::from(*k).checked_add(1)) + .ok_or(SvsmError::from(ObjError::InvalidHandle))?; + let id = ObjHandle::new(if last_key != objs.len() as u32 { + objs.keys() + .enumerate() + .find(|(i, &key)| *i as u32 != u32::from(key)) + .unwrap() + .0 as u32 + } else { + last_key + }); + + objs.insert(id, obj); + + Ok(id) + } + + /// Removes an object from the current task. + /// + /// # Arguments + /// + /// * `id` - The ObjHandle for the object to be removed. + /// + /// # Returns + /// + /// * `Result>, SvsmError>` - Returns the removed `Arc` + /// on success, or an `SvsmError` on failure. + /// + /// # Errors + /// + /// This function will return an error if the object handle id does not + /// exist in the current task. + pub fn remove_obj(&self, id: ObjHandle) -> Result, SvsmError> { + self.objs + .lock_write() + .remove(&id) + .ok_or(ObjError::NotFound.into()) + } + + /// Retrieves an object from the current task. + /// + /// # Arguments + /// + /// * `id` - The ObjHandle for the object to be retrieved. + /// + /// # Returns + /// + /// * `Result>, SvsmError>` - Returns the `Arc` on + /// success, or an `SvsmError` on failure. + /// + /// # Errors + /// + /// This function will return an error if the object handle id does not exist + /// in the current task. + pub fn get_obj(&self, id: ObjHandle) -> Result, SvsmError> { + self.objs + .lock_read() + .get(&id) + .cloned() + .ok_or(ObjError::NotFound.into()) + } } pub fn is_task_fault(vaddr: VirtAddr) -> bool { diff --git a/syscall/src/lib.rs b/syscall/src/lib.rs index d02cfdf2d..18472ad52 100644 --- a/syscall/src/lib.rs +++ b/syscall/src/lib.rs @@ -6,5 +6,7 @@ #![no_std] mod numbers; +mod obj; pub use numbers::*; +pub use obj::*; diff --git a/syscall/src/obj.rs b/syscall/src/obj.rs new file mode 100644 index 000000000..e5802e900 --- /dev/null +++ b/syscall/src/obj.rs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) 2024 Intel Corporation. +// +// Author: Chuanxiao Dong + +/// The object is exposed to the user mode via the object-opening related +/// syscalls, which returns the id of the object created by the COCONUT-SVSM +/// kernel. The user mode can make use this id to access the corresponding +/// object via other syscalls. From the user mode's point of view, an +/// ObjHanle is defined to wrap a u32 which is the value returned by an +/// object-opening syscall. This u32 value can be used as the input for the +/// syscalls to access the corresponding kernel object. +#[derive(Debug)] +pub struct ObjHandle(u32); + +impl ObjHandle { + #[allow(dead_code)] + pub(crate) fn new(id: u32) -> Self { + Self(id) + } +} + +impl From<&ObjHandle> for u32 { + #[inline] + fn from(obj_handle: &ObjHandle) -> Self { + obj_handle.0 + } +}