| // 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. |
| |
| /*! |
| * CantripOS SDK Manager CAmkES component support routines. |
| */ |
| |
| #![no_std] |
| //error[E0658]: dereferencing raw mutable pointers in statics is unstable |
| #![feature(const_mut_refs)] |
| |
| use static_assertions::assert_cfg; |
| // NB: the RPC implementation uses MCS syscalls |
| assert_cfg!(feature = "CONFIG_KERNEL_MCS"); |
| |
| use cfg_if::cfg_if; |
| |
| extern crate alloc; |
| use alloc::string::String; |
| use alloc::vec; |
| use cantrip_memory_interface::cantrip_object_alloc_in_toplevel_static; |
| use cantrip_memory_interface::ObjDesc; |
| use cantrip_os_common::camkes; |
| use cantrip_os_common::copyregion::CopyRegion; |
| use cantrip_os_common::cspace_slot::CSpaceSlot; |
| use cantrip_os_common::logger; |
| use cantrip_os_common::sel4_sys; |
| use cantrip_sdk_manager::SDKManagerError; |
| use cantrip_sdk_manager::SDKManagerInterface; |
| use cantrip_sdk_manager::SDKManagerRequest; |
| use cantrip_sdk_manager::SDK_MANAGER_REQUEST_DATA_SIZE; |
| use cantrip_sdk_runtime::CantripSDKRuntime; |
| use log::{error, info}; |
| use zerovec::ZeroVec; |
| |
| use camkes::*; |
| use logger::*; |
| |
| use sdk_interface::SDKAppId; |
| use sdk_interface::SDKError; |
| use sdk_interface::SDKRuntimeError; |
| use sdk_interface::SDKRuntimeInterface; |
| use sdk_interface::SDKRuntimeRequest; |
| use sdk_interface::SDKRUNTIME_REQUEST_DATA_SIZE; |
| |
| use sel4_sys::seL4_CPtr; |
| use sel4_sys::seL4_CapRights; |
| use sel4_sys::seL4_EndpointObject; |
| use sel4_sys::seL4_FaultTag; |
| use sel4_sys::seL4_MessageInfo; |
| use sel4_sys::seL4_Recv; |
| use sel4_sys::seL4_ReplyObject; |
| use sel4_sys::seL4_ReplyRecv; |
| use sel4_sys::seL4_Word; |
| |
| // Generated code... |
| mod generated { |
| include!(concat!(env!("SEL4_OUT_DIR"), "/../sdk_runtime/camkes.rs")); |
| } |
| use generated::*; |
| |
| fn cantrip_sdk() -> impl SDKManagerInterface + SDKRuntimeInterface { |
| static CANTRIP_SDK: CantripSDKRuntime = CantripSDKRuntime::empty(); |
| let mut runtime = CANTRIP_SDK.get(); |
| if runtime.is_empty() { |
| // Setup the SDKRuntime service (endpoint part) from scratch (no CAmkES help). |
| let bundle = |
| cantrip_object_alloc_in_toplevel_static(vec![ObjDesc::new(seL4_EndpointObject, 1, 0)]) |
| .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"); |
| unsafe { |
| SDKRUNTIME_ENDPOINT = ep_slot.release(); |
| } |
| |
| // NB: SDKRuntime needs the original (unbadged) cap to mint badged |
| // caps with WGP rights for applications (returned by get_endpoint). |
| runtime.init(&endpoint); |
| } |
| runtime |
| } |
| |
| // Server RPC plumbing. |
| static mut SDKRUNTIME_ENDPOINT: seL4_CPtr = 0; |
| static mut SDKRUNTIME_REPLY: seL4_CPtr = 0; |
| static mut SDKRUNTIME_RECV_SLOT: seL4_CPtr = 0; |
| |
| struct SdkRuntimeControlThread; |
| impl CamkesThreadInterface for SdkRuntimeControlThread { |
| fn pre_init() { |
| // XXX how to handle "maybe" inclusion |
| static_logger!(logger); |
| |
| static mut HEAP_MEMORY: [u8; 8 * 1024] = [0; 8 * 1024]; |
| unsafe { |
| CAMKES.pre_init(&mut HEAP_MEMORY); |
| } |
| |
| // Setup the SDKRuntime service (reply part) from scratch (no CAmkES help). |
| // NB: the endpoint part is done in cantrip_sdk(). |
| let bundle = |
| cantrip_object_alloc_in_toplevel_static(vec![ObjDesc::new(seL4_ReplyObject, 1, 1)]) |
| .expect("alloc"); |
| |
| // Create reply (WG). |
| let reply = Camkes::top_level_path(bundle.objs[0].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) |
| unsafe { |
| SDKRUNTIME_REPLY = reply_slot.release(); |
| } |
| |
| // Receive slot for frames with RPC parameters. |
| unsafe { |
| SDKRUNTIME_RECV_SLOT = CSpaceSlot::new().release(); |
| } |
| |
| cantrip_sdk(); |
| } |
| |
| // 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. |
| fn run() { |
| let recv_path = &Camkes::top_level_path(unsafe { SDKRUNTIME_RECV_SLOT }); |
| CAMKES.init_recv_path(recv_path); |
| Camkes::debug_assert_slot_empty("run", recv_path); |
| |
| let mut copy_region = unsafe { CopyRegion::new(get_sdk_params_mut()) }; |
| |
| // Do initial Recv; after this we use ReplyRecv to minimize syscalls. |
| unsafe { |
| let mut client_badge: seL4_Word = 0; |
| let mut response: Result<(), SDKError>; |
| let mut info = seL4_Recv( |
| /*src=*/ SDKRUNTIME_ENDPOINT, |
| /*sender=*/ &mut client_badge as _, |
| /*reply=*/ SDKRUNTIME_REPLY, |
| ); |
| loop { |
| let label = info.get_label(); |
| let app_id = client_badge as SDKAppId; // XXX safe? |
| |
| // Check for a fault condition and handle those specially. |
| if label < (SDKRuntimeRequest::Ping as usize) { |
| match seL4_FaultTag::try_from(label) { |
| Ok(fault_tag) => { |
| #[cfg(feature = "CONFIG_DEBUG_BUILD")] |
| print_fault_debug(app_id, fault_tag); |
| |
| #[cfg(not(feature = "CONFIG_DEBUG_BUILD"))] |
| info!("Fault tag {} from {}", fault_tag as usize, app_id); |
| } |
| Err(_) => error!("Bad fault tag {} on msg from {}", label, app_id), |
| } |
| |
| Camkes::debug_assert_slot_empty("fault", recv_path); |
| |
| // Can't respond to one of these messages, really, since doing so |
| // would unsuspend the faulting thread, leading possibly to another |
| // fault depending on the type. For now, just wait for another |
| // message and start back at the top of the loop. |
| |
| // XXX debug seL4 complains about an unexecuted reply cap here. |
| info = seL4_Recv( |
| /*src=*/ SDKRUNTIME_ENDPOINT, |
| /*sender=*/ &mut client_badge as _, |
| /*reply=*/ SDKRUNTIME_REPLY, |
| ); |
| continue; |
| } |
| |
| 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(). |
| // XXX not true with rust templates |
| Camkes::clear_request_cap(); |
| // Map the frame with RPC parameters and process the request. |
| if copy_region.map(recv_path.1).is_ok() { |
| // The request token is passed in the MessageInfo label field. |
| // Any request-specific parameters are serialized in the first |
| // half of the page, with the second half reserved for reply data. |
| // We might consider sending a request length out-of-band (like |
| // the request token) to enable variable page splitting. |
| // |
| // NB: the request_slice is immutable over the processing |
| // below so it's safe to pass (deserialized) values to |
| // the implementation(s) below. |
| let (request_slice, reply_slice) = copy_region |
| .as_mut() |
| .split_at_mut(SDKRUNTIME_REQUEST_DATA_SIZE); |
| let request_slice = &*request_slice; // NB: immutable alias |
| |
| // TODO(sleffler): decode from shared page instead of label |
| response = match SDKRuntimeRequest::try_from(label) { |
| Ok(tag) => Self::request(tag, app_id, request_slice, reply_slice), |
| Err(_) => { |
| // TODO(b/254286176): possible ddos |
| error!("Unknown RPC request {}", label); |
| Err(SDKError::UnknownRequest) |
| } |
| }; |
| copy_region.unmap().expect("unmap"); |
| } else { |
| // TODO(b/254286176): possible ddos |
| error!("Unable to map RPC parameters; badge {}", client_badge); |
| response = Err(SDKError::MapPageFailed); |
| } |
| Camkes::delete_path(recv_path).expect("delete"); |
| Camkes::debug_assert_slot_empty("run", recv_path); |
| |
| info = seL4_ReplyRecv( |
| /*src=*/ SDKRUNTIME_ENDPOINT, |
| /*msgInfo=*/ |
| seL4_MessageInfo::new( |
| /*label=*/ SDKRuntimeError::from(response) as seL4_Word, |
| /*capsUnwrapped=*/ 0, |
| /*extraCaps=*/ 0, |
| /*length=*/ 0, |
| ), |
| /*sender=*/ &mut client_badge as _, |
| /*reply=*/ SDKRUNTIME_REPLY, |
| ); |
| } |
| } |
| } |
| } |
| |
| #[cfg(feature = "CONFIG_DEBUG_BUILD")] |
| fn print_fault_debug(app_id: SDKAppId, fault_type: seL4_FaultTag) { |
| use sel4_sys::seL4_GetMR; |
| match fault_type { |
| seL4_FaultTag::seL4_Fault_NullFault => { |
| let _ = cantrip_sdk().log(app_id, "normal exit or termination"); |
| } |
| seL4_FaultTag::seL4_Fault_CapFault => { |
| let _ = cantrip_sdk().log(app_id, "invalid capability"); |
| } |
| seL4_FaultTag::seL4_Fault_UnknownSyscall => { |
| let _ = cantrip_sdk().log(app_id, "unknown syscall"); |
| } |
| seL4_FaultTag::seL4_Fault_UserException => { |
| let _ = cantrip_sdk().log(app_id, "user exception"); |
| } |
| seL4_FaultTag::seL4_Fault_VMFault => { |
| let _ = cantrip_sdk().log(app_id, "virtual-memory fault:"); |
| info!(target: "", "IP {:#010x}", unsafe { seL4_GetMR(0) }); |
| info!(target: "", "Addr {:#010x}", unsafe { seL4_GetMR(1) }); |
| info!(target: "", "Prefetch {:#x}", unsafe { seL4_GetMR(2) }); |
| info!(target: "", "FSR {:#x}", unsafe { seL4_GetMR(3) }); |
| info!(target: "", "Length {:#x}", unsafe { seL4_GetMR(4) }); |
| } |
| |
| #[cfg(feature = "CONFIG_KERNEL_MCS")] |
| seL4_FaultTag::seL4_Fault_Timeout => { |
| let _ = cantrip_sdk().log(app_id, "application timed out"); |
| } |
| } |
| } |
| |
| fn serialize_failure(e: postcard::Error) -> SDKError { |
| error!("serialize failed: {:?}", e); |
| SDKError::SerializeFailed |
| } |
| fn deserialize_failure(e: postcard::Error) -> SDKError { |
| error!("deserialize failed: {:?}", e); |
| SDKError::DeserializeFailed |
| } |
| |
| impl SdkRuntimeControlThread { |
| fn request( |
| request: SDKRuntimeRequest, |
| app_id: SDKAppId, |
| request_slice: &[u8], |
| reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| match request { |
| SDKRuntimeRequest::Ping => Self::ping_request(app_id, request_slice, reply_slice), |
| SDKRuntimeRequest::Log => Self::log_request(app_id, request_slice, reply_slice), |
| SDKRuntimeRequest::ReadKey => { |
| Self::read_key_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::WriteKey => { |
| Self::write_key_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::DeleteKey => { |
| Self::delete_key_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::OneshotTimer => { |
| Self::timer_oneshot_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::PeriodicTimer => { |
| Self::timer_periodic_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::CancelTimer => { |
| Self::timer_cancel_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::WaitForTimers => { |
| Self::timer_wait_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::PollForTimers => { |
| Self::timer_poll_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::OneshotModel => { |
| Self::model_oneshot_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::PeriodicModel => { |
| Self::model_periodic_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::CancelModel => { |
| Self::model_cancel_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::WaitForModel => { |
| Self::model_wait_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::PollForModels => { |
| Self::model_poll_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::GetModelOutput => { |
| Self::model_output_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::GetModelInputParams => { |
| Self::model_get_input_params_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::SetModelInput => { |
| Self::model_set_input_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::AudioReset => { |
| Self::audio_reset_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::AudioRecordStart => { |
| Self::audio_record_start_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::AudioRecordCollect => { |
| Self::audio_record_collect_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::AudioRecordStop => { |
| Self::audio_record_stop_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::AudioPlayStart => { |
| Self::audio_play_start_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::AudioPlayWrite => { |
| Self::audio_play_write_request(app_id, request_slice, reply_slice) |
| } |
| SDKRuntimeRequest::AudioPlayStop => { |
| Self::audio_play_stop_request(app_id, request_slice, reply_slice) |
| } |
| } |
| } |
| |
| fn ping_request( |
| app_id: SDKAppId, |
| _request_slice: &[u8], |
| _reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| cantrip_sdk().ping(app_id) |
| } |
| |
| fn log_request( |
| app_id: SDKAppId, |
| request_slice: &[u8], |
| _reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let request = postcard::from_bytes::<sdk_interface::LogRequest>(request_slice) |
| .map_err(deserialize_failure)?; |
| let msg = core::str::from_utf8(request.msg).or(Err(SDKError::InvalidString))?; |
| cantrip_sdk().log(app_id, msg) |
| } |
| |
| fn read_key_request( |
| app_id: SDKAppId, |
| request_slice: &[u8], |
| reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let request = postcard::from_bytes::<sdk_interface::ReadKeyRequest>(request_slice) |
| .map_err(deserialize_failure)?; |
| let value = cantrip_sdk().read_key(app_id, request.key)?; |
| let _ = postcard::to_slice(&sdk_interface::ReadKeyResponse { value: &value }, reply_slice) |
| .map_err(serialize_failure)?; |
| Ok(()) |
| } |
| |
| fn write_key_request( |
| app_id: SDKAppId, |
| request_slice: &[u8], |
| _reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let request = postcard::from_bytes::<sdk_interface::WriteKeyRequest>(request_slice) |
| .map_err(deserialize_failure)?; |
| // NB: the serialized data are variable length so copy to convert |
| let mut keyval = [0u8; sdk_interface::KEY_VALUE_DATA_SIZE]; |
| keyval[..request.value.len()].copy_from_slice(request.value); |
| cantrip_sdk().write_key(app_id, request.key, &keyval) |
| } |
| |
| fn delete_key_request( |
| app_id: SDKAppId, |
| request_slice: &[u8], |
| _reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let request = postcard::from_bytes::<sdk_interface::DeleteKeyRequest>(request_slice) |
| .map_err(deserialize_failure)?; |
| cantrip_sdk().delete_key(app_id, request.key) |
| } |
| |
| fn timer_oneshot_request( |
| app_id: SDKAppId, |
| request_slice: &[u8], |
| _reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let request = postcard::from_bytes::<sdk_interface::TimerStartRequest>(request_slice) |
| .map_err(deserialize_failure)?; |
| cantrip_sdk().timer_oneshot(app_id, request.id, request.duration_ms) |
| } |
| |
| fn timer_periodic_request( |
| app_id: SDKAppId, |
| request_slice: &[u8], |
| _reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let request = postcard::from_bytes::<sdk_interface::TimerStartRequest>(request_slice) |
| .map_err(deserialize_failure)?; |
| cantrip_sdk().timer_periodic(app_id, request.id, request.duration_ms) |
| } |
| |
| fn timer_cancel_request( |
| app_id: SDKAppId, |
| request_slice: &[u8], |
| _reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let request = postcard::from_bytes::<sdk_interface::TimerCancelRequest>(request_slice) |
| .map_err(deserialize_failure)?; |
| cantrip_sdk().timer_cancel(app_id, request.id) |
| } |
| |
| fn timer_wait_request( |
| app_id: SDKAppId, |
| _request_slice: &[u8], |
| reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let mask = cantrip_sdk().timer_wait(app_id)?; |
| let _ = postcard::to_slice(&sdk_interface::TimerWaitResponse { mask }, reply_slice) |
| .map_err(serialize_failure)?; |
| Ok(()) |
| } |
| |
| fn timer_poll_request( |
| app_id: SDKAppId, |
| _request_slice: &[u8], |
| reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let mask = cantrip_sdk().timer_poll(app_id)?; |
| let _ = postcard::to_slice(&sdk_interface::TimerWaitResponse { mask }, reply_slice) |
| .map_err(serialize_failure)?; |
| Ok(()) |
| } |
| |
| fn model_oneshot_request( |
| app_id: SDKAppId, |
| request_slice: &[u8], |
| reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let request = postcard::from_bytes::<sdk_interface::ModelOneshotRequest>(request_slice) |
| .map_err(deserialize_failure)?; |
| let id = cantrip_sdk().model_oneshot(app_id, request.model_id)?; |
| let _ = postcard::to_slice(&sdk_interface::ModelStartResponse { id }, reply_slice) |
| .map_err(serialize_failure)?; |
| Ok(()) |
| } |
| |
| fn model_periodic_request( |
| app_id: SDKAppId, |
| request_slice: &[u8], |
| reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let request = postcard::from_bytes::<sdk_interface::ModelPeriodicRequest>(request_slice) |
| .map_err(deserialize_failure)?; |
| let id = cantrip_sdk().model_periodic(app_id, request.model_id, request.duration_ms)?; |
| let _ = postcard::to_slice(&sdk_interface::ModelStartResponse { id }, reply_slice) |
| .map_err(serialize_failure)?; |
| Ok(()) |
| } |
| |
| fn model_cancel_request( |
| app_id: SDKAppId, |
| request_slice: &[u8], |
| _reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let request = postcard::from_bytes::<sdk_interface::ModelCancelRequest>(request_slice) |
| .map_err(deserialize_failure)?; |
| cantrip_sdk().model_cancel(app_id, request.id) |
| } |
| |
| fn model_wait_request( |
| app_id: SDKAppId, |
| _request_slice: &[u8], |
| reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let mask = cantrip_sdk().model_wait(app_id)?; |
| let _ = postcard::to_slice(&sdk_interface::ModelWaitResponse { mask }, reply_slice) |
| .map_err(serialize_failure)?; |
| Ok(()) |
| } |
| |
| fn model_poll_request( |
| app_id: SDKAppId, |
| _request_slice: &[u8], |
| reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let mask = cantrip_sdk().model_poll(app_id)?; |
| let _ = postcard::to_slice(&sdk_interface::ModelWaitResponse { mask }, reply_slice) |
| .map_err(serialize_failure)?; |
| Ok(()) |
| } |
| |
| fn model_output_request( |
| app_id: SDKAppId, |
| request_slice: &[u8], |
| reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let request = postcard::from_bytes::<sdk_interface::ModelOutputRequest>(request_slice) |
| .map_err(deserialize_failure)?; |
| let mloutput = cantrip_sdk().model_output(app_id, request.id)?; |
| let _ = postcard::to_slice( |
| &sdk_interface::ModelOutputResponse { |
| output: sdk_interface::ModelOutput { |
| jobnum: mloutput.jobnum, |
| return_code: mloutput.return_code, |
| epc: mloutput.epc, |
| data: mloutput.data, |
| }, |
| }, |
| reply_slice, |
| ) |
| .map_err(serialize_failure)?; |
| Ok(()) |
| } |
| |
| fn model_get_input_params_request( |
| app_id: SDKAppId, |
| request_slice: &[u8], |
| reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let request = |
| postcard::from_bytes::<sdk_interface::ModelGetInputParamsRequest>(request_slice) |
| .map_err(deserialize_failure)?; |
| let (id, input_params) = cantrip_sdk().model_get_input_params(app_id, request.model_id)?; |
| let _ = postcard::to_slice( |
| &sdk_interface::ModelGetInputParamsResponse { id, input_params }, |
| reply_slice, |
| ) |
| .map_err(serialize_failure)?; |
| Ok(()) |
| } |
| |
| fn model_set_input_request( |
| app_id: SDKAppId, |
| request_slice: &[u8], |
| _reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let request = postcard::from_bytes::<sdk_interface::ModelSetInputRequest>(request_slice) |
| .map_err(deserialize_failure)?; |
| // NB: referencing input_data in the ipc buffer is safe |
| cantrip_sdk().model_set_input( |
| app_id, |
| request.id, |
| request.input_data_offset, |
| request.input_data, |
| ) |
| } |
| |
| fn audio_reset_request( |
| app_id: SDKAppId, |
| request_slice: &[u8], |
| _reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let request = postcard::from_bytes::<sdk_interface::AudioResetRequest>(request_slice) |
| .map_err(deserialize_failure)?; |
| cantrip_sdk().audio_reset( |
| app_id, |
| request.rxrst, |
| request.txrst, |
| request.rxilvl, |
| request.txilvl, |
| ) |
| } |
| |
| fn audio_record_start_request( |
| app_id: SDKAppId, |
| request_slice: &[u8], |
| _reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let request = postcard::from_bytes::<sdk_interface::AudioRecordStartRequest>(request_slice) |
| .map_err(deserialize_failure)?; |
| cantrip_sdk().audio_record_start( |
| app_id, |
| request.rate, |
| request.buffer_size, |
| request.stop_on_full, |
| ) |
| } |
| |
| fn audio_record_collect_request( |
| app_id: SDKAppId, |
| request_slice: &[u8], |
| reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let request = |
| postcard::from_bytes::<sdk_interface::AudioRecordCollectRequest>(request_slice) |
| .map_err(deserialize_failure)?; |
| let mut sdk = cantrip_sdk(); |
| let data = sdk.audio_record_collect(app_id, request.max_samples, request.wait_if_empty)?; |
| let _ = postcard::to_slice( |
| &sdk_interface::AudioRecordCollectResponse { |
| data: ZeroVec::from_slice_or_alloc(data), |
| }, |
| reply_slice, |
| ) |
| .map_err(serialize_failure)?; |
| Ok(()) |
| } |
| |
| fn audio_record_stop_request( |
| app_id: SDKAppId, |
| _request_slice: &[u8], |
| _reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| cantrip_sdk().audio_record_stop(app_id) |
| } |
| |
| fn audio_play_start_request( |
| app_id: SDKAppId, |
| request_slice: &[u8], |
| _reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let request = postcard::from_bytes::<sdk_interface::AudioPlayStartRequest>(request_slice) |
| .map_err(deserialize_failure)?; |
| cantrip_sdk().audio_play_start(app_id, request.rate, request.buffer_size) |
| } |
| |
| fn audio_play_write_request( |
| app_id: SDKAppId, |
| request_slice: &[u8], |
| _reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| let request = postcard::from_bytes::<sdk_interface::AudioPlayWriteRequest>(request_slice) |
| .map_err(deserialize_failure)?; |
| cantrip_sdk().audio_play_write(app_id, request.data.to_vec().as_slice()) |
| } |
| |
| fn audio_play_stop_request( |
| app_id: SDKAppId, |
| _request_slice: &[u8], |
| _reply_slice: &mut [u8], |
| ) -> Result<(), SDKError> { |
| cantrip_sdk().audio_play_stop(app_id) |
| } |
| } |
| |
| type SDKManagerResult = Result<(usize, Option<seL4_CPtr>), SDKManagerError>; |
| |
| struct SdkManagerInterfaceThread; |
| impl CamkesThreadInterface for SdkManagerInterfaceThread { |
| fn run() { |
| // NB: no inbound caps, only (potentially) attached to a reply |
| rpc_basic_recv_with_reply_cap!( |
| sdk_manager, |
| SDK_MANAGER_REQUEST_DATA_SIZE, |
| SDKManagerError::Success |
| ); |
| } |
| } |
| impl SdkManagerInterfaceThread { |
| fn dispatch( |
| _client_badge: usize, |
| request_buffer: &[u8], |
| _reply_buffer: &mut [u8], |
| ) -> SDKManagerResult { |
| let request = match postcard::from_bytes::<SDKManagerRequest>(request_buffer) { |
| Ok(request) => request, |
| Err(_) => return Err(SDKManagerError::DeserializeFailed), |
| }; |
| match request { |
| SDKManagerRequest::GetEndpoint(app_id) => Self::get_endpoint_request(app_id), |
| SDKManagerRequest::ReleaseEndpoint(app_id) => Self::release_endpoint_request(app_id), |
| SDKManagerRequest::Capscan => Self::capscan_request(), |
| } |
| } |
| fn get_endpoint_request(app_id: &str) -> SDKManagerResult { |
| // TODO(283265795): copy app_id from the IPCBuffer |
| cantrip_sdk() |
| .get_endpoint(&String::from(app_id)) |
| .map(|cap| (0, Some(cap))) |
| } |
| fn release_endpoint_request(app_id: &str) -> SDKManagerResult { |
| // TODO(283265795): copy app_id from the IPCBuffer |
| cantrip_sdk() |
| .release_endpoint(&String::from(app_id)) |
| .map(|_| (0, None)) |
| } |
| fn capscan_request() -> SDKManagerResult { |
| let _ = Camkes::capscan(); |
| Ok((0, None)) |
| } |
| } |
| |
| // Glue in i2s driver (for now). |
| cfg_if! { |
| if #[cfg(feature = "i2s-driver")] { |
| pub struct I2SRxWatermarkInterfaceThread; |
| impl CamkesThreadInterface for I2SRxWatermarkInterfaceThread { |
| fn run() { |
| dedicated_irq_loop!( |
| rx_watermark, |
| i2s_driver::RxWatermarkInterfaceThread::handler |
| ); |
| } |
| } |
| pub struct I2STxWatermarkInterfaceThread; |
| impl CamkesThreadInterface for I2STxWatermarkInterfaceThread { |
| fn run() { |
| dedicated_irq_loop!( |
| tx_watermark, |
| i2s_driver::TxWatermarkInterfaceThread::handler |
| ); |
| } |
| } |
| } |
| } |