Skip to content

Commit

Permalink
Merge pull request coconut-svsm#505 from vijaydhanraj/userptr_wrapper
Browse files Browse the repository at this point in the history
Introduce UserPtr wrapper around GuestPtr
  • Loading branch information
joergroedel authored Oct 30, 2024
2 parents aba0c0e + 3121843 commit bb6e922
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 0 deletions.
27 changes: 27 additions & 0 deletions kernel/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use crate::sev::SevSnpError;
use crate::syscall::ObjError;
use crate::task::TaskError;
use elf::ElfError;
use syscall::SysCallError;

/// Errors related to APIC handling. These may originate from multiple
/// layers in the system.
Expand Down Expand Up @@ -74,6 +75,8 @@ pub enum SvsmError {
InvalidAddress,
/// Error reported when convert a usize to Bytes
InvalidBytes,
/// Error reported when converting to UTF-8
InvalidUtf8,
/// Errors related to firmware parsing
Firmware,
/// Errors related to console operation
Expand Down Expand Up @@ -113,3 +116,27 @@ impl From<ObjError> for SvsmError {
Self::Obj(err)
}
}

impl From<SvsmError> for SysCallError {
fn from(err: SvsmError) -> Self {
match err {
SvsmError::Alloc(AllocError::OutOfMemory) => SysCallError::ENOMEM,
SvsmError::FileSystem(FsError::FileExists) => SysCallError::EEXIST,

SvsmError::FileSystem(FsError::FileNotFound) | SvsmError::Obj(ObjError::NotFound) => {
SysCallError::ENOTFOUND
}

SvsmError::NotSupported => SysCallError::ENOTSUPP,

SvsmError::FileSystem(FsError::Inval)
| SvsmError::Obj(ObjError::InvalidHandle)
| SvsmError::Mem
| SvsmError::InvalidAddress
| SvsmError::InvalidBytes
| SvsmError::InvalidUtf8 => SysCallError::EINVAL,

_ => SysCallError::UNKNOWN,
}
}
}
103 changes: 103 additions & 0 deletions kernel/src/mm/guestmem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,20 @@
//
// Author: Joerg Roedel <[email protected]>

extern crate alloc;

use crate::address::{Address, VirtAddr};
use crate::cpu::x86::smap::{clac, stac};
use crate::error::SvsmError;
use crate::insn_decode::{InsnError, InsnMachineMem};
use crate::mm::{USER_MEM_END, USER_MEM_START};
use alloc::string::String;
use alloc::vec::Vec;
use core::arch::asm;
use core::ffi::c_char;
use core::mem::{size_of, MaybeUninit};
use syscall::PATH_MAX;
use zerocopy::FromBytes;

#[inline]
pub fn read_u8(v: VirtAddr) -> Result<u8, SvsmError> {
Expand Down Expand Up @@ -269,6 +278,100 @@ impl<T: Copy> InsnMachineMem for GuestPtr<T> {
}
}

struct UserAccessGuard;

impl UserAccessGuard {
pub fn new() -> Self {
stac();
Self
}
}

impl Drop for UserAccessGuard {
fn drop(&mut self) {
clac();
}
}

#[derive(Debug)]
pub struct UserPtr<T: Copy> {
guest_ptr: GuestPtr<T>,
}

impl<T: Copy> UserPtr<T> {
#[inline]
pub fn new(v: VirtAddr) -> Self {
Self {
guest_ptr: GuestPtr::new(v),
}
}

fn check_bounds(&self) -> bool {
let v = VirtAddr::from(self.guest_ptr.ptr);

(USER_MEM_START..USER_MEM_END).contains(&v)
&& (USER_MEM_START..USER_MEM_END).contains(&(v + size_of::<T>()))
}

#[inline]
pub fn read(&self) -> Result<T, SvsmError>
where
T: FromBytes,
{
if !self.check_bounds() {
return Err(SvsmError::InvalidAddress);
}
let _guard = UserAccessGuard::new();
unsafe { self.guest_ptr.read() }
}

#[inline]
pub fn write(&self, buf: T) -> Result<(), SvsmError> {
self.write_ref(&buf)
}

#[inline]
pub fn write_ref(&self, buf: &T) -> Result<(), SvsmError> {
if !self.check_bounds() {
return Err(SvsmError::InvalidAddress);
}
let _guard = UserAccessGuard::new();
unsafe { self.guest_ptr.write_ref(buf) }
}

#[inline]
pub const fn cast<N: Copy>(&self) -> UserPtr<N> {
UserPtr {
guest_ptr: self.guest_ptr.cast(),
}
}

#[inline]
pub fn offset(&self, count: isize) -> UserPtr<T> {
UserPtr {
guest_ptr: self.guest_ptr.offset(count),
}
}
}

impl UserPtr<c_char> {
/// Reads a null-terminated C string from the user space.
/// Allocates memory for the string and returns a `String`.
pub fn read_c_string(&self) -> Result<String, SvsmError> {
let mut buffer = Vec::new();

for offset in 0..PATH_MAX {
let current_ptr = self.offset(offset as isize);
let char_result = current_ptr.read()?;
match char_result {
0 => return String::from_utf8(buffer).map_err(|_| SvsmError::InvalidUtf8),
c => buffer.push(c as u8),
}
}
Err(SvsmError::InvalidBytes)
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
93 changes: 93 additions & 0 deletions syscall/src/call.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2024 Intel Corporation.
//
// Author: Chuanxiao Dong <[email protected]>

use core::arch::asm;

/// Macro to generate system call functions with varying numbers of arguments.
macro_rules! syscall {
($($name:ident($a:ident, $($b:ident, $($c:ident, $($d:ident, $($e:ident, $($f:ident, )?)?)?)?)?);)+) => {
$(
/// Performs a system call with arguments.
///
/// # Safety
///
/// This function is unsafe because it performs a system call.
/// The kernel should check the syscall number and return the
/// expected result back.
#[allow(dead_code)]
pub unsafe fn $name($a: u64, $($b: u64, $($c: u64, $($d: u64, $($e: u64, $($f: u64)?)?)?)?)?) -> Result<u64, SysCallError> {
let mut ret = $a;
asm!(
"int 0x80",
inout("rax") ret,
$(
in("rdi") $b,
$(
in("rsi") $c,
$(
in("r8") $d,
$(
in("r9") $e,
$(
in("r10") $f,
)?
)?
)?
)?
)?
out("rcx") _,
out("r11") _,
options(nostack),
);

if ret > (u64::MAX - u64::from(u16::MAX)) {
return Err(SysCallError::from(ret as i32));
}
Ok(ret)
}
)+
};
}

syscall! {
syscall0(a,);
syscall1(a, b,);
syscall2(a, b, c,);
syscall3(a, b, c, d,);
syscall4(a, b, c, d, e,);
syscall5(a, b, c, d, e, f,);
}

#[derive(Clone, Copy, Debug)]
pub enum SysCallError {
EINVAL = -1,
ENOSYS = -2,
ENOMEM = -3,
EPERM = -4,
EFAULT = -5,
EBUSY = -6,
ENOTFOUND = -7,
ENOTSUPP = -8,
EEXIST = -9,
UNKNOWN = -128,
}

impl From<i32> for SysCallError {
fn from(e: i32) -> SysCallError {
match e {
-1 => SysCallError::EINVAL,
-2 => SysCallError::ENOSYS,
-3 => SysCallError::ENOMEM,
-4 => SysCallError::EPERM,
-5 => SysCallError::EFAULT,
-6 => SysCallError::EBUSY,
-7 => SysCallError::ENOTFOUND,
-8 => SysCallError::ENOTSUPP,
-9 => SysCallError::EEXIST,
_ => SysCallError::UNKNOWN,
}
}
}
2 changes: 2 additions & 0 deletions syscall/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
// Author: Joerg Roedel <[email protected]>
#![no_std]

mod call;
mod numbers;
mod obj;

pub use call::SysCallError;
pub use numbers::*;
pub use obj::*;
3 changes: 3 additions & 0 deletions syscall/src/numbers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@

pub const SYS_HELLO: u64 = 0;
pub const SYS_EXIT: u64 = 1;

///Maximum length of path name including null character in bytes
pub const PATH_MAX: usize = 4096;

0 comments on commit bb6e922

Please sign in to comment.