enarx-shim-kvm 0.6.3

Enarx KVM shim
Documentation
// SPDX-License-Identifier: Apache-2.0

//! syscall interface layer between assembler and rust

use crate::hostcall::{HostCall, UserMemScope};
use crate::spin::{Locked, RacyCell};

use core::arch::asm;
use core::mem::size_of;

use sallyport::guest;
use sallyport::guest::Handler;
use sallyport::item::enarxcall::{SYS_GETATT, SYS_GETKEY};
#[cfg(feature = "dbg")]
use sallyport::libc::{SYS_write, STDERR_FILENO, STDOUT_FILENO};
use spinning::Lazy;

#[repr(C)]
struct X8664DoubleReturn {
    rax: u64,
    rdx: u64,
}

/// syscall service routine
///
/// # Safety
///
/// This function is not be called from rust.
#[cfg_attr(coverage, no_coverage)]
#[naked]
pub unsafe extern "sysv64" fn _syscall_enter() -> ! {
    // TaskStateSegment.privilege_stack_table[0]
    const KERNEL_RSP_OFF: usize = size_of::<u32>();
    // TaskStateSegment.privilege_stack_table[3]
    const USR_RSP_OFF: usize = size_of::<u32>() + 3 * size_of::<u64>();

    asm!(
        // prepare the stack for sysretq and load the kernel rsp
        "swapgs",                                           // set gs segment to TSS

        // swapgs variant of Spectre V1. Disable speculation past this point
        "lfence",

        "mov    QWORD PTR gs:{USR},     rsp",               // save userspace rsp
        "mov    rsp,                    QWORD PTR gs:{KRN}",// load kernel rsp
        "push   QWORD PTR gs:{USR}",                        // push userspace rsp - stack_pointer_ring_3
        "mov    QWORD PTR gs:{USR},     0x0",               // clear userspace rsp in the TSS
        "push   r11",                                       // push RFLAGS stored in r11
        "push   rcx",                                       // push userspace return pointer
        "push   rbp",
        "mov    rbp,                    rsp",               // Save stack frame

        // Arguments in registers:
        // SYSV:    rdi, rsi, rdx, rcx, r8, r9
        // SYSCALL: rdi, rsi, rdx, r10, r8, r9 and syscall number in rax
        "mov    rcx,                    r10",

        // These will be preserved by `syscall_rust` via the SYS-V ABI
        // rbx, rsp, rbp, r12, r13, r14, r15

        // save registers
        "push   rdi",
        "push   rsi",
        "push   r10",
        "push   r9",
        "push   r8",

        // syscall number on the stack as the seventh argument
        "push   rax",

        "call   {syscall_rust}",

        // skip rax pop, as it is the return value
        "add    rsp,                    0x8",

        // restore registers
        "pop    r8",
        "pop    r9",
        "pop    r10",
        "pop    rsi",
        "pop    rdi",

        "pop    rbp",

        "pop    rcx",                                       // Pop userspace return pointer
        "pop    r11",                                       // pop rflags to r11
        "pop    QWORD PTR gs:{USR}",                        // Pop userspace rsp
        "mov    rsp, gs:{USR}",                             // Restore userspace rsp

        "swapgs",

        // swapgs variant of Spectre V1. Disable speculation past this point
        "lfence",

        "sysretq",

        USR = const USR_RSP_OFF,
        KRN = const KERNEL_RSP_OFF,

        syscall_rust = sym syscall_rust,

        options(noreturn)
    )
}

/// Thread local storage
/// FIXME: when using multithreading
pub static THREAD_TLS: Lazy<Locked<&mut guest::ThreadLocalStorage>> = Lazy::new(|| unsafe {
    static TLSHANDLE: RacyCell<guest::ThreadLocalStorage> =
        RacyCell::new(guest::ThreadLocalStorage::new());
    Locked::<&mut guest::ThreadLocalStorage>::new(&mut (*TLSHANDLE.get()))
});

/// Handle a syscall in rust
#[allow(clippy::many_single_char_names)]
extern "sysv64" fn syscall_rust(
    a: usize,
    b: usize,
    c: usize,
    d: usize,
    e: usize,
    f: usize,
    nr: usize,
) -> X8664DoubleReturn {
    let orig_rdx: usize = c;

    #[cfg(feature = "dbg")]
    if !(nr == SYS_write as usize && (a == STDERR_FILENO as usize || a == STDOUT_FILENO as usize)) {
        eprintln!("syscall {}", nr)
    }

    let mut tls = THREAD_TLS.lock();
    let mut h = HostCall::try_new(&mut tls).unwrap();

    let usermemscope = UserMemScope;

    match nr as i64 {
        SYS_GETKEY => {
            let ret = h.get_key(&usermemscope, a, b);

            #[cfg(feature = "dbg")]
            eprintln!(
                "syscall SYS_GETKEY = {}",
                ret.map_or_else(|e| -e as usize, |v| v)
            );

            match ret {
                Err(e) => {
                    X8664DoubleReturn {
                        rax: e.checked_neg().unwrap() as _,
                        // Preserve `rdx` as it is normally not clobbered with a syscall
                        rdx: orig_rdx as _,
                    }
                }
                Ok(rax) => {
                    X8664DoubleReturn {
                        rax: rax as _,
                        // Preserve `rdx` as it is normally not clobbered with a syscall
                        rdx: orig_rdx as _,
                    }
                }
            }
        }
        SYS_GETATT => {
            let ret = h.get_attestation(&usermemscope, a, b, c, d);

            #[cfg(feature = "dbg")]
            eprintln!(
                "syscall SYS_GETATT = {}",
                ret.map_or_else(|e| -e as usize, |v| v[0])
            );

            match ret {
                Err(e) => {
                    X8664DoubleReturn {
                        rax: e.checked_neg().unwrap() as _,
                        // Preserve `rdx` as it is normally not clobbered with a syscall
                        rdx: orig_rdx as _,
                    }
                }
                Ok([rax, rdx]) => X8664DoubleReturn {
                    rax: rax as _,
                    rdx: rdx as _,
                },
            }
        }
        _ => {
            let ret = unsafe { h.syscall(&usermemscope, [nr, a, b, c, d, e, f]) };

            #[cfg(feature = "dbg")]
            if !(nr == SYS_write as usize
                && (a == STDERR_FILENO as usize || a == STDOUT_FILENO as usize))
            {
                eprintln!(
                    "syscall {} = {}",
                    nr,
                    ret.map_or_else(|e| -e as usize, |v| v[0])
                );
            }

            match ret {
                Err(e) => {
                    X8664DoubleReturn {
                        rax: e.checked_neg().unwrap() as _,
                        // Preserve `rdx` as it is normally not clobbered with a syscall
                        rdx: orig_rdx as _,
                    }
                }
                Ok([rax, _]) => {
                    X8664DoubleReturn {
                        rax: rax as _,
                        // Preserve `rdx` as it is normally not clobbered with a syscall
                        rdx: orig_rdx as _,
                    }
                }
            }
        }
    }
}