SDKRuntime: plumb application access

Setup a connection to the SDKRuntime for each application. To do this
add an SDKManager interface to the SDKRuntime for the ProcessManager to
obtain a badged endpoint and install that in each application's CNode.
SDKRuntime now rejects requests received without a registered badge.

RPC's are handled entirely in Rust (no CAmkES). ProcessManager sets up
RPC resources and delivers them to an application through registers.
The application-side SDK runtime uses the resources to marshal RPC
parameters in a page that is attached to the IPC buffer sent to the
SDKRuntime. Reply parameters are written to the shared page and decoded
on return.

Overhaul the SDKRuntime api to be like SecurityCoordinator to consolidate
parameter marhsaling/unmarshaling and to simplify adding new methods.

Rust applications use the SDKRuntime interface directly. C application
will wrap a C interface around the Rust impl (TBD).

Specific changes:
- add SDKManagerInterface
- sel4bundle now plumbs a connection to the SDKRuntime, the CNode slot
  with the capability is passed to the application to future-proof CNode
  setup changes (an alternative is to use a global const since we control
  the application-side runtime api's)
- add kata-sdk-manager crate with SDKManager client interface support;
  the only api's are get_endpoint (to get a badged endpoint to SDKRuntime),
  release_endpoint (to remove a badged endpoint), and capscan (to dump
  the SDKRuntime's top-level CNode)
- add "capscan sdk" in the shell to inspect the SDKRuntime service
- make SDKRuntime require a registered badge on inbound IPCs
- fill-in ping & log SDK api's
- connect ProcessManager to SDKRuntime for SDKManager api use,
  everything else happens outside CAmkES
- make SDKRuntime lock against concurrent requests--the SDKManager
  runs concurrently and shares SDKRuntime state
- remove kata-shell test_sdk_* commands (replaced by test applications)

Change-Id: I7810949ad0051ff8eda244e0385f662882a556e4
GitOrigin-RevId: 5fef55428e076f670cff325965047c98d84cfbca
This commit is contained in:
Sam Leffler 2022-08-31 21:53:02 +00:00
parent 48c3e88004
commit d0d46c89e1
27 changed files with 894 additions and 134 deletions

View File

@ -19,7 +19,7 @@ import <MlCoordinatorInterface.camkes>;
import <MemoryInterface.camkes>;
import <SecurityCoordinatorInterface.camkes>;
import <TimerServiceInterface.camkes>;
import <SDKRuntimeInterface.camkes>;
import <SDKManagerInterface.camkes>;
component DebugConsole {
control;
@ -38,8 +38,8 @@ component DebugConsole {
uses PackageManagementInterface pkg_mgmt;
uses ProcessControlInterface proc_ctrl;
// TODO(b/200707300): for debugging
uses SecurityCoordinatorInterface security;
uses SDKRuntimeInterface sdk_runtime;
maybe uses SecurityCoordinatorInterface security;
maybe uses SDKManagerInterface sdk_manager;
uses Timer timer;

View File

@ -29,7 +29,6 @@ default = [
"TEST_MEMORY_MANAGER",
"TEST_ML_COORDINATOR",
"TEST_PANIC",
"TEST_SDK_RUNTIME",
"TEST_SECURITY_COORDINATOR",
"TEST_TIMER_SERVICE",
]
@ -62,6 +61,6 @@ kata-proc-interface = { path = "../../ProcessManager/kata-proc-interface" }
kata-os-common = { path = "../../kata-os-common" }
kata-security-interface = { path = "../../SecurityCoordinator/kata-security-interface" }
kata-timer-interface = { path = "../../TimerService/kata-timer-interface" }
kata-sdk-interface = { path = "../../SDKRuntime/kata-sdk-interface" }
kata-sdk-manager = { path = "../../SDKRuntime/kata-sdk-manager" }
log = { version = "0.4", features = ["release_max_level_info"] }
zmodem = { path = "../zmodem" }

View File

@ -54,8 +54,6 @@ mod test_memory_manager;
mod test_ml_coordinator;
#[cfg(feature = "TEST_PANIC")]
mod test_panic;
#[cfg(feature = "TEST_SDK_RUNTIME")]
mod test_sdk_runtime;
#[cfg(feature = "TEST_SECURITY_COORDINATOR")]
mod test_security_coordinator;
#[cfg(feature = "TEST_TIMER_SERVICE")]
@ -145,8 +143,6 @@ pub fn repl<T: io::BufRead>(output: &mut dyn io::Write, input: &mut T, builtin_c
test_ml_coordinator::add_cmds(&mut cmds);
#[cfg(feature = "TEST_PANIC")]
test_panic::add_cmds(&mut cmds);
#[cfg(feature = "TEST_SDK_RUNTIME")]
test_sdk_runtime::add_cmds(&mut cmds);
#[cfg(feature = "TEST_SECURITY_COORDINATOR")]
test_security_coordinator::add_cmds(&mut cmds);
#[cfg(feature = "TEST_TIMER_SERVICE")]
@ -292,6 +288,9 @@ fn capscan_command(
Some("mlcoord") => {
let _ = kata_mlcoord_capscan();
}
Some("sdk") => {
let _ = kata_sdk_manager::kata_sdk_manager_capscan();
}
Some("security") => {
let _ = kata_security_interface::kata_security_capscan();
}
@ -309,8 +308,8 @@ fn capscan_command(
writeln!(output, " memory (MemoryManager)")?;
writeln!(output, " process (ProcessManager)")?;
writeln!(output, " mlcoord (MlCoordinator)")?;
writeln!(output, " sdk (SDKRuntime)")?;
writeln!(output, " securiy (SecurityCoordinator)")?;
writeln!(output, " storage (StorageManager)")?;
writeln!(output, " timer (TimerService)")?;
writeln!(output, "anything else is treated as a bundle_id")?;
}

View File

@ -1,45 +0,0 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! SDK Runtime shell test commands
use crate::CmdFn;
use crate::CommandError;
use crate::HashMap;
use core::fmt::Write;
use kata_io as io;
use kata_sdk_interface::kata_sdk_ping;
pub fn add_cmds(cmds: &mut HashMap<&str, CmdFn>) {
cmds.extend([("test_sdkping", sdk_ping_command as CmdFn)]);
}
fn sdk_ping_command(
_args: &mut dyn Iterator<Item = &str>,
_input: &mut dyn io::BufRead,
output: &mut dyn io::Write,
_builtin_cpio: &[u8],
) -> Result<(), CommandError> {
match kata_sdk_ping() {
Ok(()) => {
writeln!(output, "pong received")?;
}
Err(sdkerror) => {
writeln!(output, "ping failed: {:?}", sdkerror)?;
}
}
Ok(())
}

View File

@ -19,6 +19,7 @@ import <MemoryInterface.camkes>;
import <PackageManagementInterface.camkes>;
import <ProcessControlInterface.camkes>;
import <SecurityCoordinatorInterface.camkes>;
import <SDKManagerInterface.camkes>;
component ProcessManager {
provides PackageManagementInterface pkg_mgmt;
@ -27,6 +28,7 @@ component ProcessManager {
maybe uses LoggerInterface logger;
uses MemoryInterface memory;
uses SecurityCoordinatorInterface security;
uses SDKManagerInterface sdk_manager;
// Enable KataOS CAmkES support.
attribute int kataos = true;

View File

@ -25,7 +25,7 @@ use kata_os_common::camkes::Camkes;
use kata_os_common::sel4_sys;
use kata_os_common::slot_allocator;
use kata_proc_interface::*;
use kata_proc_manager::KATA_PROC;
use kata_proc_manager::KataProcManager;
use log::trace;
use sel4_sys::seL4_CPtr;
@ -33,6 +33,8 @@ use sel4_sys::seL4_CPtr;
use slot_allocator::KATA_CSPACE_SLOTS;
static mut CAMKES: Camkes = Camkes::new("ProcessManager");
// NB: KATA_PROC cannot be used before setup is completed with a call to init()
static mut KATA_PROC: KataProcManager = KataProcManager::empty();
// TODO(sleffler): 0 is valid
static mut PKG_MGMT_RECV_SLOT: seL4_CPtr = 0;

View File

@ -190,8 +190,19 @@ impl<'a> BundleImage<'a> {
// XXX if unmap fails bounce is cleaned up on drop but we probably want it moved instead
unsafe { seL4_Page_Unmap(self.bounce.slot) }
.map_err(|_| BundleImageError::PageUnmapFailed)?;
self.bounce
.move_from(self.frames.cnode, cptr, self.frames.depth)
// XXX temp workaround for optimizer bug
// self.bounce.move_from(self.frames.cnode, cptr, self.frames.depth).map_err(|_| BundleImageError::CapMoveFailed)?;
let src = self.bounce.get_path();
unsafe {
sel4_sys::seL4_CNode_Move(
self.frames.cnode,
cptr,
self.frames.depth,
src.0,
src.1,
src.2,
)
}
.map_err(|_| BundleImageError::CapMoveFailed)?;
}
self.last_frame = self.cur_frame;

View File

@ -38,6 +38,7 @@ kata-io = { path = "../../DebugConsole/kata-io" }
kata-proc-interface = { path = "../kata-proc-interface" }
kata-memory-interface = { path = "../../MemoryManager/kata-memory-interface" }
kata-os-common = { path = "../../kata-os-common" }
kata-sdk-manager = { path = "../../SDKRuntime/kata-sdk-manager" }
kata-security-interface = { path = "../../SecurityCoordinator/kata-security-interface" }
log = { version = "0.4", features = ["release_max_level_info"] }
smallstr = "0.2"

View File

@ -40,10 +40,6 @@ use sel4bundle::seL4BundleImpl;
mod proc_manager;
pub use proc_manager::ProcessManager;
// NB: KATA_PROC cannot be used before setup is completed with a call to init()
#[cfg(not(test))]
pub static mut KATA_PROC: KataProcManager = KataProcManager::empty();
// KataProcManager bundles an instance of the ProcessManager that operates
// on KataOS interfaces and synchronizes public use with a Mutex. There is
// a two-step dance to setup an instance because we want KATA_PROC static
@ -56,7 +52,7 @@ impl KataProcManager {
// Constructs a partially-initialized instance; to complete call init().
// This is needed because we need a const fn for static setup and with
// that constraint we cannot reference self.interface.
const fn empty() -> KataProcManager {
pub const fn empty() -> KataProcManager {
KataProcManager {
manager: Mutex::new(None),
}

View File

@ -38,6 +38,8 @@ use kata_proc_interface::Bundle;
use kata_proc_interface::BundleImage;
use kata_proc_interface::BundleImplInterface;
use kata_proc_interface::ProcessManagerError;
use kata_sdk_manager::kata_sdk_manager_get_endpoint;
use kata_sdk_manager::kata_sdk_manager_release_endpoint;
use log::{debug, error, info, trace};
use io::Read;
@ -54,7 +56,6 @@ use sel4_sys::seL4_EndpointObject;
use sel4_sys::seL4_Error;
use sel4_sys::seL4_MinSchedContextBits;
use sel4_sys::seL4_PageTableObject;
use sel4_sys::seL4_ReplyObject;
use sel4_sys::seL4_Result;
use sel4_sys::seL4_SchedContextObject;
use sel4_sys::seL4_SmallPageObject;
@ -151,17 +152,17 @@ const NOCAP: seL4_CPtr = 0;
// Layout of the CNode holding dynamic_objs. All entries are singletons
// except for STACK_COUNT so symbols up to STACK_SLOT can also be used to
// index into dynamic_objs. Perhaps too fragile...
// TODO(sleffler): SDK runtime state should be seetup by SDK in case it
// needs more than 1 endpoint + 1 small frame
const TCB_SLOT: usize = 0;
const FAULT_EP_SLOT: usize = TCB_SLOT + 1;
const SDK_EP_SLOT: usize = FAULT_EP_SLOT + 1;
const SDK_REPLY_SLOT: usize = SDK_EP_SLOT + 1;
const SCHED_CONTEXT_SLOT: usize = SDK_REPLY_SLOT + 1;
const SCHED_CONTEXT_SLOT: usize = FAULT_EP_SLOT + 1;
// TODO(sleffler): VSpace layout is arch-specific
const PD_SLOT: usize = SCHED_CONTEXT_SLOT + 1;
const PT_SLOT: usize = PD_SLOT + 1;
const IPCBUFFER_SLOT: usize = PT_SLOT + 1;
const SDK_RPC_FRAME_SLOT: usize = IPCBUFFER_SLOT + 1;
const STACK_SLOT: usize = SDK_RPC_FRAME_SLOT + 1;
const SDK_FRAME_SLOT: usize = IPCBUFFER_SLOT + 1;
const STACK_SLOT: usize = SDK_FRAME_SLOT + 1;
const STACK_COUNT: usize = 4; // 16K for stack (XXX get from manifest)
const FRAME_SLOT: usize = STACK_SLOT + STACK_COUNT;
// NB: FRAME_SLOT count is based on the BundleImage
@ -192,6 +193,8 @@ pub struct seL4BundleImpl {
tcb_ipcbuffer_addr: seL4_Word, // Address of IPCBuffer in app's VSpace
tcb_pc: seL4_Word, // Initial pc in app's VSpace
tcb_sp: seL4_Word, // Initial stack pointer in app's VSpace
sdk_ep_slot: seL4_CPtr,
sdk_frame_addr: seL4_Word, // Address of SDK frame in app's VSpace
stack_base: seL4_Word, // Base address of stack in app's VSpace
cspace_root_data: seL4_Word,
@ -252,9 +255,6 @@ impl seL4BundleImpl {
ObjDesc::new(seL4_TCBObject, 1, TCB_SLOT),
// fault redirect to SDK/ProcessManager
ObjDesc::new(seL4_EndpointObject, 1, FAULT_EP_SLOT),
// interface to SDK
ObjDesc::new(seL4_EndpointObject, 1, SDK_EP_SLOT),
ObjDesc::new(seL4_ReplyObject, 1, SDK_REPLY_SLOT),
// SchedContext for main thread
ObjDesc::new(seL4_SchedContextObject, seL4_MinSchedContextBits, SCHED_CONTEXT_SLOT),
// VSpace root (PD)
@ -263,8 +263,8 @@ impl seL4BundleImpl {
ObjDesc::new(seL4_PageTableObject, 1, PT_SLOT),
// IPC buffer frame
ObjDesc::new(seL4_SmallPageObject, 1, IPCBUFFER_SLOT),
// RPC to SDK frame?
ObjDesc::new(seL4_SmallPageObject, 1, SDK_RPC_FRAME_SLOT),
// Frame for SDK RPC parameters
ObjDesc::new(seL4_SmallPageObject, 1, SDK_FRAME_SLOT),
// Stack frames (guard frames are unpopulated PT slots)
ObjDesc::new(seL4_SmallPageObject, STACK_COUNT, STACK_SLOT),
// Page frames for application binary.
@ -288,7 +288,6 @@ impl seL4BundleImpl {
// XXX setup fault endpoint (allocate id)
// XXX setup temporal fault endpoint (allocate id)
// XXX setup SDK runtime (e.g. badge)
Ok(seL4BundleImpl {
bundle_frames: bundle_frames.clone(),
@ -306,6 +305,8 @@ impl seL4BundleImpl {
tcb_ipcbuffer_addr: 0,
tcb_pc: entry_point.unwrap_or(first_vaddr), // NB: filled in from BundleImage
tcb_sp: 0,
sdk_ep_slot: FRAME_SLOT + nframes, // SDK endpoint goes at the end
sdk_frame_addr: 0,
stack_base: 0,
// 1-level CSpace addressing
@ -444,6 +445,7 @@ impl seL4BundleImpl {
//
// NB: guard pages are unmapped frames (not a frame mapped read-only).
// XXX verify resources are reclaimed on failure?
// TODO(sleffler): who zero's any of this (or maybe not needed)?
fn init_vspace(&mut self) -> seL4_Result {
let rights_rwn = seL4_CapRights::new(
// NB: grant =>'s X on ARM+RISCV
@ -455,6 +457,7 @@ impl seL4BundleImpl {
let pd = &self.dynamic_objs.objs[PD_SLOT];
let pt = &self.dynamic_objs.objs[PT_SLOT];
let ipcbuffer_frame = &self.dynamic_objs.objs[IPCBUFFER_SLOT];
let sdk_frame = &self.dynamic_objs.objs[SDK_FRAME_SLOT];
let stack_frames = &self.dynamic_objs.objs[STACK_SLOT];
// Initializes the VSpace root (PD) in the ASID pool.
@ -470,8 +473,6 @@ impl seL4BundleImpl {
// Setup the stack & IPC buffer.
// NB: no need for actual guard pages, just leave 'em unmapped.
// XXX but this would give a different fault than a write to a read-only
// page, need to make sure this works
let mut vaddr = roundup(vaddr_top, PAGE_SIZE);
trace!("guard page vaddr 0x{:x}", vaddr);
vaddr += PAGE_SIZE; // Guard page below stack
@ -495,9 +496,22 @@ impl seL4BundleImpl {
"map ipcbuffer slot {} vaddr 0x{:x} {:?}",
ipcbuffer_frame.cptr,
vaddr,
rights_rwn
rights_rwn,
);
arch::map_page(ipcbuffer_frame, pd, vaddr, rights_rwn, vm_attribs)
arch::map_page(ipcbuffer_frame, pd, vaddr, rights_rwn, vm_attribs)?;
vaddr += ipcbuffer_frame.size_bytes().unwrap();
// Map SDK RPC frame.
self.sdk_frame_addr = vaddr;
trace!(
"map sdk_runtime slot {} vaddr 0x{:x} {:?}",
sdk_frame.cptr,
vaddr,
rights_rwn,
);
arch::map_page(sdk_frame, pd, vaddr, rights_rwn, vm_attribs)?;
Ok(())
}
// Sets up the TCB and related state (e.g. scheduler context).
@ -558,8 +572,12 @@ impl seL4BundleImpl {
let mut sp = self.tcb_sp;
assert_eq!(sp % arch::STACK_ALIGNMENT_BYTES, 0, "TCB stack pointer mis-aligned");
// XXX nonsense values for testing
let argv: &[seL4_Word] = &[self.tcb_ipcbuffer_addr, 0x11112222, 0x22223333, 0x44445555];
let argv: &[seL4_Word] = &[
self.tcb_ipcbuffer_addr, // Used to setup __sel4_ipc_buffer
self.sdk_ep_slot, // For SDKRuntime IPCs
SDK_FRAME_SLOT, // NB: must wrt application CSpace
self.sdk_frame_addr, // For SDKRuntime parameters
];
// NB: tcb_args::maybe_spill_tcb_args may write arg data to the
// stack causing the stack pointer to be adjusted.
@ -585,14 +603,23 @@ impl seL4BundleImpl {
// Do the final work to construct the application's CSpace.
fn init_cspace(&mut self) -> seL4_Result {
// Move everything back from the top-level CNode to the
// application's cspace_root and release the top-level
// CNode slots used during construction.
// XXX should we remove the TCB from the CNode?
// XXX verify no self-ref to the top-level CNode (so
// frames etc cannot be modified)
// Install a badged SDK endpoint in the slot reserved for it.
let sdk_endpoint = CSpaceSlot::new();
kata_sdk_manager_get_endpoint(&self.tcb_name, &sdk_endpoint)
.map_err(|_| seL4_Error::seL4_NoError)?; // XXX error
sdk_endpoint.move_from(
self.cspace_root.objs[0].cptr,
self.sdk_ep_slot,
self.cspace_root_depth,
)?;
// Move everything back from the top-level CNode to the application's
// cspace_root and release the top-level CNode slots used during
// construction. Note this does not clobber the sdk_endpoint because
// that slot is carefully avoidded in dynamic_objs.
self.dynamic_objs
.move_objects_from_toplevel(self.cspace_root.objs[0].cptr, self.cspace_root_depth)?;
// Keep a dup of the TCB in the top-level CNode for suspend/resume.
// We do this after the bulk move to insure there's a free slot.
self.cap_tcb.dup_to(
@ -600,6 +627,7 @@ impl seL4BundleImpl {
self.dynamic_objs.objs[TCB_SLOT].cptr,
self.dynamic_objs.depth,
)?;
// TODO(sleffler): remove the TCB from the CNode
Ok(())
}
@ -628,6 +656,8 @@ impl BundleImplInterface for seL4BundleImpl {
}
fn stop(&mut self) -> Result<(), ProcessManagerError> {
self.suspend()?;
kata_sdk_manager_release_endpoint(&self.tcb_name)
.map_err(|_| ProcessManagerError::StopFailed)?;
kata_object_free_in_cnode(&self.bundle_frames)
.map_err(|_| ProcessManagerError::StopFailed)?;
kata_object_free_in_cnode(&self.dynamic_objs)

View File

@ -17,6 +17,7 @@
members = [
"kata-sdk-component",
"kata-sdk-interface",
"kata-sdk-manager",
"kata-sdk-runtime",
]
resolver = "2"

View File

@ -15,13 +15,22 @@
// KataOS SDKRuntime services.
import <LoggerInterface.camkes>;
import <SDKRuntimeInterface.camkes>;
import <MemoryInterface.camkes>;
import <SDKManagerInterface.camkes>;
component SDKRuntime {
provides SDKRuntimeInterface sdk_runtime;
provides SDKManagerInterface sdk_manager;
control; // NB: SDKRuntimeInterface
maybe uses LoggerInterface logger;
uses MemoryInterface memory;
// Enable KataOS CAmkES support.
attribute int kataos = true;
// Add free slots for minting endpoints.
attribute int cnode_headroom = 8;
// Copyregion for mapping application request data.
has copyregion SDK_PARAMS;
}

View File

@ -16,14 +16,24 @@
name = "kata-sdk-component"
version = "0.1.0"
edition = "2021"
build = "build.rs"
[build-dependencies]
sel4-config = { path = "../../kata-os-common/src/sel4-config" }
[features]
CONFIG_KERNEL_MCS = []
[dependencies]
cstr_core = { version = "0.2.3", default-features = false }
kata-os-common = { path = "../../kata-os-common" }
kata-memory-interface = { path = "../../MemoryManager/kata-memory-interface" }
kata-sdk-interface = { path = "../kata-sdk-interface" }
kata-sdk-manager = { path = "../kata-sdk-manager" }
kata-sdk-runtime = { path = "../kata-sdk-runtime" }
log = { version = "0.4", features = ["release_max_level_info"] }
postcard = { version = "0.7", features = ["alloc"], default-features = false }
static_assertions = "1.1"
[lib]
name = "kata_sdk_runtime"

View File

@ -0,0 +1,33 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::env;
fn main() {
// If SEL4_OUT_DIR is not set we expect the kernel build at a fixed
// location relative to the ROOTDIR env variable.
println!("SEL4_OUT_DIR {:?}", env::var("SEL4_OUT_DIR"));
let sel4_out_dir = env::var("SEL4_OUT_DIR")
.unwrap_or_else(|_| format!("{}/out/kata/kernel", env::var("ROOTDIR").unwrap()));
println!("sel4_out_dir {}", sel4_out_dir);
// Dredge seL4 kernel config for settings we need as features to generate
// correct code: e.g. CONFIG_KERNEL_MCS enables MCS support which changes
// the system call numbering.
let features = sel4_config::get_sel4_features(&sel4_out_dir);
println!("features={:?}", features);
for feature in features {
println!("cargo:rustc-cfg=feature=\"{}\"", feature);
}
}

View File

@ -25,13 +25,58 @@
#![no_std]
#![allow(clippy::missing_safety_doc)]
use static_assertions::assert_cfg;
// NB: the RPC implementation uses MCS syscalls
assert_cfg!(feature = "CONFIG_KERNEL_MCS");
extern crate alloc;
use kata_os_common::camkes::Camkes;
use kata_sdk_interface::SDKRuntimeError;
use alloc::vec;
use core::mem::size_of;
use core::ptr;
use cstr_core::CStr;
use kata_memory_interface::kata_object_alloc_in_toplevel;
use kata_memory_interface::ObjDesc;
use kata_os_common::camkes::{seL4_CPath, Camkes};
use kata_os_common::copyregion::CopyRegion;
use kata_os_common::cspace_slot::CSpaceSlot;
use kata_os_common::sel4_sys;
use kata_sdk_interface::SDKAppId;
use kata_sdk_interface::SDKError;
use kata_sdk_interface::SDKReplyHeader;
use kata_sdk_interface::SDKRuntimeInterface;
use kata_sdk_runtime::KATA_SDK;
use kata_sdk_interface::SDKRuntimeRequest;
use kata_sdk_interface::SDKRUNTIME_REQUEST_DATA_SIZE;
use kata_sdk_manager::SDKManagerError;
use kata_sdk_manager::SDKManagerInterface;
use kata_sdk_runtime::KataSDKRuntime;
use log::error;
use sel4_sys::seL4_CNode_Delete;
use sel4_sys::seL4_CPtr;
use sel4_sys::seL4_CapRights;
use sel4_sys::seL4_EndpointObject;
use sel4_sys::seL4_MessageInfo;
use sel4_sys::seL4_PageBits;
use sel4_sys::seL4_Recv;
use sel4_sys::seL4_ReplyObject;
use sel4_sys::seL4_ReplyRecv;
use sel4_sys::seL4_Result;
use sel4_sys::seL4_Word;
const PAGE_SIZE: usize = 1 << seL4_PageBits;
extern "C" {
static mut SDK_PARAMS: [seL4_Word; PAGE_SIZE / size_of::<seL4_Word>()];
}
static mut CAMKES: Camkes = Camkes::new("SDKRuntime");
// NB: KATA_SDK cannot be used before setup is completed with a call to init()
static mut KATA_SDK: KataSDKRuntime = KataSDKRuntime::empty();
// Server RPC plumbing.
static mut KATA_SDK_ENDPOINT: seL4_CPtr = 0;
static mut KATA_SDK_REPLY: seL4_CPtr = 0;
static mut KATA_SDK_RECV_SLOT: seL4_CPtr = 0;
/// CAmkES component pre-init method.
///
@ -40,12 +85,184 @@ static mut CAMKES: Camkes = Camkes::new("SDKRuntime");
pub unsafe extern "C" fn pre_init() {
static mut HEAP_MEMORY: [u8; 8 * 1024] = [0; 8 * 1024];
CAMKES.pre_init(log::LevelFilter::Trace, &mut HEAP_MEMORY);
// Setup the SDKRuntime service from scratch (no CAmkES help).
let bundle = kata_object_alloc_in_toplevel(vec![
ObjDesc::new(seL4_EndpointObject, 1, 0),
ObjDesc::new(seL4_ReplyObject, 1, 1),
])
.expect("alloc");
// Create endpoint (R)
let endpoint = Camkes::top_level_path(bundle.objs[0].cptr);
let mut ep_slot = CSpaceSlot::new();
ep_slot
.copy_to(
endpoint.0,
endpoint.1,
endpoint.2 as u8,
seL4_CapRights::new(
/*grant_reply=*/ 0, /*grant=*/ 0, /*read=*/ 1, /*write=*/ 0,
),
)
.expect("endpoint");
KATA_SDK_ENDPOINT = ep_slot.release();
// Create reply (WG).
let reply = Camkes::top_level_path(bundle.objs[1].cptr);
let mut reply_slot = CSpaceSlot::new();
reply_slot
.copy_to(
reply.0,
reply.1,
reply.2 as u8,
seL4_CapRights::new(
/*grant_reply=*/ 0, /*grant=*/ 1, // XXX not sending back caps
/*read=*/ 0, /*write=*/ 1,
),
)
.expect("reply");
// NB: hold onto reply for now (only need/usee the WG copy)
KATA_SDK_REPLY = reply_slot.release();
// Receive slot for frames with RPC parameters.
KATA_SDK_RECV_SLOT = CSpaceSlot::new().release();
// NB: SDKRuntime needs the original (unbadged) cap to mint badged
// caps with WGP rights for applications (returned by get_endpoint).
KATA_SDK.init(&endpoint);
}
/// CAmkES sdk_ping method.
///
/// See also the component interface definition called
/// `SDKRuntimeInterface.camkes` outside of this crate. Since this is a C
/// function, we must use the C enum for error codes.
fn delete_path(path: &seL4_CPath) -> seL4_Result {
unsafe { seL4_CNode_Delete(path.0, path.1, path.2 as u8) }
}
fn reply_error(error: SDKError, reply_slice: &mut [u8]) {
// XXX check return
let _ = postcard::to_slice(
&SDKReplyHeader {
status: error.into(),
},
reply_slice,
);
}
/// Server-side of SDKRuntime request processing. Note CAmkES does not
/// participate in the RPC processing we use the control thread instead
/// of having CAmkES create an interface thread and pass parameters through
/// a page frame attached to the IPC buffer.
#[no_mangle]
pub unsafe extern "C" fn sdk_runtime_sdk_ping() -> SDKRuntimeError { KATA_SDK.ping().into() }
pub unsafe extern "C" fn run() -> ! {
let recv_path = &Camkes::top_level_path(KATA_SDK_RECV_SLOT);
CAMKES.init_recv_path(recv_path);
Camkes::debug_assert_slot_empty("run", recv_path);
let mut copy_region = CopyRegion::new(ptr::addr_of_mut!(SDK_PARAMS[0]), PAGE_SIZE);
// Do initial Recv; after this we use ReplyRecv to minimize syscalls.
let mut sdk_runtime_badge: seL4_Word = 0;
seL4_Recv(KATA_SDK_ENDPOINT, &mut sdk_runtime_badge as _, KATA_SDK_REPLY);
loop {
Camkes::debug_assert_slot_frame("run", recv_path);
// seL4_Recv & seL4_ReplyRecv return any badge but do not reset
// the ipcbuffer state. If the ipcbuffer is turned around for a
// send operation the received badge may be interpreted as an
// outbound capability. To guard against this clear the field here
// (so it happens for both calls) with clear_request_cap().
Camkes::clear_request_cap();
// Map the frame with RPC parameters and decode the request header.
if copy_region.map(recv_path.1).is_ok() {
// The client serializes an SDKRequestHeader first with the
// request id. This is followed by request-specific arguments
// that must be processed by each handler.
let (request_slice, reply_slice) = copy_region
.as_mut()
.split_at_mut(SDKRUNTIME_REQUEST_DATA_SIZE);
let request_slice = &*request_slice; // NB: immutable alias
match postcard::take_from_bytes::<kata_sdk_interface::SDKRequestHeader>(request_slice) {
Ok((header, args_slice)) => {
let app_id = sdk_runtime_badge as SDKAppId; // XXX safe?
if let Err(status) = match header.request {
SDKRuntimeRequest::Ping => ping_request(app_id, args_slice, reply_slice),
SDKRuntimeRequest::Log => log_request(app_id, args_slice, reply_slice),
} {
reply_error(status, reply_slice);
}
}
Err(err) => reply_error(deserialize_failure(err), reply_slice),
}
copy_region.unmap().expect("unmap");
} else {
error!("Unable to map RPC parameters; badge {}", sdk_runtime_badge);
// TODO(jtgans): no way to return an error; signal ProcessManager to stop app?
}
delete_path(recv_path).expect("delete");
Camkes::debug_assert_slot_empty("run", recv_path);
let info = seL4_MessageInfo::new(0, 0, 0, /*length=*/ 0);
seL4_ReplyRecv(KATA_SDK_ENDPOINT, info, &mut sdk_runtime_badge as _, KATA_SDK_REPLY);
}
}
// SDK RPC request handling: unmarshal request, dispatch to KATA_SDK,
// and marshal reply.
fn deserialize_failure(e: postcard::Error) -> SDKError {
error!("deserialize failed: {:?}", e);
SDKError::DeserializeFailed
}
fn ping_request(
app_id: SDKAppId,
_request_slice: &[u8],
_reply_slice: &mut [u8],
) -> Result<(), SDKError> {
unsafe { KATA_SDK.ping(app_id) }
}
fn log_request(
app_id: SDKAppId,
request_slice: &[u8],
_reply_slice: &mut [u8],
) -> Result<(), SDKError> {
let request = postcard::from_bytes::<kata_sdk_interface::LogRequest>(request_slice)
.map_err(deserialize_failure)?;
let msg = core::str::from_utf8(request.msg).map_err(|_| SDKError::InvalidString)?;
unsafe { KATA_SDK.log(app_id, msg) }
}
// SDKManager RPC handling; these arrive via CAmkES so have a C linkage.
#[no_mangle]
pub unsafe extern "C" fn sdk_manager_get_endpoint(
c_app_id: *const cstr_core::c_char,
) -> SDKManagerError {
let ret_status = match CStr::from_ptr(c_app_id).to_str() {
Ok(app_id) => match KATA_SDK.get_endpoint(app_id) {
Ok(cap_endpoint) => {
Camkes::set_reply_cap_release(cap_endpoint);
SDKManagerError::SmSuccess
}
Err(e) => e,
},
Err(_) => SDKManagerError::SmAppIdInvalid,
};
ret_status
}
#[no_mangle]
pub unsafe extern "C" fn sdk_manager_release_endpoint(
c_app_id: *const cstr_core::c_char,
) -> SDKManagerError {
let ret_status = match CStr::from_ptr(c_app_id).to_str() {
Ok(app_id) => match KATA_SDK.release_endpoint(app_id) {
Ok(_) => SDKManagerError::SmSuccess,
Err(e) => e,
},
Err(_) => SDKManagerError::SmAppIdInvalid,
};
ret_status
}
#[no_mangle]
pub unsafe extern "C" fn sdk_manager_capscan() { let _ = Camkes::capscan(); }

View File

@ -18,5 +18,6 @@ version = "0.1.0"
edition = "2021"
[dependencies]
cstr_core = "0.2.3"
postcard = { version = "0.7", features = ["alloc"], default-features = false }
sel4-sys = { path = "../../kata-os-common/src/sel4-sys", default-features = false }
serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] }

View File

@ -12,11 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use serde::{Deserialize, Serialize};
/// Rust Error enum used for representing an SDK error with postcard. This is
/// what most rust components will actually use as their error handling enum.
#[derive(Debug, Eq, PartialEq)]
pub enum SDKError {
DeserializeFailed,
SerializeFailed,
InvalidBadge,
InvalidString,
ReadKeyFailed,
WriteKeyFailed,
DeleteKeyFailed,
}
impl From<postcard::Error> for SDKError {
@ -25,17 +33,29 @@ impl From<postcard::Error> for SDKError {
/// C-version of SDKError presented over the CAmkES rpc interface.
#[repr(C)]
#[derive(Debug, Eq, PartialEq)]
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum SDKRuntimeError {
SDKSuccess = 0,
SDKDeserializeFailed,
SDKSerializeFailed,
SDKInvalidBadge,
SDKInvalidString,
SDKReadKeyFailed,
SDKWriteKeyFailed,
SDKDeleteKeyFailed,
}
/// Mapping function from Rust -> C.
impl From<SDKError> for SDKRuntimeError {
fn from(err: SDKError) -> SDKRuntimeError {
match err {
SDKError::DeserializeFailed => SDKRuntimeError::SDKDeserializeFailed,
SDKError::SerializeFailed => SDKRuntimeError::SDKSerializeFailed,
SDKError::InvalidBadge => SDKRuntimeError::SDKInvalidBadge,
SDKError::InvalidString => SDKRuntimeError::SDKInvalidString,
SDKError::ReadKeyFailed => SDKRuntimeError::SDKReadKeyFailed,
SDKError::WriteKeyFailed => SDKRuntimeError::SDKWriteKeyFailed,
SDKError::DeleteKeyFailed => SDKRuntimeError::SDKDeleteKeyFailed,
}
}
}
@ -52,7 +72,13 @@ impl From<SDKRuntimeError> for Result<(), SDKError> {
fn from(err: SDKRuntimeError) -> Result<(), SDKError> {
match err {
SDKRuntimeError::SDKSuccess => Ok(()),
SDKRuntimeError::SDKDeserializeFailed => Err(SDKError::DeserializeFailed),
SDKRuntimeError::SDKSerializeFailed => Err(SDKError::SerializeFailed),
SDKRuntimeError::SDKInvalidBadge => Err(SDKError::InvalidBadge),
SDKRuntimeError::SDKInvalidString => Err(SDKError::InvalidString),
SDKRuntimeError::SDKReadKeyFailed => Err(SDKError::ReadKeyFailed),
SDKRuntimeError::SDKWriteKeyFailed => Err(SDKError::WriteKeyFailed),
SDKRuntimeError::SDKDeleteKeyFailed => Err(SDKError::DeleteKeyFailed),
}
}
}

View File

@ -12,7 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//! KataOS SDK runtime interfaces
//! KataOS SDK application runtime interfaces.
//! These can also be used from KataOS services (for testing) by first
//! setting up the KATA_SDK_* data (e.g. using kata_sdk_manager_get_endpoint)
#![cfg_attr(not(test), no_std)]
@ -21,6 +23,85 @@ pub mod error;
pub use error::SDKError;
pub use error::SDKRuntimeError;
use serde::{Deserialize, Serialize};
use sel4_sys::seL4_CPtr;
use sel4_sys::seL4_Call;
use sel4_sys::seL4_MessageInfo;
use sel4_sys::seL4_PageBits;
use sel4_sys::seL4_SetCap;
const PAGE_SIZE: usize = 1 << seL4_PageBits;
// SDKRuntime client-side state setup by ProcessManager and crt0.
// TODO(sleffler): is 1 page enough? ProcessManager should probably have
// SDKRuntime handle this
extern "C" {
static KATA_SDK_ENDPOINT: seL4_CPtr; // IPC connection to SDKRuntime
static KATA_SDK_FRAME: seL4_CPtr; // RPC parameters frame
static KATA_SDK_PARAMS: *mut u8; // Virtual address of KATA_SDK_FRAME
}
// Size of the buffers used to pass serialized data. The data structure
// sizes are bounded by the single page (4K bytes) used to marshal & unmarshal
// parameters and also by their being allocated on the stack. We balance
// these against being able to handle large amounts of data.
// XXX do sensor frames need to be passed & are they too big?
// pub for server-side logic
pub const SDKRUNTIME_REQUEST_DATA_SIZE: usize = PAGE_SIZE / 2;
/// Application identity derived from seL4 Endpoint badge setup when
/// the application is started by ProcessManager.
///
/// NB: On 32-bit platforms the kernel truncates this to 28-bits;
/// on 64-bit platforms these are 64-bits.
pub type SDKAppId = usize;
/// All RPC request must have an SDKRequestHeader at the front.
#[derive(Serialize, Deserialize)]
pub struct SDKRequestHeader {
pub request: SDKRuntimeRequest,
}
impl SDKRequestHeader {
pub fn new(request: SDKRuntimeRequest) -> Self { Self { request } }
}
/// All RPC responses must have an SDKReplyHeader at the front.
#[derive(Serialize, Deserialize)]
pub struct SDKReplyHeader {
pub status: SDKRuntimeError,
}
impl SDKReplyHeader {
pub fn new(status: SDKRuntimeError) -> Self { Self { status } }
}
impl From<SDKReplyHeader> for Result<(), SDKRuntimeError> {
fn from(header: SDKReplyHeader) -> Result<(), SDKRuntimeError> {
if header.status == SDKRuntimeError::SDKSuccess {
Ok(())
} else {
Err(header.status)
}
}
}
/// SDKRuntimeRequest::Ping
#[derive(Serialize, Deserialize)]
pub struct PingRequest {}
/// SDKRuntimeRequest::Log
#[derive(Serialize, Deserialize)]
pub struct LogRequest<'a> {
pub msg: &'a [u8],
}
#[repr(C)] // XXX needed?
#[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)]
pub enum SDKRuntimeRequest {
Ping = 0, // Check runtime is alive
Log, // Log message: [msg: &str]
}
/// Rust interface for the SDKRuntime.
///
/// This trait defines all of the same verbs we expect to support in the component
@ -31,20 +112,86 @@ pub use error::SDKRuntimeError;
/// as a global mutable object where the incoming calls from the CAmkES C side
/// are wrapped.
///
/// On the client side, this trait is implemented using top-level functions,
/// wrapping their CAmkES C stubs.
/// On the client side, this trait is implemented using top-level functions.
pub trait SDKRuntimeInterface {
/// Pings the SDK runtime, going from client to server and back via CAmkES IPC.
fn ping(&self) -> Result<(), SDKError>;
fn ping(&self, app_id: SDKAppId) -> Result<(), SDKError>;
/// Logs |msg| through the system logger.
fn log(&self, app_id: SDKAppId, msg: &str) -> Result<(), SDKError>;
}
/// Rust client-side wrapper for the autogenerated CAmkES ping method.
#[inline]
#[allow(dead_code)]
pub fn kata_sdk_ping() -> Result<(), SDKError> {
extern "C" {
fn sdk_runtime_sdk_ping() -> SDKRuntimeError;
/// Rust client-side request processing. Note there is no CAmkES stub to
/// call; everything is done here. A single page frame is attached to the
/// IPC buffer with request parameters in the first half and return values
/// in the second half. Requests must have an SDKRequestHeader serialized
/// separately from any arguments. Responses must have an SDKReplyHeader
/// included in the reply data. For the moment this uses postcard to do
/// serde work; this may change in the future (e.g. to flatbuffers).
///
/// The caller is responsible for synchronizing access to KATA_SDK_* state
/// and the IPC buffer.
//
// TODO(sleffler): this attaches the call params to the IPC; might be
// better to keep the page(s) mapped in SDKRuntime to avoid map/unmap
// per-RPC but that requires a vspace allocator (or somerthing special
// purpose) and a redesign of the server side to use the endpoint badge
// to lookup the mapped page early. Downside to a fixed mapping is it
// limits how to handle requests w/ different-sized params (e.g. sensor
// frame vs key-value params).
// TODO(sleffler): could send request header and reponse statatus inline.
// This would align request arguments to the page boundary which might
// be useful and having the reply inline would mean SDKRuntime could
// send a meaningful error back when unable to map the page frame.
fn kata_sdk_request<'a, S: Serialize, D: Deserialize<'a>>(
request: SDKRuntimeRequest,
request_args: &S,
) -> Result<D, SDKRuntimeError> {
let params_slice = unsafe { core::slice::from_raw_parts_mut(KATA_SDK_PARAMS, PAGE_SIZE) };
// NB: server-side must do the same split
let (request_slice, reply_slice) = params_slice.split_at_mut(SDKRUNTIME_REQUEST_DATA_SIZE);
reply_slice.fill(0); // XXX paranoid, could zero-pad request too
// Encode heeader with request.
// TODO(sleffler): eliminate struct? (could add a sequence #)
let header_size = (postcard::to_slice(&SDKRequestHeader::new(request), request_slice)
.map_err(|_| SDKRuntimeError::SDKSerializeFailed)?)
.len();
// Encode arguments immediately after.
let (_, args_slice) = request_slice.split_at_mut(header_size);
let _ = postcard::to_slice(request_args, args_slice)
.map_err(|_| SDKRuntimeError::SDKSerializeFailed)?;
// Attach params & call the SDKRuntime; then wait (block) for a reply.
unsafe {
seL4_SetCap(0, KATA_SDK_FRAME);
seL4_Call(KATA_SDK_ENDPOINT, seL4_MessageInfo::new(0, 0, 1, 0));
seL4_SetCap(0, 0);
}
unsafe { sdk_runtime_sdk_ping().into() }
postcard::from_bytes::<D>(reply_slice).map_err(|_| SDKRuntimeError::SDKDeserializeFailed)
}
/// Rust client-side wrapper for the ping method.
#[inline]
#[allow(dead_code)]
pub fn kata_sdk_ping() -> Result<(), SDKRuntimeError> {
let header =
kata_sdk_request::<PingRequest, SDKReplyHeader>(SDKRuntimeRequest::Ping, &PingRequest {})?;
header.into()
}
/// Rust client-side wrapper for the log method.
#[inline]
#[allow(dead_code)]
pub fn kata_sdk_log(msg: &str) -> Result<(), SDKRuntimeError> {
let header = kata_sdk_request::<LogRequest, SDKReplyHeader>(
SDKRuntimeRequest::Log,
&LogRequest {
msg: msg.as_bytes(),
},
)?;
header.into()
}

View File

@ -0,0 +1,23 @@
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
[package]
name = "kata-sdk-manager"
version = "0.1.0"
edition = "2021"
[dependencies]
cstr_core = "0.2.3"
kata-os-common = { path = "../../kata-os-common" }
postcard = { version = "0.7", default-features = false }

View File

@ -14,5 +14,5 @@
INTERFACES=${OUT}/kata/components
${INTERFACES}/SDKRuntimeInterfaceBindings.h: src/lib.rs cbindgen.toml
${INTERFACES}/SDKManagerInterfaceBindings.h: src/lib.rs cbindgen.toml
cbindgen -c cbindgen.toml src/lib.rs -o $@

View File

@ -20,5 +20,5 @@ includes = ["CamkesBindings.h"]
[export]
include = [
"SDKRuntimeError",
"SDKManagerError",
]

View File

@ -0,0 +1,99 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! KataOS SDK manager interfaces
#![cfg_attr(not(test), no_std)]
use cstr_core::CString;
use kata_os_common::cspace_slot::CSpaceSlot;
use kata_os_common::sel4_sys;
use sel4_sys::seL4_CPtr;
#[repr(C)]
#[derive(Eq, PartialEq)]
pub enum SDKManagerError {
SmSuccess = 0,
SmSerializeFailed,
SmAppIdInvalid,
SmGetEndpointFailed,
SmReleaseEndpointFailed,
}
impl From<SDKManagerError> for Result<(), SDKManagerError> {
fn from(err: SDKManagerError) -> Result<(), SDKManagerError> {
if err == SDKManagerError::SmSuccess {
Ok(())
} else {
Err(err)
}
}
}
/// Rust manager interface for the SDKRuntime.
pub trait SDKManagerInterface {
/// Returns a badged endpoint capability setup for making
/// SDKRuntime requests. The endpoint is meant to be returned
/// to the caller attached to the IPC buffer. SDKRuntime requests
/// are rejected unless they arrive through a properly-badged endpoint.
fn get_endpoint(&mut self, app_id: &str) -> Result<seL4_CPtr, SDKManagerError>;
/// Remove an application badge setup with get_endpoint.
fn release_endpoint(&mut self, app_id: &str) -> Result<(), SDKManagerError>;
}
#[inline]
#[allow(dead_code)]
pub fn kata_sdk_manager_get_endpoint(
app_id: &str,
container_slot: &CSpaceSlot,
) -> Result<(), SDKManagerError> {
container_slot.set_recv_path();
// NB: make sure the receive slot is empty or the cap will be dropped.
sel4_sys::debug_assert_slot_empty!(
container_slot.slot,
"Expected slot {:?} empty but has cap type {:?}",
&container_slot.get_path(),
sel4_sys::cap_identify(container_slot.slot)
);
extern "C" {
pub fn sdk_manager_get_endpoint(c_bundle_id: *const cstr_core::c_char) -> SDKManagerError;
}
let cstr = CString::new(app_id).map_err(|_| SDKManagerError::SmSerializeFailed)?;
unsafe { sdk_manager_get_endpoint(cstr.as_ptr()) }.into()
}
#[inline]
#[allow(dead_code)]
pub fn kata_sdk_manager_release_endpoint(app_id: &str) -> Result<(), SDKManagerError> {
extern "C" {
pub fn sdk_manager_release_endpoint(
c_bundle_id: *const cstr_core::c_char,
) -> SDKManagerError;
}
let cstr = CString::new(app_id).map_err(|_| SDKManagerError::SmSerializeFailed)?;
unsafe { sdk_manager_release_endpoint(cstr.as_ptr()) }.into()
}
#[inline]
#[allow(dead_code)]
pub fn kata_sdk_manager_capscan() -> Result<(), SDKManagerError> {
extern "C" {
pub fn sdk_manager_capscan();
}
unsafe { sdk_manager_capscan() }
Ok(())
}

View File

@ -18,5 +18,10 @@ version = "0.1.0"
edition = "2021"
[dependencies]
hashbrown = { version = "0.11", features = ["ahash-compile-time-rng"] }
kata-os-common = { path = "../../kata-os-common" }
kata-sdk-interface = { path = "../kata-sdk-interface" }
kata-sdk-manager = { path = "../kata-sdk-manager" }
log = { version = "0.4", features = ["release_max_level_info"] }
smallstr = "0.2"
spin = "0.9"

View File

@ -13,23 +13,60 @@
// limitations under the License.
#![cfg_attr(not(test), no_std)]
#![feature(build_hasher_simple_hash_one)]
use kata_os_common::camkes::seL4_CPath;
use kata_os_common::sel4_sys;
use kata_sdk_interface::error::SDKError;
use kata_sdk_interface::SDKAppId;
use kata_sdk_interface::SDKRuntimeInterface;
use log::trace;
use kata_sdk_manager::SDKManagerError;
use kata_sdk_manager::SDKManagerInterface;
use spin::Mutex;
#[cfg(not(test))]
pub static mut KATA_SDK: KataSDKRuntime = KataSDKRuntime {};
use sel4_sys::seL4_CPtr;
/// Kata OS SDK support for third-party applications, Rust core.
///
/// This is the actual Rust implementation of the SDK runtime component. Here's
/// where we can encapsulate all of our Rust fanciness, away from the C
/// bindings. This is the server-side implementation.
pub struct KataSDKRuntime;
impl SDKRuntimeInterface for KataSDKRuntime {
fn ping(&self) -> Result<(), SDKError> {
trace!("ping!");
Ok(())
mod runtime;
use runtime::SDKRuntime;
/// Wrapper around SDKRuntime implementation. Because we have two CAmkES
/// interfaces there may be concurrent calls so we lock at this level.
pub struct KataSDKRuntime {
runtime: Mutex<Option<SDKRuntime>>,
}
impl KataSDKRuntime {
// Constructs a partially-initialized instance; to complete call init().
// This is needed because we need a const fn for static setup.
pub const fn empty() -> KataSDKRuntime {
KataSDKRuntime {
runtime: Mutex::new(None),
}
}
// Finishes the setup started by empty():
pub fn init(&self, endpoint: &seL4_CPath) {
*self.runtime.lock() = Some(SDKRuntime::new(endpoint));
}
// Returns the bundle capacity.
pub fn capacity(&self) -> usize { self.runtime.lock().as_ref().unwrap().capacity() }
}
// These just lock accesses and handle the necessary indirection.
impl SDKManagerInterface for KataSDKRuntime {
fn get_endpoint(&mut self, app_id: &str) -> Result<seL4_CPtr, SDKManagerError> {
self.runtime.lock().as_mut().unwrap().get_endpoint(app_id)
}
fn release_endpoint(&mut self, app_id: &str) -> Result<(), SDKManagerError> {
self.runtime
.lock()
.as_mut()
.unwrap()
.release_endpoint(app_id)
}
}
impl SDKRuntimeInterface for KataSDKRuntime {
fn ping(&self, app_id: SDKAppId) -> Result<(), SDKError> {
self.runtime.lock().as_ref().unwrap().ping(app_id)
}
fn log(&self, app_id: SDKAppId, msg: &str) -> Result<(), SDKError> {
self.runtime.lock().as_ref().unwrap().log(app_id, msg)
}
}

View File

@ -0,0 +1,150 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use core::hash::BuildHasher;
use hashbrown::HashMap;
use kata_os_common::camkes::seL4_CPath;
use kata_os_common::cspace_slot::CSpaceSlot;
use kata_os_common::sel4_sys;
use kata_sdk_interface::error::SDKError;
use kata_sdk_interface::SDKAppId;
use kata_sdk_interface::SDKRuntimeInterface;
use kata_sdk_manager::SDKManagerError;
use kata_sdk_manager::SDKManagerInterface;
use log::{error, info};
use smallstr::SmallString;
use sel4_sys::seL4_CPtr;
use sel4_sys::seL4_CapRights;
// App capacity before spillover to the heap; should be the max concurrent
// started apps. Set very small because we expect, at least initially, that
// only one app at a time will be started.
const DEFAULT_APP_CAPACITY: usize = 3;
// BundleId capacity before spillover to the heap.
// TODO(sleffler): hide this; it's part of the implementation
// TODO(sleffler): shared with kata-proc-interface
const DEFAULT_BUNDLE_ID_CAPACITY: usize = 64;
type SmallId = SmallString<[u8; DEFAULT_BUNDLE_ID_CAPACITY]>;
struct SDKRuntimeState {
id: SmallId,
}
impl SDKRuntimeState {
pub fn new(app_id: &str) -> Self {
Self {
id: SmallId::from_str(app_id),
}
}
}
/// Kata OS SDK support for third-party applications, Rust core.
///
/// This is the actual Rust implementation of the SDK runtime component. Here's
/// where we can encapsulate all of our Rust fanciness, away from the C
/// bindings. This is the server-side implementation.
// XXX hashmap may be overkill, could use SmallVec and badge by index
pub struct SDKRuntime {
endpoint: seL4_CPath,
apps: HashMap<SDKAppId, SDKRuntimeState>,
}
impl SDKRuntime {
pub fn new(endpoint: &seL4_CPath) -> Self {
Self {
endpoint: *endpoint,
apps: HashMap::with_capacity(DEFAULT_APP_CAPACITY),
}
}
// Calculates the badge assigned to the seL4 endpoint the client will use
// to send requests to the SDKRuntime. This must be unique among active
// clients but may be reused. There is no need to randomize or otherwise
// secure this value since clients cannot forge an endpoint.
// TODO(sleffler): is it worth doing a hash? counter is probably sufficient
#[cfg(target_pointer_width = "32")]
fn calculate_badge(&self, id: &SmallId) -> SDKAppId {
(self.apps.hasher().hash_one(id) & 0x0ffffff) as SDKAppId
}
#[cfg(target_pointer_width = "64")]
fn calculate_badge(&self, id: &SmallId) -> SDKAppId {
self.apps.hasher().hash_one(id) as SDKAppId
}
pub fn capacity(&self) -> usize { self.apps.capacity() }
}
impl SDKManagerInterface for SDKRuntime {
/// Returns an seL4 Endpoint capability for |app_id| to make SDKRuntime
/// requests..Without a registered endpoint all requests will fail.
/// first calling kata_sdk_manager_get_endpoint().
fn get_endpoint(&mut self, app_id: &str) -> Result<seL4_CPtr, SDKManagerError> {
let badge = self.calculate_badge(&SmallId::from_str(app_id));
// Mint a badged endpoint for the client to talk to us.
let mut slot = CSpaceSlot::new();
slot.mint_to(
self.endpoint.0,
self.endpoint.1,
self.endpoint.2 as u8,
seL4_CapRights::new(
/*grant_reply=*/ 1,
/*grant=*/ 1, // NB: to send frame with RPC params
/*read=*/ 0, /*write=*/ 1,
),
badge,
)
.map_err(|_| SDKManagerError::SmGetEndpointFailed)?;
// Create the entry & return the endpoint capability.
assert!(self
.apps
.insert(badge, SDKRuntimeState::new(app_id))
.is_none());
Ok(slot.release())
}
/// Releases |app_id| state. No future requests may be made without
/// first calling kata_sdk_manager_get_endpoint().
fn release_endpoint(&mut self, app_id: &str) -> Result<(), SDKManagerError> {
let badge = self.calculate_badge(&SmallId::from_str(app_id));
let _ = self.apps.remove(&badge);
Ok(())
}
}
impl SDKRuntimeInterface for SDKRuntime {
/// Pings the SDK runtime, going from client to server and back via CAmkES IPC.
fn ping(&self, app_id: SDKAppId) -> Result<(), SDKError> {
match self.apps.get(&app_id) {
Some(_) => Ok(()),
None => {
// XXX potential console spammage/DOS
error!("No entry for app_id {:x}", app_id);
Err(SDKError::InvalidBadge)
}
}
}
/// Logs |msg| through the system logger.
fn log(&self, app_id: SDKAppId, msg: &str) -> Result<(), SDKError> {
match self.apps.get(&app_id) {
Some(app) => {
info!("[{}] {}", app.id, msg);
Ok(())
}
None => Err(SDKError::InvalidBadge),
}
}
}

View File

@ -12,8 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
procedure SDKRuntimeInterface {
include <SDKRuntimeInterfaceBindings.h>;
procedure SDKManagerInterface {
include <SDKManagerInterfaceBindings.h>;
SDKRuntimeError sdk_ping();
void capscan();
SDKManagerError get_endpoint(in string bundle_id);
SDKManagerError release_endpoint(in string bundle_id);
};

View File

@ -141,9 +141,13 @@ assembly {
connection seL4RPCCall shell_ml(from debug_console.mlcoord,
to ml_coordinator.mlcoord);
// Hookup SDKRuntime to DebugConsole for shell commands.
connection seL4RPCCall sdk_ping(from debug_console.sdk_runtime,
to sdk_runtime.sdk_runtime);
// ProcessMaanager talks to the SDKManager (the privileged part of
// the SDKRuntime) to plumb a badged connection between applications
// and the SDKRuntime.
connection seL4RPCCall multi_sdk_manager(
from process_manager.sdk_manager,
from debug_console.sdk_manager, // NB: for capscan support
to sdk_runtime.sdk_manager);
// Note this allocates a 4KB shared memory region for pkg install
// to pass an ObjDescArray
@ -158,6 +162,7 @@ assembly {
from debug_console.memory,
from process_manager.memory,
from security_coordinator.memory,
from sdk_runtime.memory,
from ml_coordinator.memory,
to memory_manager.memory);