diff --git a/apps/system/components/DebugConsole/DebugConsole.camkes b/apps/system/components/DebugConsole/DebugConsole.camkes index 4978eee..4435a78 100644 --- a/apps/system/components/DebugConsole/DebugConsole.camkes +++ b/apps/system/components/DebugConsole/DebugConsole.camkes @@ -19,7 +19,7 @@ import ; import ; import ; import ; -import ; +import ; 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; diff --git a/apps/system/components/DebugConsole/kata-shell/Cargo.toml b/apps/system/components/DebugConsole/kata-shell/Cargo.toml index ff24e50..49208fe 100644 --- a/apps/system/components/DebugConsole/kata-shell/Cargo.toml +++ b/apps/system/components/DebugConsole/kata-shell/Cargo.toml @@ -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" } diff --git a/apps/system/components/DebugConsole/kata-shell/src/lib.rs b/apps/system/components/DebugConsole/kata-shell/src/lib.rs index adbad73..f66bbbf 100644 --- a/apps/system/components/DebugConsole/kata-shell/src/lib.rs +++ b/apps/system/components/DebugConsole/kata-shell/src/lib.rs @@ -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(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")?; } diff --git a/apps/system/components/DebugConsole/kata-shell/src/test_sdk_runtime.rs b/apps/system/components/DebugConsole/kata-shell/src/test_sdk_runtime.rs deleted file mode 100644 index cf771d6..0000000 --- a/apps/system/components/DebugConsole/kata-shell/src/test_sdk_runtime.rs +++ /dev/null @@ -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, - _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(()) -} diff --git a/apps/system/components/ProcessManager/ProcessManager.camkes b/apps/system/components/ProcessManager/ProcessManager.camkes index 9663f91..fef4ea9 100644 --- a/apps/system/components/ProcessManager/ProcessManager.camkes +++ b/apps/system/components/ProcessManager/ProcessManager.camkes @@ -19,6 +19,7 @@ import ; import ; import ; import ; +import ; 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; diff --git a/apps/system/components/ProcessManager/kata-proc-component/src/run.rs b/apps/system/components/ProcessManager/kata-proc-component/src/run.rs index e144aed..06d83ea 100644 --- a/apps/system/components/ProcessManager/kata-proc-component/src/run.rs +++ b/apps/system/components/ProcessManager/kata-proc-component/src/run.rs @@ -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; diff --git a/apps/system/components/ProcessManager/kata-proc-interface/src/bundle_image.rs b/apps/system/components/ProcessManager/kata-proc-interface/src/bundle_image.rs index 1d2e0c2..725446b 100644 --- a/apps/system/components/ProcessManager/kata-proc-interface/src/bundle_image.rs +++ b/apps/system/components/ProcessManager/kata-proc-interface/src/bundle_image.rs @@ -190,9 +190,20 @@ 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) - .map_err(|_| BundleImageError::CapMoveFailed)?; + // 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; self.cur_frame = None; diff --git a/apps/system/components/ProcessManager/kata-proc-manager/Cargo.toml b/apps/system/components/ProcessManager/kata-proc-manager/Cargo.toml index 98db1f9..b228e03 100644 --- a/apps/system/components/ProcessManager/kata-proc-manager/Cargo.toml +++ b/apps/system/components/ProcessManager/kata-proc-manager/Cargo.toml @@ -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" diff --git a/apps/system/components/ProcessManager/kata-proc-manager/src/lib.rs b/apps/system/components/ProcessManager/kata-proc-manager/src/lib.rs index 9fed300..a62f1aa 100644 --- a/apps/system/components/ProcessManager/kata-proc-manager/src/lib.rs +++ b/apps/system/components/ProcessManager/kata-proc-manager/src/lib.rs @@ -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), } diff --git a/apps/system/components/ProcessManager/kata-proc-manager/src/sel4bundle/mod.rs b/apps/system/components/ProcessManager/kata-proc-manager/src/sel4bundle/mod.rs index 57c03a7..f3038ce 100644 --- a/apps/system/components/ProcessManager/kata-proc-manager/src/sel4bundle/mod.rs +++ b/apps/system/components/ProcessManager/kata-proc-manager/src/sel4bundle/mod.rs @@ -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,7 +193,9 @@ 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 - stack_base: seL4_Word, // Base address of stack 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, cspace_root_depth: u8, @@ -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) diff --git a/apps/system/components/SDKRuntime/Cargo.toml b/apps/system/components/SDKRuntime/Cargo.toml index 5a35672..db6a740 100644 --- a/apps/system/components/SDKRuntime/Cargo.toml +++ b/apps/system/components/SDKRuntime/Cargo.toml @@ -17,6 +17,7 @@ members = [ "kata-sdk-component", "kata-sdk-interface", + "kata-sdk-manager", "kata-sdk-runtime", ] resolver = "2" diff --git a/apps/system/components/SDKRuntime/SDKRuntime.camkes b/apps/system/components/SDKRuntime/SDKRuntime.camkes index 93df512..facd82f 100644 --- a/apps/system/components/SDKRuntime/SDKRuntime.camkes +++ b/apps/system/components/SDKRuntime/SDKRuntime.camkes @@ -15,13 +15,22 @@ // KataOS SDKRuntime services. import ; -import ; +import ; +import ; 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; } diff --git a/apps/system/components/SDKRuntime/kata-sdk-component/Cargo.toml b/apps/system/components/SDKRuntime/kata-sdk-component/Cargo.toml index 3ee329f..a76568d 100644 --- a/apps/system/components/SDKRuntime/kata-sdk-component/Cargo.toml +++ b/apps/system/components/SDKRuntime/kata-sdk-component/Cargo.toml @@ -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" diff --git a/apps/system/components/SDKRuntime/kata-sdk-component/build.rs b/apps/system/components/SDKRuntime/kata-sdk-component/build.rs new file mode 100644 index 0000000..2083524 --- /dev/null +++ b/apps/system/components/SDKRuntime/kata-sdk-component/build.rs @@ -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); + } +} diff --git a/apps/system/components/SDKRuntime/kata-sdk-component/src/run.rs b/apps/system/components/SDKRuntime/kata-sdk-component/src/run.rs index 210d94c..728a219 100644 --- a/apps/system/components/SDKRuntime/kata-sdk-component/src/run.rs +++ b/apps/system/components/SDKRuntime/kata-sdk-component/src/run.rs @@ -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::()]; +} 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::(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::(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(); } diff --git a/apps/system/components/SDKRuntime/kata-sdk-interface/Cargo.toml b/apps/system/components/SDKRuntime/kata-sdk-interface/Cargo.toml index ea7ee27..cb9293e 100644 --- a/apps/system/components/SDKRuntime/kata-sdk-interface/Cargo.toml +++ b/apps/system/components/SDKRuntime/kata-sdk-interface/Cargo.toml @@ -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"] } diff --git a/apps/system/components/SDKRuntime/kata-sdk-interface/src/error.rs b/apps/system/components/SDKRuntime/kata-sdk-interface/src/error.rs index b77e522..aff8f56 100644 --- a/apps/system/components/SDKRuntime/kata-sdk-interface/src/error.rs +++ b/apps/system/components/SDKRuntime/kata-sdk-interface/src/error.rs @@ -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 for SDKError { @@ -25,17 +33,29 @@ impl From 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 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 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), } } } diff --git a/apps/system/components/SDKRuntime/kata-sdk-interface/src/lib.rs b/apps/system/components/SDKRuntime/kata-sdk-interface/src/lib.rs index e0fffa1..35c1ef9 100644 --- a/apps/system/components/SDKRuntime/kata-sdk-interface/src/lib.rs +++ b/apps/system/components/SDKRuntime/kata-sdk-interface/src/lib.rs @@ -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 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 { + 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::(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::(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::( + SDKRuntimeRequest::Log, + &LogRequest { + msg: msg.as_bytes(), + }, + )?; + header.into() } diff --git a/apps/system/components/SDKRuntime/kata-sdk-manager/Cargo.toml b/apps/system/components/SDKRuntime/kata-sdk-manager/Cargo.toml new file mode 100644 index 0000000..89aebf4 --- /dev/null +++ b/apps/system/components/SDKRuntime/kata-sdk-manager/Cargo.toml @@ -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 } diff --git a/apps/system/components/SDKRuntime/kata-sdk-interface/Makefile b/apps/system/components/SDKRuntime/kata-sdk-manager/Makefile similarity index 91% rename from apps/system/components/SDKRuntime/kata-sdk-interface/Makefile rename to apps/system/components/SDKRuntime/kata-sdk-manager/Makefile index 0b9d3bc..b3f3004 100644 --- a/apps/system/components/SDKRuntime/kata-sdk-interface/Makefile +++ b/apps/system/components/SDKRuntime/kata-sdk-manager/Makefile @@ -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 $@ diff --git a/apps/system/components/SDKRuntime/kata-sdk-interface/cbindgen.toml b/apps/system/components/SDKRuntime/kata-sdk-manager/cbindgen.toml similarity index 97% rename from apps/system/components/SDKRuntime/kata-sdk-interface/cbindgen.toml rename to apps/system/components/SDKRuntime/kata-sdk-manager/cbindgen.toml index 310945f..0250819 100644 --- a/apps/system/components/SDKRuntime/kata-sdk-interface/cbindgen.toml +++ b/apps/system/components/SDKRuntime/kata-sdk-manager/cbindgen.toml @@ -20,5 +20,5 @@ includes = ["CamkesBindings.h"] [export] include = [ - "SDKRuntimeError", + "SDKManagerError", ] diff --git a/apps/system/components/SDKRuntime/kata-sdk-manager/src/lib.rs b/apps/system/components/SDKRuntime/kata-sdk-manager/src/lib.rs new file mode 100644 index 0000000..84f8227 --- /dev/null +++ b/apps/system/components/SDKRuntime/kata-sdk-manager/src/lib.rs @@ -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 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; + + /// 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(()) +} diff --git a/apps/system/components/SDKRuntime/kata-sdk-runtime/Cargo.toml b/apps/system/components/SDKRuntime/kata-sdk-runtime/Cargo.toml index ad17ac3..a165d19 100644 --- a/apps/system/components/SDKRuntime/kata-sdk-runtime/Cargo.toml +++ b/apps/system/components/SDKRuntime/kata-sdk-runtime/Cargo.toml @@ -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" diff --git a/apps/system/components/SDKRuntime/kata-sdk-runtime/src/lib.rs b/apps/system/components/SDKRuntime/kata-sdk-runtime/src/lib.rs index 77d6f2f..a6409cf 100644 --- a/apps/system/components/SDKRuntime/kata-sdk-runtime/src/lib.rs +++ b/apps/system/components/SDKRuntime/kata-sdk-runtime/src/lib.rs @@ -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>, +} +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 { + 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) } } diff --git a/apps/system/components/SDKRuntime/kata-sdk-runtime/src/runtime/mod.rs b/apps/system/components/SDKRuntime/kata-sdk-runtime/src/runtime/mod.rs new file mode 100644 index 0000000..ec6b59c --- /dev/null +++ b/apps/system/components/SDKRuntime/kata-sdk-runtime/src/runtime/mod.rs @@ -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, +} +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 { + 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), + } + } +} diff --git a/apps/system/interfaces/SDKRuntimeInterface.camkes b/apps/system/interfaces/SDKManagerInterface.camkes similarity index 74% rename from apps/system/interfaces/SDKRuntimeInterface.camkes rename to apps/system/interfaces/SDKManagerInterface.camkes index 3ee6902..2cbc6cb 100644 --- a/apps/system/interfaces/SDKRuntimeInterface.camkes +++ b/apps/system/interfaces/SDKManagerInterface.camkes @@ -12,8 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -procedure SDKRuntimeInterface { - include ; +procedure SDKManagerInterface { + include ; - SDKRuntimeError sdk_ping(); + void capscan(); + SDKManagerError get_endpoint(in string bundle_id); + SDKManagerError release_endpoint(in string bundle_id); }; diff --git a/apps/system/system.camkes b/apps/system/system.camkes index 6b17281..f561691 100644 --- a/apps/system/system.camkes +++ b/apps/system/system.camkes @@ -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);