mirror of
https://github.com/AmbiML/sparrow-kata-full.git
synced 2025-07-16 07:16:13 +00:00
Add initial ProcessMaanger interfaces.
- new ProcessManager component (aka kata-process-manager) NB: interfaces/ProcessManagerBindings.h is manually generated by cbindgen for the moment; e.g. cargo install cbindgen; cd components/ProcessManager; cbindgen -c cbindgen.toml \ -o ../../interfaces/ProcessManagerBindings.h kata-proc-common Change-Id: I153c6b193c6ba8e376b87a2563dc8543753f0b42 GitOrigin-RevId: 18c354f14cbec6ce01c020136fe9aefd88248ee9
This commit is contained in:
parent
c61d7890a7
commit
e35c69ae4c
@ -29,6 +29,19 @@ DeclareCAmkESComponent(DebugConsole
|
||||
LIBS kata_debug_console
|
||||
)
|
||||
|
||||
RustAddLibrary(
|
||||
kata_process_manager
|
||||
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/components/ProcessManager
|
||||
TARGET "riscv32imc-unknown-none-elf"
|
||||
LIB_FILENAME libkata_process_manager.a
|
||||
)
|
||||
|
||||
DeclareCAmkESComponent(ProcessManager
|
||||
LIBS kata_process_manager
|
||||
INCLUDES interfaces
|
||||
)
|
||||
|
||||
|
||||
DeclareCAmkESComponent(UartDriver
|
||||
SOURCES components/UartDriver/src/driver.c
|
||||
)
|
||||
|
6
apps/system/components/ProcessManager/Cargo.toml
Normal file
6
apps/system/components/ProcessManager/Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
||||
[workspace]
|
||||
|
||||
members = [
|
||||
"kata-proc-common",
|
||||
"kata-proc-manager",
|
||||
]
|
13
apps/system/components/ProcessManager/ProcessManager.camkes
Normal file
13
apps/system/components/ProcessManager/ProcessManager.camkes
Normal file
@ -0,0 +1,13 @@
|
||||
// Kata OS ProcessManager services.
|
||||
|
||||
import <ProcessControlInterface.camkes>;
|
||||
import <PackageManagementInterface.camkes>;
|
||||
import <SeL4DebugInterface.camkes>;
|
||||
|
||||
component ProcessManager {
|
||||
control;
|
||||
provides ProcessControlInterface proc_ctrl;
|
||||
provides PackageManagementInterface proc_mgmt;
|
||||
|
||||
uses SeL4DebugInterface sel4debug;
|
||||
}
|
7
apps/system/components/ProcessManager/cbindgen.toml
Normal file
7
apps/system/components/ProcessManager/cbindgen.toml
Normal file
@ -0,0 +1,7 @@
|
||||
language = "C"
|
||||
include_guard = "__PROCESS_MANAGER_BINDINGS_H__"
|
||||
autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
|
||||
no_includes = true
|
||||
|
||||
[export]
|
||||
include = ["Bundle", "BundleId", "BundleIdArray"]
|
@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "kata-proc-common"
|
||||
version = "0.1.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
@ -0,0 +1,105 @@
|
||||
//! Kata OS process management support
|
||||
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
|
||||
use core::ops::{Index, IndexMut};
|
||||
|
||||
// NB: struct's marked repr(C) are processed by cbindgen to get a .h file
|
||||
// used in camkes interfaces.
|
||||
|
||||
// Max bundles that can be installed at one time.
|
||||
pub const MAX_BUNDLES: usize = 10;
|
||||
// Max/fixed size of a BundleId; this is stopgap for C compatibility
|
||||
pub const MAX_BUNDLE_ID_SIZE: usize = 32;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct BundleId {
|
||||
pub id: [u8; MAX_BUNDLE_ID_SIZE],
|
||||
}
|
||||
impl BundleId {
|
||||
pub fn empty(value: u8) -> Self {
|
||||
BundleId {
|
||||
id: [value; MAX_BUNDLE_ID_SIZE],
|
||||
}
|
||||
}
|
||||
pub fn new() -> Self {
|
||||
BundleId::empty(0)
|
||||
}
|
||||
pub fn is_zero(&self) -> bool {
|
||||
self.id == [0; MAX_BUNDLE_ID_SIZE]
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct BundleIdArray {
|
||||
pub ids: [BundleId; MAX_BUNDLES], // TODO(sleffler): placeholder
|
||||
}
|
||||
impl BundleIdArray {
|
||||
pub fn new() -> Self {
|
||||
BundleIdArray {
|
||||
ids: [BundleId::new(); MAX_BUNDLES],
|
||||
}
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
self.ids.iter().filter(|&id| !id.is_zero()).count()
|
||||
}
|
||||
}
|
||||
impl Index<usize> for BundleIdArray {
|
||||
type Output = BundleId;
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
&self.ids[index]
|
||||
}
|
||||
}
|
||||
impl IndexMut<usize> for BundleIdArray {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
&mut self.ids[index]
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(sleffler): Bundle should come from whomever implements install+uninstall
|
||||
// for ProcessManagerInterface
|
||||
#[repr(C)]
|
||||
pub struct Bundle {
|
||||
pub something: u32, // TODO(sleffler): placeholder
|
||||
}
|
||||
impl Bundle {
|
||||
pub fn new() -> Self {
|
||||
Bundle { something: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum ProcessManagerError {
|
||||
BundleNotFound,
|
||||
BundleFound,
|
||||
NoSpace,
|
||||
// Generic errors for interface failures.
|
||||
InstallFailed,
|
||||
UninstallFailed,
|
||||
StartFailed,
|
||||
StopFailed,
|
||||
}
|
||||
|
||||
// Abstract interface for injecting fakes, etc.
|
||||
pub trait ProcessManagerInterface {
|
||||
fn install(&self, bundle: &Bundle) -> Result<(), ProcessManagerError>;
|
||||
fn uninstall(&self, bundle: &Bundle) -> Result<(), ProcessManagerError>;
|
||||
fn start(&self, bundle: &Bundle) -> Result<(), ProcessManagerError>;
|
||||
fn stop(&self, bundle: &Bundle) -> Result<(), ProcessManagerError>;
|
||||
}
|
||||
|
||||
pub trait PackageManagementInterface<'a> {
|
||||
fn install(
|
||||
&mut self,
|
||||
bundle_id: &BundleId,
|
||||
bundle: &'a Bundle,
|
||||
) -> Result<(), ProcessManagerError>;
|
||||
fn uninstall(&mut self, bundle_id: &BundleId) -> Result<(), ProcessManagerError>;
|
||||
}
|
||||
|
||||
pub trait ProcessControlInterface {
|
||||
fn start(&mut self, bundle_id: &BundleId) -> Result<(), ProcessManagerError>;
|
||||
fn stop(&mut self, bundle_id: &BundleId) -> Result<(), ProcessManagerError>;
|
||||
fn get_running_bundles(&self) -> BundleIdArray;
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "kata-process-manager"
|
||||
version = "0.1.0"
|
||||
description = "Kata OS ProcessManager services"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
arrayvec = { version = "0.7", default-features = false }
|
||||
cstr_core = { version = "0.2.3", default-features = false }
|
||||
kata-proc-common = { path = "../kata-proc-common" }
|
||||
panic-halt = "0.2.0"
|
||||
|
||||
[lib]
|
||||
name = "kata_process_manager"
|
||||
path = "src/run.rs"
|
||||
crate-type = ["staticlib"]
|
@ -0,0 +1,269 @@
|
||||
//! Kata OS process management support
|
||||
|
||||
// TODO(sleffler): need locking? (maybe guarantee single-threading via ipc)
|
||||
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
#![feature(const_fn_trait_bound)] // NB: for ProcessManager::empty using manager: None
|
||||
|
||||
#[cfg(not(test))]
|
||||
extern crate panic_halt;
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use core::marker::Sync;
|
||||
use cstr_core::CStr;
|
||||
use kata_proc_common as proc;
|
||||
use proc::*;
|
||||
|
||||
// Prints/logs a message to the console.
|
||||
// Temporary workaround for LoggerInterface not working
|
||||
fn syslog(_level: i32, msg: &str) {
|
||||
// Print |str| on the consule using the SeL4 debug syscall.
|
||||
// NB: for now the message must be explicitly \0-terminated.
|
||||
fn sel4_putstr(msg: &str) {
|
||||
extern "C" {
|
||||
fn sel4debug_put_string(msg: *const cstr_core::c_char);
|
||||
}
|
||||
unsafe {
|
||||
sel4debug_put_string(CStr::from_bytes_with_nul(msg.as_bytes()).unwrap().as_ptr());
|
||||
}
|
||||
}
|
||||
sel4_putstr(msg); // NB:assumes caller includes \0
|
||||
}
|
||||
|
||||
// Bundle state tracks start/stop operations.
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
enum BundleState {
|
||||
Stopped,
|
||||
Running,
|
||||
}
|
||||
|
||||
// We track the Bundle & ProcessControlInterface state.
|
||||
// NB: assume storage manager (or other) owns Bundle
|
||||
struct BundleData<'a> {
|
||||
state: BundleState,
|
||||
bundle: &'a Bundle,
|
||||
}
|
||||
|
||||
impl<'b> BundleData<'b> {
|
||||
fn new(bundle: &'b Bundle) -> Self {
|
||||
BundleData {
|
||||
state: BundleState::Stopped,
|
||||
bundle: bundle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The ProcessManager presents the PackageManagementInterface (for loading
|
||||
// applications from storage) and the ProcessControlInterface (for starting
|
||||
// and stopping associated applications). The interface to the underlying
|
||||
// system(s) are abstracted through the ProcessManagerInterface. One instance
|
||||
// of the ProcessManager is created at start and accessed through SeL4 RPC's
|
||||
// (from other components).
|
||||
pub struct ProcessManager<'a> {
|
||||
// TODO(sleffler): Option is for empty which is meant for static setup
|
||||
manager: Option<&'a (dyn ProcessManagerInterface + Sync)>,
|
||||
|
||||
// TODO(sleffler): hash table (requires missing deps)
|
||||
ids: ArrayVec<BundleId, MAX_BUNDLES>,
|
||||
bundles: ArrayVec<BundleData<'a>, MAX_BUNDLES>,
|
||||
}
|
||||
|
||||
impl<'a> ProcessManager<'a> {
|
||||
// Creates a new ProcessManager instance.
|
||||
pub fn new(manager: &'a (dyn ProcessManagerInterface + Sync)) -> ProcessManager<'a> {
|
||||
ProcessManager {
|
||||
manager: Some(manager),
|
||||
ids: ArrayVec::<BundleId, MAX_BUNDLES>::new(),
|
||||
bundles: ArrayVec::<BundleData, MAX_BUNDLES>::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// Creates an incomplete ProcessManager instance for static initialization.
|
||||
// The instance must be followed with an init() call to complete setup.
|
||||
pub const fn empty() -> Self {
|
||||
ProcessManager {
|
||||
manager: None,
|
||||
ids: ArrayVec::<BundleId, MAX_BUNDLES>::new_const(),
|
||||
bundles: ArrayVec::<BundleData, MAX_BUNDLES>::new_const(),
|
||||
}
|
||||
}
|
||||
|
||||
// Completes initialization of an instance created with empty().
|
||||
pub fn init(&mut self, manager: &'a (dyn ProcessManagerInterface + Sync)) {
|
||||
self.manager = Some(manager);
|
||||
}
|
||||
|
||||
// Returns the index of |bundle_id| if previously installed.
|
||||
fn get_bundle_index(&self, bundle_id: &BundleId) -> Option<usize> {
|
||||
self.ids.iter().position(|x| bundle_id == x)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PackageManagementInterface<'a> for ProcessManager<'a> {
|
||||
fn install(
|
||||
&mut self,
|
||||
bundle_id: &BundleId,
|
||||
bundle: &'a Bundle,
|
||||
) -> Result<(), ProcessManagerError> {
|
||||
match self.get_bundle_index(bundle_id) {
|
||||
Some(_) => Err(ProcessManagerError::BundleFound),
|
||||
None => {
|
||||
if self.ids.is_full() {
|
||||
return Err(ProcessManagerError::NoSpace);
|
||||
}
|
||||
self.manager.unwrap().install(bundle)?;
|
||||
self.bundles.push(BundleData::new(bundle));
|
||||
self.ids.push(*bundle_id);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
fn uninstall(&mut self, bundle_id: &BundleId) -> Result<(), ProcessManagerError> {
|
||||
match self.get_bundle_index(bundle_id) {
|
||||
None => Err(ProcessManagerError::BundleNotFound),
|
||||
Some(index) => {
|
||||
let bundle = &mut self.bundles[index];
|
||||
// TODO(sleffler): remove private state regardless of error?
|
||||
self.manager.unwrap().uninstall(bundle.bundle)?;
|
||||
self.bundles.remove(index);
|
||||
self.ids.remove(index);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ProcessControlInterface for ProcessManager<'a> {
|
||||
fn start(&mut self, bundle_id: &BundleId) -> Result<(), ProcessManagerError> {
|
||||
match self.get_bundle_index(bundle_id) {
|
||||
Some(index) => {
|
||||
let bundle = &mut self.bundles[index];
|
||||
if bundle.state == BundleState::Stopped {
|
||||
self.manager.unwrap().start(bundle.bundle)?;
|
||||
}
|
||||
bundle.state = BundleState::Running;
|
||||
Ok(())
|
||||
}
|
||||
None => Err(ProcessManagerError::BundleNotFound),
|
||||
}
|
||||
}
|
||||
fn stop(&mut self, bundle_id: &BundleId) -> Result<(), ProcessManagerError> {
|
||||
match self.get_bundle_index(bundle_id) {
|
||||
Some(index) => {
|
||||
let bundle = &mut self.bundles[index];
|
||||
if bundle.state == BundleState::Running {
|
||||
self.manager.unwrap().stop(bundle.bundle)?;
|
||||
}
|
||||
bundle.state = BundleState::Stopped;
|
||||
Ok(())
|
||||
}
|
||||
None => Err(ProcessManagerError::BundleNotFound), // XXX ignore & return true?
|
||||
}
|
||||
}
|
||||
fn get_running_bundles(&self) -> BundleIdArray {
|
||||
let mut result = BundleIdArray::new();
|
||||
for (index, (&id, _bundle)) in self
|
||||
.ids
|
||||
.iter()
|
||||
.zip(self.bundles.iter())
|
||||
.filter(|(_, bundle)| matches!(bundle.state, BundleState::Running))
|
||||
.enumerate()
|
||||
{
|
||||
result[index] = id;
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(sleffler): move to init or similar if a thread isn't needed
|
||||
#[no_mangle]
|
||||
pub extern "C" fn run() {
|
||||
// Setup the userland address spaces, lifecycles, and system introspection
|
||||
// for third-party applications.
|
||||
syslog(0, "ProcessManager::run\n\0");
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use proc::ProcessManagerError as pme;
|
||||
|
||||
struct FakeManager {}
|
||||
|
||||
impl ProcessManagerInterface for FakeManager {
|
||||
fn install(&self, _bundle: &Bundle) -> Result<(), pme> {
|
||||
Ok(())
|
||||
}
|
||||
fn uninstall(&self, _bundle: &Bundle) -> Result<(), pme> {
|
||||
Ok(())
|
||||
}
|
||||
fn start(&self, _bundle: &Bundle) -> Result<(), pme> {
|
||||
Ok(())
|
||||
}
|
||||
fn stop(&self, _bundle: &Bundle) -> Result<(), pme> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pkg_mgmt() {
|
||||
let bundle_id = BundleId::empty(1);
|
||||
let bundle = Bundle::new();
|
||||
let fake = tests::FakeManager {};
|
||||
let mut mgr = ProcessManager::new(&fake);
|
||||
|
||||
// Not installed, should fail.
|
||||
assert_eq!(mgr.uninstall(&bundle_id).err(), Some(pme::BundleNotFound));
|
||||
// Install the bundle.
|
||||
assert!(mgr.install(&bundle_id, &bundle).is_ok());
|
||||
// Re-install the same bundle should fail.
|
||||
assert_eq!(
|
||||
mgr.install(&bundle_id, &bundle).err(),
|
||||
Some(pme::BundleFound)
|
||||
);
|
||||
// Now uninstalling the bundle should work.
|
||||
assert!(mgr.uninstall(&bundle_id).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_proc_ctrl() {
|
||||
let bundle_id = BundleId::empty(2);
|
||||
let bundle = Bundle::new();
|
||||
let fake = tests::FakeManager {};
|
||||
let mut mgr = ProcessManager::new(&fake);
|
||||
|
||||
assert!(mgr.install(&bundle_id, &bundle).is_ok());
|
||||
assert!(mgr.stop(&bundle_id).is_ok());
|
||||
assert!(mgr.start(&bundle_id).is_ok());
|
||||
|
||||
let running = mgr.get_running_bundles();
|
||||
assert_eq!(running.len(), 1);
|
||||
assert_eq!(running[0], bundle_id);
|
||||
|
||||
assert!(mgr.stop(&bundle_id).is_ok());
|
||||
// After stopping the bundle we should see nothing running.
|
||||
let running = mgr.get_running_bundles();
|
||||
assert_eq!(running.len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_init() {
|
||||
let bundle_id = BundleId { id: [1; 32] };
|
||||
let bundle = Bundle::new();
|
||||
let fake = tests::FakeManager {};
|
||||
let mut mgr = ProcessManager::empty();
|
||||
mgr.init(&fake);
|
||||
|
||||
// Not installed, should fail.
|
||||
assert_eq!(mgr.uninstall(&bundle_id).err(), Some(pme::BundleNotFound));
|
||||
// Install the bundle.
|
||||
assert!(mgr.install(&bundle_id, &bundle).is_ok());
|
||||
// Re-install the same bundle should fail.
|
||||
assert_eq!(
|
||||
mgr.install(&bundle_id, &bundle).err(),
|
||||
Some(pme::BundleFound)
|
||||
);
|
||||
// Now uninstalling the bundle should work.
|
||||
assert!(mgr.uninstall(&bundle_id).is_ok());
|
||||
}
|
||||
}
|
6
apps/system/interfaces/PackageManagementInterface.camkes
Normal file
6
apps/system/interfaces/PackageManagementInterface.camkes
Normal file
@ -0,0 +1,6 @@
|
||||
procedure PackageManagementInterface {
|
||||
include <ProcessManagerBindings.h>;
|
||||
|
||||
bool install(in string bundleId, in Bundle bundle);
|
||||
bool uninstall(in string bundleId);
|
||||
};
|
7
apps/system/interfaces/ProcessControlInterface.camkes
Normal file
7
apps/system/interfaces/ProcessControlInterface.camkes
Normal file
@ -0,0 +1,7 @@
|
||||
procedure ProcessControlInterface {
|
||||
include <ProcessManagerBindings.h>;
|
||||
|
||||
bool start(in string bundleId);
|
||||
bool stop(in string bundleId);
|
||||
BundleIdArray get_running_bundles();
|
||||
};
|
20
apps/system/interfaces/ProcessManagerBindings.h
Normal file
20
apps/system/interfaces/ProcessManagerBindings.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef __PROCESS_MANAGER_BINDINGS_H__
|
||||
#define __PROCESS_MANAGER_BINDINGS_H__
|
||||
|
||||
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
|
||||
|
||||
#define MAX_BUNDLES 10
|
||||
|
||||
typedef struct Bundle {
|
||||
uint32_t something;
|
||||
} Bundle;
|
||||
|
||||
typedef struct BundleId {
|
||||
uint8_t id[32];
|
||||
} BundleId;
|
||||
|
||||
typedef struct BundleIdArray {
|
||||
struct BundleId ids[MAX_BUNDLES];
|
||||
} BundleIdArray;
|
||||
|
||||
#endif /* __PROCESS_MANAGER_BINDINGS_H__ */
|
@ -15,6 +15,7 @@ import <std_connector.camkes>;
|
||||
import "interfaces/uart.idl4";
|
||||
import "components/UartDriver/UartDriver.camkes";
|
||||
import "components/DebugConsole/DebugConsole.camkes";
|
||||
import "components/ProcessManager/ProcessManager.camkes";
|
||||
import "components/SeL4Debug/SeL4Debug.camkes";
|
||||
|
||||
component UART {
|
||||
@ -29,6 +30,7 @@ assembly {
|
||||
component UART uart;
|
||||
component UartDriver drv;
|
||||
component DebugConsole debug_console;
|
||||
component ProcessManager process_manager;
|
||||
component SeL4Debug sel4debug;
|
||||
|
||||
connection seL4HardwareMMIO uart_mem(from drv.mem, to uart.mem);
|
||||
@ -39,6 +41,7 @@ assembly {
|
||||
|
||||
// Connect the SeL4Debug interface of each component that needs access.
|
||||
connection seL4RPCCall SeL4DebugInterface(from debug_console.sel4debug,
|
||||
from process_manager.sel4debug,
|
||||
to sel4debug.sel4debug);
|
||||
|
||||
connection seL4SharedData tx_channel(
|
||||
|
Loading…
Reference in New Issue
Block a user