mirror of
https://github.com/AmbiML/sparrow-kata-full.git
synced 2025-09-16 15:08:27 +00:00
Add MemoryManager service.
The MemoryManager service allocates & frees seL4 objects. Requests can be batched. Capabilities to dynamically allocated objects are moved in CNode containers attached to IPC requests. Specific changes: - Add new CAmkES MemoryManager component. - Add api's for allocating & freeing singleton objects (e.g. kata_cnode_alloc) and batches of objects (kata_object_alloc & kata_object_free). - Add support to kata-os-rootserver to hand-off UntypedMemory objects just before terminating. The objects are placed directly in the MemoryManager's top-level CNode and a BootInfo frame is constructed that describes where the objects are. - Switch the rootserver to kata-os-rootserver as the C version lacks the UntypedMemory hand-off. - Add test_bootinfo kata-shell command to dump the MemoryManager BootInfo frame contents (broken for now because it directlry references the shared page). - Add test_obj_alloc kata-shell command that exercises the MemoryManager singleton and batch api's While here, did some cleanup of arg handling in kata-shell. TODO: top-level object allocations use a simplistic capability allocator TODO: move test_bootinfo to the MemoryManager and add an interface rpc Change-Id: I778b2d5fe7f2f9b65ee642ff905cf56d4b2b02fd GitOrigin-RevId: 7fc72d1927bba165234955e68f8b9ad1b556f6fb
This commit is contained in:
@@ -31,13 +31,13 @@ DeclareCAmkESComponent(DebugConsole
|
|||||||
)
|
)
|
||||||
|
|
||||||
RustAddLibrary(
|
RustAddLibrary(
|
||||||
kata_process_manager
|
kata_memory_manager
|
||||||
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/components/ProcessManager
|
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/components/MemoryManager
|
||||||
LIB_FILENAME libkata_process_manager.a
|
LIB_FILENAME libkata_memory_manager.a
|
||||||
)
|
)
|
||||||
|
|
||||||
DeclareCAmkESComponent(ProcessManager
|
DeclareCAmkESComponent(MemoryManager
|
||||||
LIBS kata_process_manager
|
LIBS kata_memory_manager
|
||||||
INCLUDES interfaces
|
INCLUDES interfaces
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -52,6 +52,17 @@ DeclareCAmkESComponent(MlCoordinator
|
|||||||
INCLUDES interfaces
|
INCLUDES interfaces
|
||||||
)
|
)
|
||||||
|
|
||||||
|
RustAddLibrary(
|
||||||
|
kata_process_manager
|
||||||
|
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/components/ProcessManager
|
||||||
|
LIB_FILENAME libkata_process_manager.a
|
||||||
|
)
|
||||||
|
|
||||||
|
DeclareCAmkESComponent(ProcessManager
|
||||||
|
LIBS kata_process_manager
|
||||||
|
INCLUDES interfaces
|
||||||
|
)
|
||||||
|
|
||||||
RustAddLibrary(
|
RustAddLibrary(
|
||||||
kata_security_coordinator
|
kata_security_coordinator
|
||||||
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/components/SecurityCoordinator
|
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/components/SecurityCoordinator
|
||||||
|
@@ -2,6 +2,7 @@ import <LoggerInterface.camkes>;
|
|||||||
import <ProcessControlInterface.camkes>;
|
import <ProcessControlInterface.camkes>;
|
||||||
import <PackageManagementInterface.camkes>;
|
import <PackageManagementInterface.camkes>;
|
||||||
import <MlCoordinatorInterface.camkes>;
|
import <MlCoordinatorInterface.camkes>;
|
||||||
|
import <MemoryInterface.camkes>;
|
||||||
import <SecurityCoordinatorInterface.camkes>;
|
import <SecurityCoordinatorInterface.camkes>;
|
||||||
import <StorageInterface.camkes>;
|
import <StorageInterface.camkes>;
|
||||||
|
|
||||||
@@ -15,11 +16,12 @@ component DebugConsole {
|
|||||||
uses rust_read_inf uart_read;
|
uses rust_read_inf uart_read;
|
||||||
|
|
||||||
provides LoggerInterface logger;
|
provides LoggerInterface logger;
|
||||||
uses ProcessControlInterface proc_ctrl;
|
uses MemoryInterface memory;
|
||||||
|
uses MlCoordinatorInterface mlcoord;
|
||||||
uses PackageManagementInterface pkg_mgmt;
|
uses PackageManagementInterface pkg_mgmt;
|
||||||
|
uses ProcessControlInterface proc_ctrl;
|
||||||
// TODO(b/200707300): for debugging
|
// TODO(b/200707300): for debugging
|
||||||
uses SecurityCoordinatorInterface security;
|
uses SecurityCoordinatorInterface security;
|
||||||
// TODO(b/200707300): for debugging
|
// TODO(b/200707300): for debugging
|
||||||
uses StorageInterface storage;
|
uses StorageInterface storage;
|
||||||
uses MlCoordinatorInterface mlcoord;
|
|
||||||
}
|
}
|
||||||
|
@@ -8,18 +8,17 @@ build = "build.rs"
|
|||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
sel4-config = { path = "../../kata-os-common/src/sel4-config" }
|
sel4-config = { path = "../../kata-os-common/src/sel4-config" }
|
||||||
|
|
||||||
[build-env]
|
|
||||||
SEL4_OUT_DIR = "${ROOTDIR}out/kata/kernel"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
CONFIG_DEBUG_BUILD = []
|
CONFIG_DEBUG_BUILD = []
|
||||||
|
CONFIG_KERNEL_MCS = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
crc = { version = "1.4.0", default_features = false }
|
crc = { version = "1.4.0", default_features = false }
|
||||||
hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
|
hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
|
||||||
kata-io = { path = "../kata-io" }
|
kata-io = { path = "../kata-io" }
|
||||||
kata-line-reader = { path = "../kata-line-reader" }
|
kata-line-reader = { path = "../kata-line-reader" }
|
||||||
|
kata-memory-interface = { path = "../../MemoryManager/kata-memory-interface" }
|
||||||
kata-proc-interface = { path = "../../ProcessManager/kata-proc-interface" }
|
kata-proc-interface = { path = "../../ProcessManager/kata-proc-interface" }
|
||||||
kata-os-common = { path = "../../kata-os-common" }
|
kata-os-common = { path = "../../kata-os-common" }
|
||||||
kata-security-interface = { path = "../../SecurityCoordinator/kata-security-interface" }
|
kata-security-interface = { path = "../../SecurityCoordinator/kata-security-interface" }
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
//extern crate sel4_config;
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
|
use alloc::vec;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
@@ -10,6 +11,8 @@ use log;
|
|||||||
|
|
||||||
use kata_io as io;
|
use kata_io as io;
|
||||||
use kata_line_reader::LineReader;
|
use kata_line_reader::LineReader;
|
||||||
|
use kata_os_common::sel4_sys;
|
||||||
|
use kata_memory_interface::*;
|
||||||
use kata_proc_interface::kata_pkg_mgmt_install;
|
use kata_proc_interface::kata_pkg_mgmt_install;
|
||||||
use kata_proc_interface::kata_pkg_mgmt_uninstall;
|
use kata_proc_interface::kata_pkg_mgmt_uninstall;
|
||||||
use kata_proc_interface::kata_proc_ctrl_get_running_bundles;
|
use kata_proc_interface::kata_proc_ctrl_get_running_bundles;
|
||||||
@@ -19,6 +22,11 @@ use kata_storage_interface::kata_storage_delete;
|
|||||||
use kata_storage_interface::kata_storage_read;
|
use kata_storage_interface::kata_storage_read;
|
||||||
use kata_storage_interface::kata_storage_write;
|
use kata_storage_interface::kata_storage_write;
|
||||||
|
|
||||||
|
use sel4_sys::seL4_CPtr;
|
||||||
|
use sel4_sys::seL4_MinSchedContextBits;
|
||||||
|
use sel4_sys::seL4_ObjectType::*;
|
||||||
|
use sel4_sys::seL4_WordBits;
|
||||||
|
|
||||||
mod rz;
|
mod rz;
|
||||||
|
|
||||||
/// Error type indicating why a command line is not runnable.
|
/// Error type indicating why a command line is not runnable.
|
||||||
@@ -108,6 +116,9 @@ fn dispatch_command(cmdline: &str, input: &mut dyn io::BufRead, output: &mut dyn
|
|||||||
"kvwrite" => kvwrite_command(&mut args, output),
|
"kvwrite" => kvwrite_command(&mut args, output),
|
||||||
"install" => install_command(&mut args, output),
|
"install" => install_command(&mut args, output),
|
||||||
"loglevel" => loglevel_command(&mut args, output),
|
"loglevel" => loglevel_command(&mut args, output),
|
||||||
|
"malloc" => malloc_command(&mut args, output),
|
||||||
|
"mfree" => mfree_command(&mut args, output),
|
||||||
|
"mstats" => mstats_command(&mut args, output),
|
||||||
"rz" => rz_command(input, output),
|
"rz" => rz_command(input, output),
|
||||||
"ps" => ps_command(output),
|
"ps" => ps_command(output),
|
||||||
"scecho" => scecho_command(cmdline, output),
|
"scecho" => scecho_command(cmdline, output),
|
||||||
@@ -117,9 +128,11 @@ fn dispatch_command(cmdline: &str, input: &mut dyn io::BufRead, output: &mut dyn
|
|||||||
|
|
||||||
"test_alloc" => test_alloc_command(output),
|
"test_alloc" => test_alloc_command(output),
|
||||||
"test_alloc_error" => test_alloc_error_command(output),
|
"test_alloc_error" => test_alloc_error_command(output),
|
||||||
"test_panic" => test_panic_command(),
|
"test_bootinfo" => test_bootinfo_command(output),
|
||||||
"test_mlexecute" => test_mlexecute_command(),
|
"test_mlexecute" => test_mlexecute_command(),
|
||||||
"test_mlcontinuous" => test_mlcontinuous_command(&mut args),
|
"test_mlcontinuous" => test_mlcontinuous_command(&mut args),
|
||||||
|
"test_obj_alloc" => test_obj_alloc_command(output),
|
||||||
|
"test_panic" => test_panic_command(),
|
||||||
|
|
||||||
_ => Err(CommandError::UnknownCommand),
|
_ => Err(CommandError::UnknownCommand),
|
||||||
};
|
};
|
||||||
@@ -237,14 +250,11 @@ fn add_command(
|
|||||||
args: &mut dyn Iterator<Item = &str>,
|
args: &mut dyn Iterator<Item = &str>,
|
||||||
output: &mut dyn io::Write,
|
output: &mut dyn io::Write,
|
||||||
) -> Result<(), CommandError> {
|
) -> Result<(), CommandError> {
|
||||||
if let Some(x_str) = args.nth(0) {
|
let x_str = args.next().ok_or(CommandError::BadArgs)?;
|
||||||
if let Some(y_str) = args.nth(0) {
|
let x = x_str.parse::<f32>()?;
|
||||||
let x = x_str.parse::<f32>()?;
|
let y_str = args.next().ok_or(CommandError::BadArgs)?;
|
||||||
let y = y_str.parse::<f32>()?;
|
let y = y_str.parse::<f32>()?;
|
||||||
return Ok(writeln!(output, "{}", x + y)?);
|
return Ok(writeln!(output, "{}", x + y)?);
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(CommandError::BadArgs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implements a command that outputs the ANSI "clear console" sequence.
|
/// Implements a command that outputs the ANSI "clear console" sequence.
|
||||||
@@ -285,122 +295,175 @@ fn uninstall_command(
|
|||||||
args: &mut dyn Iterator<Item = &str>,
|
args: &mut dyn Iterator<Item = &str>,
|
||||||
output: &mut dyn io::Write,
|
output: &mut dyn io::Write,
|
||||||
) -> Result<(), CommandError> {
|
) -> Result<(), CommandError> {
|
||||||
if let Some(bundle_id) = args.nth(0) {
|
let bundle_id = args.next().ok_or(CommandError::BadArgs)?;
|
||||||
match kata_pkg_mgmt_uninstall(bundle_id) {
|
match kata_pkg_mgmt_uninstall(bundle_id) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
writeln!(output, "Bundle \"{}\" uninstalled.", bundle_id)?;
|
writeln!(output, "Bundle \"{}\" uninstalled.", bundle_id)?;
|
||||||
}
|
}
|
||||||
Err(status) => {
|
Err(status) => {
|
||||||
writeln!(output, "uninstall failed: {:?}", status)?;
|
writeln!(output, "uninstall failed: {:?}", status)?;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(CommandError::BadArgs)
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_command(
|
fn start_command(
|
||||||
args: &mut dyn Iterator<Item = &str>,
|
args: &mut dyn Iterator<Item = &str>,
|
||||||
output: &mut dyn io::Write,
|
output: &mut dyn io::Write,
|
||||||
) -> Result<(), CommandError> {
|
) -> Result<(), CommandError> {
|
||||||
if let Some(bundle_id) = args.nth(0) {
|
let bundle_id = args.next().ok_or(CommandError::BadArgs)?;
|
||||||
match kata_proc_ctrl_start(bundle_id) {
|
match kata_proc_ctrl_start(bundle_id) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
writeln!(output, "Bundle \"{}\" started.", bundle_id)?;
|
writeln!(output, "Bundle \"{}\" started.", bundle_id)?;
|
||||||
}
|
}
|
||||||
Err(status) => {
|
Err(status) => {
|
||||||
writeln!(output, "start failed: {:?}", status)?;
|
writeln!(output, "start failed: {:?}", status)?;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(CommandError::BadArgs)
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stop_command(
|
fn stop_command(
|
||||||
args: &mut dyn Iterator<Item = &str>,
|
args: &mut dyn Iterator<Item = &str>,
|
||||||
output: &mut dyn io::Write,
|
output: &mut dyn io::Write,
|
||||||
) -> Result<(), CommandError> {
|
) -> Result<(), CommandError> {
|
||||||
if let Some(bundle_id) = args.nth(0) {
|
let bundle_id = args.next().ok_or(CommandError::BadArgs)?;
|
||||||
match kata_proc_ctrl_stop(bundle_id) {
|
match kata_proc_ctrl_stop(bundle_id) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
writeln!(output, "Bundle \"{}\" stopped.", bundle_id)?;
|
writeln!(output, "Bundle \"{}\" stopped.", bundle_id)?;
|
||||||
}
|
}
|
||||||
Err(status) => {
|
Err(status) => {
|
||||||
writeln!(output, "stop failed: {:?}", status)?;
|
writeln!(output, "stop failed: {:?}", status)?;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(CommandError::BadArgs)
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kvdelete_command(
|
fn kvdelete_command(
|
||||||
args: &mut dyn Iterator<Item = &str>,
|
args: &mut dyn Iterator<Item = &str>,
|
||||||
output: &mut dyn io::Write,
|
output: &mut dyn io::Write,
|
||||||
) -> Result<(), CommandError> {
|
) -> Result<(), CommandError> {
|
||||||
if let Some(key) = args.nth(0) {
|
let key = args.next().ok_or(CommandError::BadArgs)?;
|
||||||
match kata_storage_delete(key) {
|
match kata_storage_delete(key) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
writeln!(output, "Delete key \"{}\".", key)?;
|
writeln!(output, "Delete key \"{}\".", key)?;
|
||||||
}
|
}
|
||||||
Err(status) => {
|
Err(status) => {
|
||||||
writeln!(output, "Delete key \"{}\" failed: {:?}", key, status)?;
|
writeln!(output, "Delete key \"{}\" failed: {:?}", key, status)?;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(CommandError::BadArgs)
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kvread_command(
|
fn kvread_command(
|
||||||
args: &mut dyn Iterator<Item = &str>,
|
args: &mut dyn Iterator<Item = &str>,
|
||||||
output: &mut dyn io::Write,
|
output: &mut dyn io::Write,
|
||||||
) -> Result<(), CommandError> {
|
) -> Result<(), CommandError> {
|
||||||
if let Some(key) = args.nth(0) {
|
let key = args.next().ok_or(CommandError::BadArgs)?;
|
||||||
match kata_storage_read(key) {
|
match kata_storage_read(key) {
|
||||||
Ok(value) => {
|
Ok(value) => {
|
||||||
writeln!(output, "Read key \"{}\" = {:?}.", key, value)?;
|
writeln!(output, "Read key \"{}\" = {:?}.", key, value)?;
|
||||||
}
|
}
|
||||||
Err(status) => {
|
Err(status) => {
|
||||||
writeln!(output, "Read key \"{}\" failed: {:?}", key, status)?;
|
writeln!(output, "Read key \"{}\" failed: {:?}", key, status)?;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(CommandError::BadArgs)
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kvwrite_command(
|
fn kvwrite_command(
|
||||||
args: &mut dyn Iterator<Item = &str>,
|
args: &mut dyn Iterator<Item = &str>,
|
||||||
output: &mut dyn io::Write,
|
output: &mut dyn io::Write,
|
||||||
) -> Result<(), CommandError> {
|
) -> Result<(), CommandError> {
|
||||||
if let Some(key) = args.nth(0) {
|
let key = args.next().ok_or(CommandError::BadArgs)?;
|
||||||
let value = args.collect::<Vec<&str>>().join(" ");
|
let value = args.collect::<Vec<&str>>().join(" ");
|
||||||
match kata_storage_write(key, value.as_bytes()) {
|
match kata_storage_write(key, value.as_bytes()) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
writeln!(output, "Write key \"{}\" = {:?}.", key, value)?;
|
writeln!(output, "Write key \"{}\" = {:?}.", key, value)?;
|
||||||
}
|
}
|
||||||
Err(status) => {
|
Err(status) => {
|
||||||
writeln!(output, "Write key \"{}\" failed: {:?}", key, status)?;
|
writeln!(output, "Write key \"{}\" failed: {:?}", key, status)?;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(CommandError::BadArgs)
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn malloc_command(
|
||||||
|
args: &mut dyn Iterator<Item = &str>,
|
||||||
|
output: &mut dyn io::Write,
|
||||||
|
) -> Result<(), CommandError> {
|
||||||
|
let space_str = args.next().ok_or(CommandError::BadArgs)?;
|
||||||
|
let space_bytes = space_str.parse::<usize>()?;
|
||||||
|
match kata_frame_alloc(space_bytes) {
|
||||||
|
Ok(frames) => {
|
||||||
|
writeln!(output, "Allocated {:?}", frames)?;
|
||||||
|
}
|
||||||
|
Err(status) => {
|
||||||
|
writeln!(output, "malloc failed: {:?}", status)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mfree_command(
|
||||||
|
args: &mut dyn Iterator<Item = &str>,
|
||||||
|
output: &mut dyn io::Write,
|
||||||
|
) -> Result<(), CommandError> {
|
||||||
|
extern "C" { static SELF_CNODE: seL4_CPtr; }
|
||||||
|
let cptr_str = args.next().ok_or(CommandError::BadArgs)?;
|
||||||
|
let count_str = args.next().ok_or(CommandError::BadArgs)?;
|
||||||
|
let frames = ObjDescBundle::new(
|
||||||
|
unsafe { SELF_CNODE },
|
||||||
|
seL4_WordBits as u8,
|
||||||
|
vec![
|
||||||
|
ObjDesc::new(
|
||||||
|
sel4_sys::seL4_RISCV_4K_Page,
|
||||||
|
count_str.parse::<usize>()?,
|
||||||
|
cptr_str.parse::<usize>()? as seL4_CPtr,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
match kata_object_free_toplevel(&frames) {
|
||||||
|
Ok(_) => {
|
||||||
|
writeln!(output, "Free'd {:?}", frames)?;
|
||||||
|
}
|
||||||
|
Err(status) => {
|
||||||
|
writeln!(output, "mfree failed: {:?}", status)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mstats(output: &mut dyn io::Write, stats: &MemoryManagerStats)
|
||||||
|
-> Result<(), CommandError>
|
||||||
|
{
|
||||||
|
writeln!(output, "{} bytes in-use, {} bytes free, {} bytes requested, {} overhead",
|
||||||
|
stats.allocated_bytes,
|
||||||
|
stats.free_bytes,
|
||||||
|
stats.total_requested_bytes,
|
||||||
|
stats.overhead_bytes)?;
|
||||||
|
writeln!(output, "{} objs in-use, {} objs requested",
|
||||||
|
stats.allocated_objs,
|
||||||
|
stats.total_requested_objs)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mstats_command(
|
||||||
|
_args: &mut dyn Iterator<Item = &str>,
|
||||||
|
output: &mut dyn io::Write,
|
||||||
|
) -> Result<(), CommandError> {
|
||||||
|
match kata_memory_stats() {
|
||||||
|
Ok(stats) => { mstats(output, &stats)?; }
|
||||||
|
Err(status) => { writeln!(output, "stats failed: {:?}", status)?; }
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implements a command that tests facilities that use the global allocator.
|
/// Implements a command that tests facilities that use the global allocator.
|
||||||
/// Shamelessly cribbed from https://os.phil-opp.com/heap-allocation/
|
/// Shamelessly cribbed from https://os.phil-opp.com/heap-allocation/
|
||||||
fn test_alloc_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
|
fn test_alloc_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
use alloc::{boxed::Box, rc::Rc, vec};
|
use alloc::{boxed::Box, rc::Rc};
|
||||||
|
|
||||||
// allocate a number on the heap
|
// allocate a number on the heap
|
||||||
let heap_value = Box::new(41);
|
let heap_value = Box::new(41);
|
||||||
@@ -443,6 +506,26 @@ fn test_alloc_error_command(output: &mut dyn io::Write) -> Result<(), CommandErr
|
|||||||
Ok(writeln!(output, "vec at {:p}", vec.as_slice())?)
|
Ok(writeln!(output, "vec at {:p}", vec.as_slice())?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_bootinfo_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
|
||||||
|
use kata_os_common::sel4_sys::seL4_BootInfo;
|
||||||
|
extern "C" {
|
||||||
|
fn sel4runtime_bootinfo() -> *const seL4_BootInfo;
|
||||||
|
}
|
||||||
|
let bootinfo_ref = unsafe { &*sel4runtime_bootinfo() };
|
||||||
|
writeln!(output, "{}:{} empty slots {}:{} untyped",
|
||||||
|
bootinfo_ref.empty.start, bootinfo_ref.empty.end,
|
||||||
|
bootinfo_ref.untyped.start, bootinfo_ref.untyped.end)?;
|
||||||
|
|
||||||
|
// NB: seL4_DebugCapIdentify is only available in debug builds
|
||||||
|
#[cfg(feature = "CONFIG_DEBUG_BUILD")]
|
||||||
|
for ut in bootinfo_ref.untyped.start..bootinfo_ref.untyped.end {
|
||||||
|
let cap_tag = unsafe { kata_os_common::sel4_sys::seL4_DebugCapIdentify(ut) };
|
||||||
|
assert_eq!(cap_tag, 2,
|
||||||
|
"expected untyped (2), got {} for cap at {}", cap_tag, ut);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Implements a command that tests panic handling.
|
/// Implements a command that tests panic handling.
|
||||||
fn test_panic_command() -> Result<(), CommandError> {
|
fn test_panic_command() -> Result<(), CommandError> {
|
||||||
panic!("testing");
|
panic!("testing");
|
||||||
@@ -473,3 +556,77 @@ fn test_mlcontinuous_command(args: &mut dyn Iterator<Item = &str>) -> Result<(),
|
|||||||
}
|
}
|
||||||
Err(CommandError::BadArgs)
|
Err(CommandError::BadArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_obj_alloc_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
|
||||||
|
let before_stats = kata_memory_stats().expect("before stats");
|
||||||
|
mstats(output, &before_stats)?;
|
||||||
|
|
||||||
|
fn check_alloc(output: &mut dyn io::Write,
|
||||||
|
name: &str,
|
||||||
|
res: Result<ObjDescBundle, MemoryManagerError>) {
|
||||||
|
match res {
|
||||||
|
Ok(obj) => {
|
||||||
|
if let Err(e) = kata_object_free_toplevel(&obj) {
|
||||||
|
let _ = writeln!(output, "free {} {:?} failed: {:?}", name, obj, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let _ = writeln!(output, "alloc {} failed: {:?}", name, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NB: alloc+free immediately so we don't run out of top-level CNode slots
|
||||||
|
check_alloc(output, "untyped", kata_untyped_alloc(12)); // NB: 4KB
|
||||||
|
check_alloc(output, "tcb", kata_tcb_alloc());
|
||||||
|
check_alloc(output, "endpoint", kata_endpoint_alloc());
|
||||||
|
check_alloc(output, "notification", kata_notification_alloc());
|
||||||
|
check_alloc(output, "cnode", kata_cnode_alloc(5)); // NB: 32 slots
|
||||||
|
check_alloc(output, "frame", kata_frame_alloc(4096));
|
||||||
|
// check_alloc(output, "large frame", kata_frame_alloc(1024*1024));
|
||||||
|
check_alloc(output, "page table", kata_page_table_alloc());
|
||||||
|
|
||||||
|
#[cfg(feature = "CONFIG_KERNEL_MCS")]
|
||||||
|
check_alloc(output, "sched context",
|
||||||
|
kata_sched_context_alloc(seL4_MinSchedContextBits));
|
||||||
|
|
||||||
|
#[cfg(feature = "CONFIG_KERNEL_MCS")]
|
||||||
|
check_alloc(output, "reply", kata_reply_alloc());
|
||||||
|
|
||||||
|
let after_stats = kata_memory_stats().expect("after stats");
|
||||||
|
mstats(output, &after_stats)?;
|
||||||
|
assert_eq!(before_stats.allocated_bytes, after_stats.allocated_bytes);
|
||||||
|
assert_eq!(before_stats.free_bytes, after_stats.free_bytes);
|
||||||
|
|
||||||
|
// Batch allocate into a private CNode as we might to build a process.
|
||||||
|
const CNODE_DEPTH: usize = 7; // 128 slots
|
||||||
|
let cnode = kata_cnode_alloc(CNODE_DEPTH).unwrap(); // XXX handle error
|
||||||
|
let objs = ObjDescBundle::new(
|
||||||
|
cnode.objs[0].cptr,
|
||||||
|
CNODE_DEPTH as u8,
|
||||||
|
vec![
|
||||||
|
ObjDesc::new(seL4_TCBObject, 1, 0), // 1 tcb
|
||||||
|
ObjDesc::new(seL4_EndpointObject, 2, 1), // 2 endpoiints
|
||||||
|
ObjDesc::new(seL4_ReplyObject, 2, 3), // 2 replys
|
||||||
|
ObjDesc::new(seL4_SchedContextObject, // 1 sched context
|
||||||
|
seL4_MinSchedContextBits, 5),
|
||||||
|
ObjDesc::new(seL4_RISCV_4K_Page, 10, 6), // 10 4K pages
|
||||||
|
],
|
||||||
|
);
|
||||||
|
match kata_object_alloc(&objs) {
|
||||||
|
Ok(_) => {
|
||||||
|
writeln!(output, "Batch alloc ok: {:?}", objs)?;
|
||||||
|
if let Err(e) = kata_object_free(&objs) {
|
||||||
|
writeln!(output, "Batch free err: {:?}", e)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
writeln!(output, "Batch alloc err: {:?} {:?}", objs, e)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Err(e) = kata_object_free_toplevel(&cnode) {
|
||||||
|
writeln!(output, "Cnode free err: {:?} {:?}", cnode, e)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(writeln!(output, "All tests passed!")?)
|
||||||
|
}
|
||||||
|
24
apps/system/components/MemoryManager/Cargo.toml
Normal file
24
apps/system/components/MemoryManager/Cargo.toml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[workspace]
|
||||||
|
|
||||||
|
members = [
|
||||||
|
"kata-memory-component",
|
||||||
|
"kata-memory-interface",
|
||||||
|
"kata-memory-manager",
|
||||||
|
]
|
||||||
|
resolver = "2"
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
opt-level = 0
|
||||||
|
debug = true
|
||||||
|
lto = false
|
||||||
|
codegen-units = 1
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = "z"
|
||||||
|
lto = "fat"
|
||||||
|
codegen-units = 1
|
||||||
|
split-debuginfo = "unpacked"
|
||||||
|
|
||||||
|
[profile.release.build-override]
|
||||||
|
opt-level = "z"
|
||||||
|
codegen-units = 1
|
18
apps/system/components/MemoryManager/MemoryManager.camkes
Normal file
18
apps/system/components/MemoryManager/MemoryManager.camkes
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// Kata OS MemoryManager service.
|
||||||
|
|
||||||
|
import <LoggerInterface.camkes>;
|
||||||
|
import <MemoryInterface.camkes>;
|
||||||
|
|
||||||
|
component MemoryManager {
|
||||||
|
provides MemoryInterface memory;
|
||||||
|
|
||||||
|
uses LoggerInterface logger;
|
||||||
|
|
||||||
|
// Mark the component that should receive the unallocated UntypedMemory
|
||||||
|
// passed to the rootserver from the kernel. In addition to the
|
||||||
|
// capabilities the component also gets a page with Bootinfo data that
|
||||||
|
// includes updated UntypedMemory descriptors. In order to pass the
|
||||||
|
// capabilitiies the component's cnode is up-sized to be large enough
|
||||||
|
// to hold the extra capabilties.
|
||||||
|
attribute int untyped_memory = true;
|
||||||
|
}
|
7
apps/system/components/MemoryManager/cbindgen.toml
Normal file
7
apps/system/components/MemoryManager/cbindgen.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
language = "C"
|
||||||
|
include_guard = "__MEMORY_MANAGER_BINDINGS_H__"
|
||||||
|
autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
|
||||||
|
no_includes = true
|
||||||
|
|
||||||
|
[export]
|
||||||
|
include = ["RawPageDescData", "RawMemoryStatsData", "MemoryManagerStats", "MemoryManagerError"]
|
@@ -0,0 +1,19 @@
|
|||||||
|
[package]
|
||||||
|
name = "kata-memory-component"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
kata-allocator = { path = "../../DebugConsole/kata-allocator" }
|
||||||
|
kata-logger = { path = "../../DebugConsole/kata-logger" }
|
||||||
|
kata-panic = { path = "../../DebugConsole/kata-panic" }
|
||||||
|
kata-memory-interface = { path = "../kata-memory-interface" }
|
||||||
|
kata-memory-manager = { path = "../kata-memory-manager" }
|
||||||
|
kata-os-common = { path = "../../kata-os-common" }
|
||||||
|
log = "0.4"
|
||||||
|
postcard = { version = "0.7", features = ["alloc"], default-features = false }
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "kata_memory_manager"
|
||||||
|
path = "src/run.rs"
|
||||||
|
crate-type = ["staticlib"]
|
@@ -0,0 +1,182 @@
|
|||||||
|
//! Kata OS MemoryManager component support.
|
||||||
|
|
||||||
|
// Code here binds the camkes component to the rust code.
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use core::ops::Range;
|
||||||
|
use core::slice;
|
||||||
|
extern crate kata_panic;
|
||||||
|
use kata_allocator;
|
||||||
|
use kata_logger::KataLogger;
|
||||||
|
use kata_memory_interface::MemoryManagerError;
|
||||||
|
use kata_memory_interface::MemoryManagerInterface;
|
||||||
|
use kata_memory_interface::ObjDescBundle;
|
||||||
|
use kata_memory_interface::RawMemoryStatsData;
|
||||||
|
use kata_memory_manager::KataMemoryManager;
|
||||||
|
use kata_os_common::sel4_sys;
|
||||||
|
use log::{info, trace};
|
||||||
|
use sel4_sys::seL4_BootInfo;
|
||||||
|
use sel4_sys::seL4_CNode_Delete;
|
||||||
|
use sel4_sys::seL4_CPtr;
|
||||||
|
use sel4_sys::seL4_GetCapReceivePath;
|
||||||
|
use sel4_sys::seL4_SetCapReceivePath;
|
||||||
|
use sel4_sys::seL4_Word;
|
||||||
|
use sel4_sys::seL4_WordBits;
|
||||||
|
|
||||||
|
// NB: KATA_MEMORY cannot be used before setup is completed with a call to init()
|
||||||
|
static mut KATA_MEMORY: KataMemoryManager = KataMemoryManager::empty();
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
// Each CAmkES-generated CNode has a writable self-reference to itself in
|
||||||
|
// the slot SELF_CNODE. This is used to pass CNode containers of dynamically
|
||||||
|
// allocated objects between clients & the MemoryManager.
|
||||||
|
static SELF_CNODE: seL4_CPtr;
|
||||||
|
|
||||||
|
// Each CAmkES-component has a CNode setup at a well-known slot in SELF_CNODE.
|
||||||
|
// We re-use that slot to receive CNode caps passed with alloc & free requests.
|
||||||
|
static RECV_CNODE: seL4_CPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn pre_init() {
|
||||||
|
static KATA_LOGGER: KataLogger = KataLogger;
|
||||||
|
log::set_logger(&KATA_LOGGER).unwrap();
|
||||||
|
// NB: set to max; the LoggerInterface will filter
|
||||||
|
log::set_max_level(log::LevelFilter::Trace);
|
||||||
|
|
||||||
|
// TODO(sleffler): temp until we integrate with seL4
|
||||||
|
static mut HEAP_MEMORY: [u8; 8 * 1024] = [0; 8 * 1024];
|
||||||
|
unsafe {
|
||||||
|
kata_allocator::ALLOCATOR.init(HEAP_MEMORY.as_mut_ptr() as usize, HEAP_MEMORY.len());
|
||||||
|
trace!(
|
||||||
|
"setup heap: start_addr {:p} size {}",
|
||||||
|
HEAP_MEMORY.as_ptr(),
|
||||||
|
HEAP_MEMORY.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn sel4runtime_bootinfo() -> *const seL4_BootInfo;
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
// The MemoryManager component is labeled to receive BootInfo); use
|
||||||
|
// it to complete initialization of the MemoryManager interface.
|
||||||
|
let bootinfo = &*sel4runtime_bootinfo();
|
||||||
|
KATA_MEMORY.init(
|
||||||
|
/*slots=*/Range::<seL4_CPtr> {
|
||||||
|
start: bootinfo.untyped.start,
|
||||||
|
end: bootinfo.untyped.end
|
||||||
|
},
|
||||||
|
/*untypeds=*/ bootinfo.untyped_descs(),
|
||||||
|
);
|
||||||
|
if let Ok(stats) = KATA_MEMORY.stats() {
|
||||||
|
trace!("Global memory: {} allocated {} free",
|
||||||
|
stats.allocated_bytes,
|
||||||
|
stats.free_bytes,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
// Delete the CAmkES-setup CNode; we're going to reuse the
|
||||||
|
// well-known slot once it is empty (see below).
|
||||||
|
seL4_CNode_Delete(SELF_CNODE, RECV_CNODE, seL4_WordBits as u8)
|
||||||
|
.expect("recv_node");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn memory__init() {
|
||||||
|
unsafe {
|
||||||
|
// Point the receive path to the well-known slot that was emptied.
|
||||||
|
// This will be used to receive CNode's from clients for alloc &
|
||||||
|
// free requests.
|
||||||
|
//
|
||||||
|
// NB: this must be done here (rather than someplace like pre_init)
|
||||||
|
// so it's in the context of the MemoryInterface thread (so we write
|
||||||
|
// the correct ipc buffer).
|
||||||
|
seL4_SetCapReceivePath(SELF_CNODE, RECV_CNODE, seL4_WordBits);
|
||||||
|
trace!("Cap receive path {}:{}:{}", SELF_CNODE, RECV_CNODE, seL4_WordBits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MemoryInterface glue stubs.
|
||||||
|
|
||||||
|
// Clears any capability the specified path points to.
|
||||||
|
fn clear_path(&(root, index, depth): &(seL4_CPtr, seL4_CPtr, seL4_Word)) {
|
||||||
|
// TODO(sleffler): assert since future receives are likely to fail?
|
||||||
|
if let Err(e) = unsafe { seL4_CNode_Delete(root, index, depth as u8) } {
|
||||||
|
// NB: no error is returned if the slot is empty.
|
||||||
|
info!("Failed to clear receive path {:?}: {:?}",
|
||||||
|
(root, index, depth), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn memory_alloc(
|
||||||
|
c_raw_data_len: u32,
|
||||||
|
c_raw_data: *const u8,
|
||||||
|
) -> MemoryManagerError {
|
||||||
|
unsafe {
|
||||||
|
let recv_path = seL4_GetCapReceivePath();
|
||||||
|
// NB: make sure noone clobbers the setup done in memory__init
|
||||||
|
assert_eq!(recv_path, (SELF_CNODE, RECV_CNODE, seL4_WordBits));
|
||||||
|
|
||||||
|
let raw_slice = slice::from_raw_parts(c_raw_data, c_raw_data_len as usize);
|
||||||
|
let ret_status = match postcard::from_bytes::<ObjDescBundle>(raw_slice) {
|
||||||
|
Ok(mut bundle) => {
|
||||||
|
// TODO(sleffler): verify we received a CNode in RECV_CNODE.
|
||||||
|
bundle.cnode = recv_path.1;
|
||||||
|
// NB: bundle.depth should reflect the received cnode
|
||||||
|
KATA_MEMORY.alloc(&bundle).into()
|
||||||
|
}
|
||||||
|
Err(_) => MemoryManagerError::MmeDeserializeFailed,
|
||||||
|
};
|
||||||
|
// NB: must clear ReceivePath for next request
|
||||||
|
clear_path(&recv_path);
|
||||||
|
ret_status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn memory_free(
|
||||||
|
c_raw_data_len: u32,
|
||||||
|
c_raw_data: *const u8,
|
||||||
|
) -> MemoryManagerError {
|
||||||
|
unsafe {
|
||||||
|
let recv_path = seL4_GetCapReceivePath();
|
||||||
|
// NB: make sure noone clobbers the setup done in memory__init
|
||||||
|
assert_eq!(recv_path, (SELF_CNODE, RECV_CNODE, seL4_WordBits));
|
||||||
|
|
||||||
|
let raw_slice = slice::from_raw_parts(c_raw_data, c_raw_data_len as usize);
|
||||||
|
let ret_status = match postcard::from_bytes::<ObjDescBundle>(raw_slice) {
|
||||||
|
Ok(mut bundle) => {
|
||||||
|
// TODO(sleffler): verify we received a CNode in RECV_CNODE.
|
||||||
|
bundle.cnode = recv_path.1;
|
||||||
|
// NB: bundle.depth should reflect the received cnode
|
||||||
|
KATA_MEMORY.free(&bundle).into()
|
||||||
|
}
|
||||||
|
Err(_) => MemoryManagerError::MmeDeserializeFailed,
|
||||||
|
};
|
||||||
|
// NB: must clear ReceivePath for next request
|
||||||
|
clear_path(&recv_path);
|
||||||
|
ret_status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn memory_stats(
|
||||||
|
c_raw_resp_data: *mut RawMemoryStatsData,
|
||||||
|
) -> MemoryManagerError {
|
||||||
|
unsafe {
|
||||||
|
// TODO(sleffler): verify no cap was received
|
||||||
|
match KATA_MEMORY.stats() {
|
||||||
|
Ok(stats) => {
|
||||||
|
match postcard::to_slice(&stats, &mut (*c_raw_resp_data)[..]) {
|
||||||
|
Ok(_) => MemoryManagerError::MmeSuccess,
|
||||||
|
Err(_) => MemoryManagerError::MmeSerializeFailed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => e.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,19 @@
|
|||||||
|
[package]
|
||||||
|
name = "kata-memory-interface"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
sel4-config = { path = "../../kata-os-common/src/sel4-config" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
CONFIG_KERNEL_MCS = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
postcard = { version = "0.7", features = ["alloc"], default-features = false }
|
||||||
|
serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] }
|
||||||
|
kata-os-common = { path = "../../kata-os-common" }
|
||||||
|
log = "0.4"
|
||||||
|
smallvec = "1.2"
|
@@ -0,0 +1,20 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,509 @@
|
|||||||
|
//! Kata OS memory management support
|
||||||
|
|
||||||
|
#![cfg_attr(not(test), no_std)]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
use alloc::vec;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use kata_os_common::sel4_sys;
|
||||||
|
use log::trace;
|
||||||
|
use postcard;
|
||||||
|
use sel4_sys::seL4_CNode_Move;
|
||||||
|
use sel4_sys::seL4_CPtr;
|
||||||
|
use sel4_sys::seL4_ObjectType::*;
|
||||||
|
use sel4_sys::seL4_ObjectType;
|
||||||
|
use sel4_sys::seL4_PageBits;
|
||||||
|
use sel4_sys::seL4_SetCap;
|
||||||
|
use sel4_sys::seL4_WordBits;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
// NB: @14b per desc this supports ~150 descriptors (depending
|
||||||
|
// on serde overhead), the rpc buffer is actually 4K so we could
|
||||||
|
// raise this
|
||||||
|
pub const RAW_OBJ_DESC_DATA_SIZE: usize = 2048;
|
||||||
|
pub type RawObjDescData = [u8; RAW_OBJ_DESC_DATA_SIZE];
|
||||||
|
|
||||||
|
// The MemoryManager takes collections of Object Descriptors.
|
||||||
|
//
|
||||||
|
// For an alloc request an object descriptor provides everything needed
|
||||||
|
// to allocate & retype untyped memory. Capabilities for the realized
|
||||||
|
// objects are attached to the IPC buffer holding the reply in a CNode
|
||||||
|
// container. For free requests the same object descriptors should be
|
||||||
|
// provided. Otherwise clients are responsible for filling in
|
||||||
|
// allocated objects; e.g. map page frames into a VSpace, bind endpoints
|
||||||
|
// to irq's, configure TCB slots, etc.
|
||||||
|
//
|
||||||
|
// TODO(sleffler): support setting fixed physical address for drivers
|
||||||
|
// TODO(sleffler): allocate associated resources like endpoint #'s?
|
||||||
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ObjDesc {
|
||||||
|
// Requested object type or type of object being released.
|
||||||
|
pub type_: seL4_ObjectType,
|
||||||
|
|
||||||
|
// Count of consecutive objects with the same type or, for CNode
|
||||||
|
// objects the log2 number of slots to use in sizing the object,
|
||||||
|
// or for untyped objects the log2 size in bytes, or for scheduler
|
||||||
|
// context objects the size in bits. See seL4_ObjectType::size_bits().
|
||||||
|
count: usize, // XXX oversized (except for untyped use)
|
||||||
|
|
||||||
|
// CSpace address for realized objects requested. If |count| is >1
|
||||||
|
// this descriptor describes objects with |cptr|'s [0..|count|).
|
||||||
|
// Since each block of objects has it's own |cptr| one can describe
|
||||||
|
// a collection with random layout in CSpace (useful for construction).
|
||||||
|
//
|
||||||
|
// Object capabilities returned by the MemoryManager have the maximal
|
||||||
|
// rights. We depend on trusted agents (e.g. ProcessManager) to reduce
|
||||||
|
// rights when assigning them to an application. This also applies to
|
||||||
|
// the vm attributes of page frames (e.g. mark not executable as
|
||||||
|
// appropriate).
|
||||||
|
pub cptr: seL4_CPtr,
|
||||||
|
}
|
||||||
|
impl ObjDesc {
|
||||||
|
pub fn new(
|
||||||
|
type_: seL4_ObjectType,
|
||||||
|
count: usize,
|
||||||
|
cptr: seL4_CPtr,
|
||||||
|
) -> Self {
|
||||||
|
ObjDesc { type_, count, cptr }
|
||||||
|
}
|
||||||
|
// Parameters for seL4_Untyped_Retype call.
|
||||||
|
pub fn retype_size_bits(&self) -> Option<usize> {
|
||||||
|
match self.type_ {
|
||||||
|
seL4_UntypedObject => Some(self.count), // Log2 memory size
|
||||||
|
seL4_CapTableObject => Some(self.count), // Log2 number of slots
|
||||||
|
seL4_SchedContextObject => Some(self.count), // Log2 context size
|
||||||
|
_ => self.type_.size_bits(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn retype_count(&self) -> usize {
|
||||||
|
match self.type_ {
|
||||||
|
// NB: we don't support creating multiple instances of the same
|
||||||
|
// size; the caller must supply multiple object descriptors.
|
||||||
|
seL4_UntypedObject
|
||||||
|
| seL4_CapTableObject
|
||||||
|
| seL4_SchedContextObject => 1,
|
||||||
|
_ => self.count,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Memory occupied by objects. Used for bookkeeping and statistics.
|
||||||
|
pub fn size_bytes(&self) -> Option<usize> {
|
||||||
|
match self.type_ {
|
||||||
|
seL4_UntypedObject => Some(1 << self.count),
|
||||||
|
seL4_CapTableObject =>
|
||||||
|
self.type_.size_bits().map(|x| self.count * (1 << x)),
|
||||||
|
seL4_SchedContextObject => Some(1 << self.count),
|
||||||
|
_ => self.type_.size_bits().map(|x| 1 << x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ObjDescBundle {
|
||||||
|
pub cnode: seL4_CPtr,
|
||||||
|
pub depth: u8,
|
||||||
|
pub objs: Vec<ObjDesc>,
|
||||||
|
}
|
||||||
|
impl ObjDescBundle {
|
||||||
|
pub fn new(
|
||||||
|
cnode: seL4_CPtr,
|
||||||
|
depth: u8,
|
||||||
|
objs: Vec<ObjDesc>,
|
||||||
|
) -> Self {
|
||||||
|
ObjDescBundle { cnode, depth, objs }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub enum MemoryError {
|
||||||
|
ObjCountInvalid = 0, // Too many objects requested
|
||||||
|
ObjTypeInvalid, // Request with invalid object type
|
||||||
|
ObjCapInvalid, // Request with invalid cptr XXX
|
||||||
|
UnknownMemoryError,
|
||||||
|
// Generic errors.
|
||||||
|
AllocFailed,
|
||||||
|
FreeFailed,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const RAW_MEMORY_STATS_DATA_SIZE: usize = 100;
|
||||||
|
pub type RawMemoryStatsData = [u8; RAW_MEMORY_STATS_DATA_SIZE];
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||||
|
pub struct MemoryManagerStats {
|
||||||
|
// Current space committed to allocations.
|
||||||
|
pub allocated_bytes: usize,
|
||||||
|
|
||||||
|
// Current space available.
|
||||||
|
pub free_bytes: usize,
|
||||||
|
|
||||||
|
// Total space for user requests (independent of any alignment).
|
||||||
|
pub total_requested_bytes: usize,
|
||||||
|
|
||||||
|
// Space required for operation of the MemoryManager service.
|
||||||
|
pub overhead_bytes: usize,
|
||||||
|
|
||||||
|
// Current number of seL4 objects allocated.
|
||||||
|
pub allocated_objs: usize,
|
||||||
|
|
||||||
|
// Total number of seL4 objects allocated.
|
||||||
|
pub total_requested_objs: usize,
|
||||||
|
|
||||||
|
// Retype requests failed due to insufficient available memory.
|
||||||
|
pub untyped_slab_too_small: usize,
|
||||||
|
|
||||||
|
// Alloc requests failed due to lack of untyped memory.
|
||||||
|
pub out_of_memory: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Objects are potentially batched with caps to allocated objects returned
|
||||||
|
// in the container slots specified by the |bundle] objects.
|
||||||
|
pub trait MemoryManagerInterface {
|
||||||
|
fn alloc(&mut self, bundle: &ObjDescBundle) -> Result<(), MemoryError>;
|
||||||
|
fn free(&mut self, bundle: &ObjDescBundle) -> Result<(), MemoryError>;
|
||||||
|
fn stats(&self) -> Result<MemoryManagerStats, MemoryError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public version of MemoryError presented over rpc interface.
|
||||||
|
// This is needed because the enum is exported to C users and needs to
|
||||||
|
// be unique from other enum's.
|
||||||
|
// TODO(sleffler): switch to single generic error space ala absl::StatusCode
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub enum MemoryManagerError {
|
||||||
|
MmeSuccess = 0,
|
||||||
|
MmeObjCountInvalid,
|
||||||
|
MmeObjTypeInvalid,
|
||||||
|
MmeObjCapInvalid,
|
||||||
|
MmeSerializeFailed,
|
||||||
|
MmeDeserializeFailed,
|
||||||
|
MmeUnknownError,
|
||||||
|
// Generic errors.
|
||||||
|
MmeAllocFailed,
|
||||||
|
MmeFreeFailed,
|
||||||
|
}
|
||||||
|
impl From<MemoryError> for MemoryManagerError {
|
||||||
|
fn from(err: MemoryError) -> MemoryManagerError {
|
||||||
|
match err {
|
||||||
|
MemoryError::ObjCountInvalid => MemoryManagerError::MmeObjCountInvalid,
|
||||||
|
MemoryError::ObjTypeInvalid => MemoryManagerError::MmeObjTypeInvalid,
|
||||||
|
MemoryError::ObjCapInvalid => MemoryManagerError::MmeObjCapInvalid,
|
||||||
|
MemoryError::AllocFailed => MemoryManagerError::MmeAllocFailed,
|
||||||
|
MemoryError::FreeFailed => MemoryManagerError::MmeFreeFailed,
|
||||||
|
_ => MemoryManagerError::MmeUnknownError,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<Result<(), MemoryError>> for MemoryManagerError {
|
||||||
|
fn from(result: Result<(), MemoryError>) -> MemoryManagerError {
|
||||||
|
result.map_or_else(
|
||||||
|
|e| MemoryManagerError::from(e),
|
||||||
|
|_v| MemoryManagerError::MmeSuccess,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<MemoryManagerError> for Result<(), MemoryManagerError> {
|
||||||
|
fn from(err: MemoryManagerError) -> Result<(), MemoryManagerError> {
|
||||||
|
if err == MemoryManagerError::MmeSuccess {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client wrappers.
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
// Each CAmkES-generated CNode has a writable self-reference to itself in
|
||||||
|
// the slot SELF_CNODE. This is used to pass CNode containers of dynamically
|
||||||
|
// allocated objects between clients & the MemoryManager.
|
||||||
|
static SELF_CNODE: seL4_CPtr;
|
||||||
|
|
||||||
|
// Each CAmkES-component has a CNode setup at a well-known slot. We use that
|
||||||
|
// CNode to pass capabilities with alloc & free requests.
|
||||||
|
static RECV_CNODE: seL4_CPtr;
|
||||||
|
static RECV_CNODE_DEPTH: u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn kata_object_alloc(
|
||||||
|
request: &ObjDescBundle,
|
||||||
|
) -> Result<(), MemoryManagerError> {
|
||||||
|
extern "C" {
|
||||||
|
// NB: this assumes the MemoryManager component is named "memory".
|
||||||
|
fn memory_alloc(c_request_len: u32, c_request_data: *const u8)
|
||||||
|
-> MemoryManagerError;
|
||||||
|
}
|
||||||
|
trace!("kata_object_alloc {:?}", request); // XXX
|
||||||
|
let raw_data = &mut [0u8; RAW_OBJ_DESC_DATA_SIZE];
|
||||||
|
postcard::to_slice(&request, &mut raw_data[..])
|
||||||
|
.map_err(|_| MemoryManagerError::MmeSerializeFailed)?;
|
||||||
|
unsafe {
|
||||||
|
// Attach our CNode for returning objects; the CAmkES template
|
||||||
|
// forces extraCaps=1 when constructing the MessageInfo struct
|
||||||
|
// used by the seL4_Call inside memory_alloc.
|
||||||
|
seL4_SetCap(0, request.cnode);
|
||||||
|
|
||||||
|
memory_alloc(raw_data.len() as u32, raw_data.as_ptr()).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(sleffler): is anyone going to use these convience wrappers given
|
||||||
|
// the objects are returned inside a CNode (so it's way more convenient
|
||||||
|
// to allocate 'em all togerher and then move the CNode to a TCB)?
|
||||||
|
|
||||||
|
// XXX need real allocator
|
||||||
|
static mut EMPTY_SLOT: seL4_CPtr = 30; // Next empty slot in debug_console_cnode XXX
|
||||||
|
|
||||||
|
impl ObjDescBundle {
|
||||||
|
// Move objects from |src_cnode| to our top-levbel CSpace and mutate
|
||||||
|
// the Object Descriptor with adjusted cptr's.
|
||||||
|
// TODO(sleffler) make generic?
|
||||||
|
fn move_objects_to_toplevel(
|
||||||
|
&mut self,
|
||||||
|
dest_cnode: seL4_CPtr,
|
||||||
|
dest_depth: u8,
|
||||||
|
) -> Result<(), MemoryManagerError> {
|
||||||
|
unsafe {
|
||||||
|
let mut dest_slot = EMPTY_SLOT;
|
||||||
|
for od in &mut self.objs {
|
||||||
|
for offset in 0..od.retype_count() {
|
||||||
|
// XXX cleanup on error?
|
||||||
|
seL4_CNode_Move(
|
||||||
|
/*root=*/ dest_cnode,
|
||||||
|
/*dest_index=*/ dest_slot,
|
||||||
|
/*dest_depth=*/ dest_depth,
|
||||||
|
/*src_root=*/ self.cnode,
|
||||||
|
/*src_index=*/ od.cptr + offset,
|
||||||
|
/*src_depth=*/ self.depth,
|
||||||
|
).map_err(|_| MemoryManagerError::MmeObjCapInvalid)?;
|
||||||
|
dest_slot += 1;
|
||||||
|
}
|
||||||
|
od.cptr = dest_slot - od.retype_count();
|
||||||
|
}
|
||||||
|
EMPTY_SLOT = dest_slot;
|
||||||
|
}
|
||||||
|
self.cnode = dest_cnode;
|
||||||
|
self.depth = dest_depth;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn kata_untyped_alloc(space_bytes: usize)
|
||||||
|
-> Result<ObjDescBundle, MemoryManagerError>
|
||||||
|
{
|
||||||
|
let mut objs = ObjDescBundle::new(
|
||||||
|
unsafe { RECV_CNODE },
|
||||||
|
unsafe { RECV_CNODE_DEPTH },
|
||||||
|
vec![ ObjDesc::new(seL4_UntypedObject, space_bytes, /*cptr=*/ 0) ],
|
||||||
|
);
|
||||||
|
kata_object_alloc(&objs)?;
|
||||||
|
objs.move_objects_to_toplevel(unsafe { SELF_CNODE }, seL4_WordBits as u8)?;
|
||||||
|
Ok(objs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn kata_tcb_alloc() -> Result<ObjDescBundle, MemoryManagerError> {
|
||||||
|
let mut objs = ObjDescBundle::new(
|
||||||
|
unsafe { RECV_CNODE },
|
||||||
|
unsafe { RECV_CNODE_DEPTH },
|
||||||
|
vec![ ObjDesc::new(seL4_TCBObject, 1, /*cptr=*/ 0) ],
|
||||||
|
);
|
||||||
|
kata_object_alloc(&objs)?;
|
||||||
|
objs.move_objects_to_toplevel(unsafe { SELF_CNODE }, seL4_WordBits as u8)?;
|
||||||
|
Ok(objs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn kata_endpoint_alloc() -> Result<ObjDescBundle, MemoryManagerError> {
|
||||||
|
let mut objs = ObjDescBundle::new(
|
||||||
|
unsafe { RECV_CNODE },
|
||||||
|
unsafe { RECV_CNODE_DEPTH },
|
||||||
|
vec![ ObjDesc::new(seL4_EndpointObject, 1, /*cptr=*/ 0) ],
|
||||||
|
);
|
||||||
|
kata_object_alloc(&objs)?;
|
||||||
|
objs.move_objects_to_toplevel(unsafe { SELF_CNODE }, seL4_WordBits as u8)?;
|
||||||
|
Ok(objs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn kata_notification_alloc() -> Result<ObjDescBundle, MemoryManagerError> {
|
||||||
|
let mut objs = ObjDescBundle::new(
|
||||||
|
unsafe { RECV_CNODE },
|
||||||
|
unsafe { RECV_CNODE_DEPTH },
|
||||||
|
vec![ ObjDesc::new(seL4_NotificationObject, 1, /*cptr=*/ 0) ],
|
||||||
|
);
|
||||||
|
kata_object_alloc(&objs)?;
|
||||||
|
objs.move_objects_to_toplevel(unsafe { SELF_CNODE }, seL4_WordBits as u8)?;
|
||||||
|
Ok(objs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
// |size_bits| is the log2 of the #slots to allocate.
|
||||||
|
pub fn kata_cnode_alloc(size_bits: usize) -> Result<ObjDescBundle, MemoryManagerError>
|
||||||
|
{
|
||||||
|
let mut objs = ObjDescBundle::new(
|
||||||
|
unsafe { RECV_CNODE },
|
||||||
|
unsafe { RECV_CNODE_DEPTH },
|
||||||
|
vec![ ObjDesc::new(seL4_CapTableObject, size_bits, /*cptr=*/ 0 )],
|
||||||
|
);
|
||||||
|
kata_object_alloc(&objs)?;
|
||||||
|
objs.move_objects_to_toplevel(unsafe { SELF_CNODE }, seL4_WordBits as u8)?;
|
||||||
|
Ok(objs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "CONFIG_KERNEL_MCS")]
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn kata_sched_context_alloc(size_bits: usize) -> Result<ObjDescBundle, MemoryManagerError>
|
||||||
|
{
|
||||||
|
let mut objs = ObjDescBundle::new(
|
||||||
|
unsafe { RECV_CNODE },
|
||||||
|
unsafe { RECV_CNODE_DEPTH },
|
||||||
|
vec![ ObjDesc::new(seL4_SchedContextObject, size_bits, /*cptr=*/ 0) ],
|
||||||
|
);
|
||||||
|
kata_object_alloc(&objs)?;
|
||||||
|
objs.move_objects_to_toplevel(unsafe { SELF_CNODE }, seL4_WordBits as u8)?;
|
||||||
|
Ok(objs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "CONFIG_KERNEL_MCS")]
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn kata_reply_alloc() -> Result<ObjDescBundle, MemoryManagerError>
|
||||||
|
{
|
||||||
|
let mut objs = ObjDescBundle::new(
|
||||||
|
unsafe { RECV_CNODE },
|
||||||
|
unsafe { RECV_CNODE_DEPTH },
|
||||||
|
vec![ ObjDesc::new(seL4_ReplyObject, 1, /*cptr=*/ 0) ],
|
||||||
|
);
|
||||||
|
kata_object_alloc(&objs)?;
|
||||||
|
objs.move_objects_to_toplevel(unsafe { SELF_CNODE }, seL4_WordBits as u8)?;
|
||||||
|
Ok(objs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
// Wrapper for allocating 4K pages.
|
||||||
|
pub fn kata_frame_alloc(space_bytes: usize) -> Result<ObjDescBundle, MemoryManagerError>
|
||||||
|
{
|
||||||
|
fn howmany(value: usize, unit: usize) -> usize {
|
||||||
|
(value + (unit - 1)) / unit
|
||||||
|
}
|
||||||
|
// NB: always allocate 4K pages
|
||||||
|
let mut objs = ObjDescBundle::new(
|
||||||
|
unsafe { RECV_CNODE },
|
||||||
|
unsafe { RECV_CNODE_DEPTH },
|
||||||
|
vec![ ObjDesc::new(
|
||||||
|
seL4_RISCV_4K_Page,
|
||||||
|
howmany(space_bytes, 1 << seL4_PageBits),
|
||||||
|
/*cptr=*/ 0,
|
||||||
|
)],
|
||||||
|
);
|
||||||
|
kata_object_alloc(&objs)?;
|
||||||
|
objs.move_objects_to_toplevel(unsafe { SELF_CNODE }, seL4_WordBits as u8)?;
|
||||||
|
Ok(objs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn kata_page_table_alloc() -> Result<ObjDescBundle, MemoryManagerError> {
|
||||||
|
let mut objs = ObjDescBundle::new(
|
||||||
|
unsafe { RECV_CNODE },
|
||||||
|
unsafe { RECV_CNODE_DEPTH },
|
||||||
|
vec![ ObjDesc::new(seL4_RISCV_PageTableObject, 1, /*cptr=*/ 0) ],
|
||||||
|
);
|
||||||
|
kata_object_alloc(&objs)?;
|
||||||
|
objs.move_objects_to_toplevel(unsafe { SELF_CNODE }, seL4_WordBits as u8)?;
|
||||||
|
Ok(objs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(sleffler): other objects, esp. vm stuff
|
||||||
|
|
||||||
|
pub fn kata_object_free(
|
||||||
|
request: &ObjDescBundle,
|
||||||
|
) -> Result<(), MemoryManagerError> {
|
||||||
|
extern "C" {
|
||||||
|
// NB: this assumes the MemoryManager component is named "memory".
|
||||||
|
fn memory_free(c_data_len: u32, c_data: *const u8) -> MemoryManagerError;
|
||||||
|
}
|
||||||
|
trace!("kata_object_free {:?}", request); // XXX
|
||||||
|
let raw_data = &mut [0u8; RAW_OBJ_DESC_DATA_SIZE];
|
||||||
|
postcard::to_slice(request, &mut raw_data[..])
|
||||||
|
.map_err(|_| MemoryManagerError::MmeSerializeFailed)?;
|
||||||
|
unsafe {
|
||||||
|
// Attach our CNode for returning objects; the CAmkES template
|
||||||
|
// forces extraCaps=1 when constructing the MessageInfo struct
|
||||||
|
// used in the seL4_Call.
|
||||||
|
seL4_SetCap(0, request.cnode);
|
||||||
|
|
||||||
|
memory_free(raw_data.len() as u32, raw_data.as_ptr()).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjDescBundle {
|
||||||
|
// Move objects from our top-level CSpace to |dest_cnode| and return
|
||||||
|
// mutate Object Descriptor with adjusted cptr's.
|
||||||
|
fn move_objects_from_toplevel(
|
||||||
|
&mut self,
|
||||||
|
dest_cnode: seL4_CPtr,
|
||||||
|
dest_depth: u8,
|
||||||
|
) -> Result<(), MemoryManagerError> {
|
||||||
|
unsafe {
|
||||||
|
let mut dest_slot = 0;
|
||||||
|
for od in &mut self.objs {
|
||||||
|
for offset in 0..od.retype_count() {
|
||||||
|
// XXX cleanup on error?
|
||||||
|
seL4_CNode_Move(
|
||||||
|
/*root=*/ dest_cnode,
|
||||||
|
/*dest_index=*/ dest_slot,
|
||||||
|
/*dest_depth=*/ dest_depth,
|
||||||
|
/*src_root=*/ self.cnode,
|
||||||
|
/*src_index=*/ od.cptr + offset,
|
||||||
|
/*src_depth=*/ self.depth,
|
||||||
|
).map_err(|_| MemoryManagerError::MmeObjCapInvalid)?;
|
||||||
|
dest_slot += 1;
|
||||||
|
}
|
||||||
|
// XXX assumes od.cptr's are sequential
|
||||||
|
od.cptr = dest_slot - od.retype_count();
|
||||||
|
EMPTY_SLOT -= od.retype_count(); // XXX assumes no intervening alloc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.cnode = dest_cnode;
|
||||||
|
self.depth = dest_depth;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn kata_object_free_toplevel(objs: &ObjDescBundle) -> Result<(), MemoryManagerError> {
|
||||||
|
let mut objs_mut = objs.clone();
|
||||||
|
objs_mut.move_objects_from_toplevel(
|
||||||
|
unsafe { RECV_CNODE },
|
||||||
|
unsafe { RECV_CNODE_DEPTH }
|
||||||
|
)?;
|
||||||
|
kata_object_free(&objs_mut)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn kata_memory_stats() -> Result<MemoryManagerStats, MemoryManagerError> {
|
||||||
|
extern "C" {
|
||||||
|
// NB: this assumes the MemoryManager component is named "memory".
|
||||||
|
fn memory_stats(c_data: *mut RawMemoryStatsData) -> MemoryManagerError;
|
||||||
|
}
|
||||||
|
let raw_data = &mut [0u8; RAW_MEMORY_STATS_DATA_SIZE];
|
||||||
|
match unsafe { memory_stats(raw_data as *mut _) }.into() {
|
||||||
|
MemoryManagerError::MmeSuccess => {
|
||||||
|
let stats = postcard::from_bytes::<MemoryManagerStats>(raw_data)
|
||||||
|
.map_err(|_| MemoryManagerError::MmeDeserializeFailed)?;
|
||||||
|
Ok(stats)
|
||||||
|
}
|
||||||
|
status => Err(status),
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "kata-memory-manager"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Kata OS MemoryManager service"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
kata-allocator = { path = "../../DebugConsole/kata-allocator" }
|
||||||
|
kata-os-common = { path = "../../kata-os-common" }
|
||||||
|
kata-memory-interface = { path = "../kata-memory-interface" }
|
||||||
|
log = "0.4"
|
||||||
|
smallvec = "1.2"
|
||||||
|
spin = "0.9"
|
@@ -0,0 +1,50 @@
|
|||||||
|
//! Kata OS global memory management support
|
||||||
|
|
||||||
|
#![cfg_attr(not(test), no_std)]
|
||||||
|
|
||||||
|
use core::ops::Range;
|
||||||
|
use kata_memory_interface::ObjDescBundle;
|
||||||
|
use kata_memory_interface::MemoryError;
|
||||||
|
use kata_memory_interface::MemoryManagerInterface;
|
||||||
|
use kata_memory_interface::MemoryManagerStats;
|
||||||
|
use kata_os_common::sel4_sys;
|
||||||
|
use sel4_sys::seL4_CPtr;
|
||||||
|
use sel4_sys::seL4_UntypedDesc;
|
||||||
|
use spin::Mutex;
|
||||||
|
|
||||||
|
mod memory_manager;
|
||||||
|
pub use memory_manager::MemoryManager;
|
||||||
|
|
||||||
|
// KataMemoryManager bundles an instance of the MemoryManager 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_MEMORY static
|
||||||
|
// and MemoryManager is incapable of supplying a const fn due it's use of
|
||||||
|
// hashbrown::HashMap.
|
||||||
|
pub struct KataMemoryManager {
|
||||||
|
manager: Mutex<Option<MemoryManager>>,
|
||||||
|
}
|
||||||
|
impl KataMemoryManager {
|
||||||
|
// Constructs a partially-initialized instance; to complete call init().
|
||||||
|
pub const fn empty() -> KataMemoryManager {
|
||||||
|
KataMemoryManager {
|
||||||
|
manager: Mutex::new(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finishes the setup started by empty():
|
||||||
|
pub fn init(&self, ut_slots: Range<seL4_CPtr>, untypeds: &[seL4_UntypedDesc]) {
|
||||||
|
*self.manager.lock() = Some(MemoryManager::new(ut_slots, untypeds));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// These just lock accesses and handle the necessary indirection.
|
||||||
|
impl MemoryManagerInterface for KataMemoryManager {
|
||||||
|
fn alloc(&mut self, objs: &ObjDescBundle) -> Result<(), MemoryError> {
|
||||||
|
self.manager.lock().as_mut().unwrap().alloc(objs)
|
||||||
|
}
|
||||||
|
fn free(&mut self, objs: &ObjDescBundle) -> Result<(), MemoryError> {
|
||||||
|
self.manager.lock().as_mut().unwrap().free(objs)
|
||||||
|
}
|
||||||
|
fn stats(&self) -> Result<MemoryManagerStats, MemoryError> {
|
||||||
|
self.manager.lock().as_ref().unwrap().stats()
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,263 @@
|
|||||||
|
//! Kata OS global memory management support
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
use core::ops::Range;
|
||||||
|
use kata_memory_interface::ObjDesc;
|
||||||
|
use kata_memory_interface::ObjDescBundle;
|
||||||
|
use kata_memory_interface::MemoryError;
|
||||||
|
use kata_memory_interface::MemoryManagerInterface;
|
||||||
|
use kata_memory_interface::MemoryManagerStats;
|
||||||
|
use kata_os_common::sel4_sys;
|
||||||
|
use log::{debug, error};
|
||||||
|
use sel4_sys::seL4_CPtr;
|
||||||
|
use sel4_sys::seL4_CNode_Delete;
|
||||||
|
use sel4_sys::seL4_Error;
|
||||||
|
use sel4_sys::seL4_Result;
|
||||||
|
use sel4_sys::seL4_Word;
|
||||||
|
use sel4_sys::seL4_UntypedDesc;
|
||||||
|
use sel4_sys::seL4_Untyped_Retype;
|
||||||
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
// SmallVec capacity for untyped memory slabs. There are two instances;
|
||||||
|
// one for anonymous memory and one for device-backed memory. The memory
|
||||||
|
// manager is expected to be setup as a static global so these data
|
||||||
|
// structures will land in .bss and only overflow to the heap if
|
||||||
|
// initialized with more than this count.
|
||||||
|
const UNTYPED_SLAB_CAPACITY: usize = 64; // # slabs kept inline
|
||||||
|
|
||||||
|
// The MemoryManager supports allocating & freeing seL4 objects that are
|
||||||
|
// instantiated from UntypedMemory "slabs". Allocation causes untyped memory
|
||||||
|
// to be converted to concrete types. Freeing deletes the specified capabilities
|
||||||
|
// and updates the bookkeeping. Note that a free only releases the specified
|
||||||
|
// cap; if there are dups or derived objects the memory will not be returned
|
||||||
|
// to the untyped slab from which it was allocated and the bookkeeping done
|
||||||
|
// here will be out of sync with the kernel.
|
||||||
|
// TODO(sleffler): support device-backed memory objects
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct UntypedSlab {
|
||||||
|
pub size_bits: usize, // XXX only used to sort
|
||||||
|
pub _base_paddr: seL4_Word, // Physical address of slab start
|
||||||
|
pub _last_paddr: seL4_Word, // Physical address of slab end
|
||||||
|
pub cptr: seL4_CPtr, // seL4 untyped object
|
||||||
|
pub _next_paddr: seL4_Word, // Physical address of next available chunk
|
||||||
|
}
|
||||||
|
impl UntypedSlab {
|
||||||
|
fn new(ut: &seL4_UntypedDesc, cptr: seL4_CPtr) -> Self {
|
||||||
|
UntypedSlab {
|
||||||
|
size_bits: ut.size_bits(),
|
||||||
|
_base_paddr: ut.paddr,
|
||||||
|
_last_paddr: ut.paddr + (1 << ut.size_bits()),
|
||||||
|
cptr: cptr,
|
||||||
|
_next_paddr: ut.paddr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn _size(&self) -> usize { l2tob(self.size_bits) }
|
||||||
|
fn size_bits(&self) -> usize { self.size_bits }
|
||||||
|
}
|
||||||
|
pub struct MemoryManager {
|
||||||
|
untypeds: SmallVec<[UntypedSlab; UNTYPED_SLAB_CAPACITY]>,
|
||||||
|
_device_untypeds: SmallVec<[UntypedSlab; UNTYPED_SLAB_CAPACITY]>,
|
||||||
|
cur_untyped: usize,
|
||||||
|
_cur_device_untyped: usize,
|
||||||
|
|
||||||
|
total_bytes: usize, // Total available space
|
||||||
|
allocated_bytes: usize, // Amount of space currently allocated
|
||||||
|
requested_bytes: usize, // Amount of space allocated over all time
|
||||||
|
|
||||||
|
allocated_objs: usize, // # seL4 objects currently allocated
|
||||||
|
requested_objs: usize, // # seL4 objects allocated over all time
|
||||||
|
|
||||||
|
// Retype requests failed due to insufficient available memory.
|
||||||
|
untyped_slab_too_small: usize,
|
||||||
|
|
||||||
|
// Alloc requests failed due to lack of untyped memory (NB: may be
|
||||||
|
// due to fragmentation of untyped slabs).
|
||||||
|
out_of_memory: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _howmany(value: usize, unit: usize) -> usize {
|
||||||
|
value + (unit - 1) / unit
|
||||||
|
}
|
||||||
|
fn _round_up(value: usize, align: usize) -> usize {
|
||||||
|
_howmany(value, align) * align
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log2 bits to bytes.
|
||||||
|
fn l2tob(size_bits: usize) -> usize { 1 << size_bits }
|
||||||
|
|
||||||
|
impl MemoryManager {
|
||||||
|
// Creates a new MemoryManager instance. The allocator is seeded
|
||||||
|
// from the untyped memory descriptors.
|
||||||
|
pub fn new(slots: Range<seL4_CPtr>, untypeds: &[seL4_UntypedDesc]) -> Self {
|
||||||
|
assert!(untypeds.len() > 0);
|
||||||
|
assert_eq!(slots.end - slots.start, untypeds.len());
|
||||||
|
let mut m = MemoryManager {
|
||||||
|
untypeds: SmallVec::new(),
|
||||||
|
_device_untypeds: SmallVec::new(),
|
||||||
|
cur_untyped: 0,
|
||||||
|
_cur_device_untyped: 0,
|
||||||
|
|
||||||
|
total_bytes: 0,
|
||||||
|
allocated_bytes: 0,
|
||||||
|
requested_bytes: 0,
|
||||||
|
|
||||||
|
allocated_objs: 0,
|
||||||
|
requested_objs: 0,
|
||||||
|
|
||||||
|
untyped_slab_too_small: 0,
|
||||||
|
out_of_memory: 0,
|
||||||
|
};
|
||||||
|
for (ut_index, ut) in untypeds.iter().enumerate() {
|
||||||
|
if ut.is_device() {
|
||||||
|
m._device_untypeds.push(UntypedSlab::new(ut, slots.start + ut_index));
|
||||||
|
} else {
|
||||||
|
m.untypeds.push(UntypedSlab::new(ut, slots.start + ut_index));
|
||||||
|
m.total_bytes += l2tob(ut.size_bits());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Sort non-device slabs by descending size.
|
||||||
|
m.untypeds.sort_unstable_by(|a, b| b.size_bits().cmp(&a.size_bits()));
|
||||||
|
m
|
||||||
|
}
|
||||||
|
|
||||||
|
// Total available space.
|
||||||
|
pub fn total_available_space(&self) -> usize {
|
||||||
|
self.total_bytes
|
||||||
|
}
|
||||||
|
// Current allocated space
|
||||||
|
pub fn allocated_space(&self) -> usize {
|
||||||
|
self.allocated_bytes
|
||||||
|
}
|
||||||
|
// Current free space.
|
||||||
|
pub fn free_space(&self) -> usize {
|
||||||
|
self.total_bytes - self.allocated_bytes
|
||||||
|
}
|
||||||
|
// Total space allocated over time
|
||||||
|
pub fn total_requested_space(&self) -> usize {
|
||||||
|
self.requested_bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current allocated objects
|
||||||
|
pub fn allocated_objs(&self) -> usize {
|
||||||
|
self.allocated_objs
|
||||||
|
}
|
||||||
|
// Total objects allocated over time
|
||||||
|
pub fn total_requested_objs(&self) -> usize {
|
||||||
|
self.requested_objs
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn untyped_slab_too_small(&self) -> usize {
|
||||||
|
self.untyped_slab_too_small
|
||||||
|
}
|
||||||
|
pub fn out_of_memory(&self) -> usize {
|
||||||
|
self.out_of_memory
|
||||||
|
}
|
||||||
|
|
||||||
|
fn retype_untyped(
|
||||||
|
free_untyped: seL4_CPtr,
|
||||||
|
root: seL4_CPtr,
|
||||||
|
obj: &ObjDesc,
|
||||||
|
) -> seL4_Result {
|
||||||
|
unsafe {
|
||||||
|
seL4_Untyped_Retype(
|
||||||
|
free_untyped,
|
||||||
|
/*type=*/ obj.type_.into(),
|
||||||
|
/*size_bits=*/ obj.retype_size_bits().unwrap(),
|
||||||
|
/*root=*/ root,
|
||||||
|
/*node_index=*/ 0, // Ignored 'cuz depth is zero
|
||||||
|
/*node_depth=*/ 0, // NB: store in cnode
|
||||||
|
/*node_offset=*/ obj.cptr,
|
||||||
|
/*num_objects=*/ obj.retype_count(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete_caps(root: seL4_CPtr, depth: u8, od: &ObjDesc) -> seL4_Result {
|
||||||
|
for offset in 0..od.retype_count() {
|
||||||
|
// XXX warn about errors?
|
||||||
|
unsafe { seL4_CNode_Delete(root, od.cptr + offset, depth) }?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryManagerInterface for MemoryManager {
|
||||||
|
fn alloc(&mut self, bundle: &ObjDescBundle) -> Result<(), MemoryError> {
|
||||||
|
// TODO(sleffler): split by device vs no-device (or allow mixing)
|
||||||
|
let first_ut = self.cur_untyped;
|
||||||
|
let mut ut_index = first_ut;
|
||||||
|
|
||||||
|
let mut allocated_bytes: usize = 0;
|
||||||
|
let mut allocated_objs: usize = 0;
|
||||||
|
|
||||||
|
for od in &bundle.objs {
|
||||||
|
// NB: we don't check slots are available (the kernel will tell us).
|
||||||
|
// XXX check size_bytes() against untyped slab? (depend on kernel for now)
|
||||||
|
while let Err(e) =
|
||||||
|
// XXX ASIDPool maps to UntypedObject?
|
||||||
|
MemoryManager::retype_untyped(self.untypeds[ut_index].cptr, bundle.cnode, od)
|
||||||
|
{
|
||||||
|
if e != seL4_Error::seL4_NotEnoughMemory {
|
||||||
|
// Should not happen.
|
||||||
|
// TODO(sleffler): reclaim allocations
|
||||||
|
error!("Allocation request failed (retype returned {:?})", e);
|
||||||
|
return Err(MemoryError::UnknownMemoryError);
|
||||||
|
}
|
||||||
|
// This untyped does not have enough available space, try
|
||||||
|
// the next slab until we exhaust all slabs. This is the best
|
||||||
|
// we can do without per-slab bookkeeping.
|
||||||
|
self.untyped_slab_too_small += 1;
|
||||||
|
ut_index = (ut_index + 1) % self.untypeds.len();
|
||||||
|
debug!("Advance to untyped slab {}", ut_index);
|
||||||
|
if ut_index == first_ut {
|
||||||
|
// TODO(sleffler): reclaim allocations
|
||||||
|
self.out_of_memory += 1;
|
||||||
|
debug!("Allocation request failed (out of space)");
|
||||||
|
return Err(MemoryError::AllocFailed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allocated_objs += od.retype_count();
|
||||||
|
allocated_bytes += od.size_bytes().unwrap();
|
||||||
|
|
||||||
|
}
|
||||||
|
self.cur_untyped = ut_index;
|
||||||
|
|
||||||
|
self.allocated_bytes += allocated_bytes;
|
||||||
|
self.allocated_objs += allocated_objs;
|
||||||
|
|
||||||
|
// NB: does not include requests that fail
|
||||||
|
self.requested_objs += allocated_objs;
|
||||||
|
self.requested_bytes += allocated_bytes;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn free(&mut self, bundle: &ObjDescBundle) -> Result<(), MemoryError> {
|
||||||
|
for od in &bundle.objs {
|
||||||
|
// XXX support leaving objects so client can do bulk reclaim on exit
|
||||||
|
// (maybe require cptr != 0)
|
||||||
|
if MemoryManager::delete_caps(bundle.cnode, bundle.depth, od).is_ok() {
|
||||||
|
// NB: atm we do not do per-untyped bookkeeping so just track
|
||||||
|
// global stats.
|
||||||
|
self.allocated_bytes -= od.size_bytes().ok_or(MemoryError::ObjTypeInvalid)?;
|
||||||
|
self.allocated_objs -= od.retype_count();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn stats(&self) -> Result<MemoryManagerStats, MemoryError> {
|
||||||
|
Ok(MemoryManagerStats {
|
||||||
|
allocated_bytes: self.allocated_space(),
|
||||||
|
free_bytes: self.free_space(),
|
||||||
|
total_requested_bytes: self.total_requested_space(),
|
||||||
|
// TODO(sleffler): track fragmentation
|
||||||
|
// NB: assumes all heap usage is for the buddy allocator
|
||||||
|
overhead_bytes: 0,
|
||||||
|
|
||||||
|
allocated_objs: self.allocated_objs(),
|
||||||
|
total_requested_objs: self.total_requested_objs(),
|
||||||
|
|
||||||
|
untyped_slab_too_small: self.untyped_slab_too_small(),
|
||||||
|
out_of_memory: self.out_of_memory(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
use cstr_core;
|
use cstr_core;
|
||||||
|
use sel4_sys::SEL4_BOOTINFO_HEADER_BOOTINFO;
|
||||||
use sel4_sys::SEL4_BOOTINFO_HEADER_FDT;
|
use sel4_sys::SEL4_BOOTINFO_HEADER_FDT;
|
||||||
use sel4_sys::SEL4_BOOTINFO_HEADER_PADDING;
|
use sel4_sys::SEL4_BOOTINFO_HEADER_PADDING;
|
||||||
use sel4_sys::SEL4_BOOTINFO_HEADER_X86_ACPI_RSDP;
|
use sel4_sys::SEL4_BOOTINFO_HEADER_X86_ACPI_RSDP;
|
||||||
@@ -461,6 +462,12 @@ impl From<CDL_ObjectType> for seL4_ObjectType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct CDL_CNodeExtraInfo {
|
||||||
|
pub has_untyped_memory: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct CDL_TCBExtraInfo {
|
pub struct CDL_TCBExtraInfo {
|
||||||
@@ -536,6 +543,7 @@ pub enum CDL_FrameFill_BootInfoEnum_t {
|
|||||||
CDL_FrameFill_BootInfo_X86_Framebuffer = SEL4_BOOTINFO_HEADER_X86_FRAMEBUFFER as isize,
|
CDL_FrameFill_BootInfo_X86_Framebuffer = SEL4_BOOTINFO_HEADER_X86_FRAMEBUFFER as isize,
|
||||||
CDL_FrameFill_BootInfo_X86_TSC_Freq = SEL4_BOOTINFO_HEADER_X86_TSC_FREQ as isize,
|
CDL_FrameFill_BootInfo_X86_TSC_Freq = SEL4_BOOTINFO_HEADER_X86_TSC_FREQ as isize,
|
||||||
CDL_FrameFill_BootInfo_FDT = SEL4_BOOTINFO_HEADER_FDT as isize,
|
CDL_FrameFill_BootInfo_FDT = SEL4_BOOTINFO_HEADER_FDT as isize,
|
||||||
|
CDL_FrameFill_BootInfo_BootInfo = SEL4_BOOTINFO_HEADER_BOOTINFO as isize,
|
||||||
}
|
}
|
||||||
impl From<CDL_FrameFill_BootInfoEnum_t> for usize {
|
impl From<CDL_FrameFill_BootInfoEnum_t> for usize {
|
||||||
fn from(bi_type: CDL_FrameFill_BootInfoEnum_t) -> usize {
|
fn from(bi_type: CDL_FrameFill_BootInfoEnum_t) -> usize {
|
||||||
@@ -666,6 +674,11 @@ impl<'a> CDL_Object {
|
|||||||
self.paddr().map_or(false, |v| v != 0)
|
self.paddr().map_or(false, |v| v != 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn cnode_has_untyped_memory(&self) -> bool {
|
||||||
|
unsafe { self.extra.cnode_extra.has_untyped_memory }
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(sleffler): maybe assert type_ before referencing union members
|
// TODO(sleffler): maybe assert type_ before referencing union members
|
||||||
// NB: return everything as seL4_Word to minimize conversions
|
// NB: return everything as seL4_Word to minimize conversions
|
||||||
#[inline]
|
#[inline]
|
||||||
@@ -804,6 +817,7 @@ pub union CDL_ObjectExtra {
|
|||||||
pub msiirq_extra: CDL_MSIIRQExtraInfo,
|
pub msiirq_extra: CDL_MSIIRQExtraInfo,
|
||||||
pub armirq_extra: CDL_ARMIRQExtraInfo,
|
pub armirq_extra: CDL_ARMIRQExtraInfo,
|
||||||
pub frame_extra: CDL_FrameExtraInfo,
|
pub frame_extra: CDL_FrameExtraInfo,
|
||||||
|
pub cnode_extra: CDL_CNodeExtraInfo,
|
||||||
|
|
||||||
// Physical address; only defined for untyped objects.
|
// Physical address; only defined for untyped objects.
|
||||||
pub paddr: seL4_Word,
|
pub paddr: seL4_Word,
|
||||||
|
@@ -2,9 +2,11 @@
|
|||||||
|
|
||||||
use crate::KataOsModel;
|
use crate::KataOsModel;
|
||||||
use capdl::kobject_t::KOBJECT_FRAME;
|
use capdl::kobject_t::KOBJECT_FRAME;
|
||||||
|
use capdl::CDL_FrameFillType_t::*;
|
||||||
|
use capdl::CDL_FrameFill_BootInfoEnum_t::*;
|
||||||
use capdl::CDL_ObjectType::*;
|
use capdl::CDL_ObjectType::*;
|
||||||
use capdl::*;
|
use capdl::*;
|
||||||
use log::{debug, trace};
|
use log::{debug, info, trace};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use sel4_sys::seL4_BootInfo;
|
use sel4_sys::seL4_BootInfo;
|
||||||
@@ -58,6 +60,10 @@ impl<'a> KataOsModel<'a> {
|
|||||||
// possibility of vpsace_roots spilling to the heap.
|
// possibility of vpsace_roots spilling to the heap.
|
||||||
let mut roots = SmallVec::new();
|
let mut roots = SmallVec::new();
|
||||||
|
|
||||||
|
// Record objects that receive special treatment later on.
|
||||||
|
let mut bootinfo_frame: CDL_ObjID = CDL_ObjID::MAX;
|
||||||
|
let mut untyped_cnode: CDL_ObjID = CDL_ObjID::MAX;
|
||||||
|
|
||||||
// First, allocate most objects and record the cslot locations.
|
// First, allocate most objects and record the cslot locations.
|
||||||
// The exception is ASIDPools, where create_object only allocates
|
// The exception is ASIDPools, where create_object only allocates
|
||||||
// the backing untypeds.
|
// the backing untypeds.
|
||||||
@@ -90,11 +96,40 @@ impl<'a> KataOsModel<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Capture VSpace roots for later use.
|
match obj.r#type() {
|
||||||
if obj.r#type() == CDL_TCB {
|
// Capture VSpace roots & TCBs for later use.
|
||||||
if let Some(root_cap) = obj.get_cap_at(CDL_TCB_VTable_Slot) {
|
CDL_TCB => {
|
||||||
roots.push(root_cap.obj_id);
|
if let Some(root_cap) = obj.get_cap_at(CDL_TCB_VTable_Slot) {
|
||||||
|
roots.push(root_cap.obj_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// Capture one bootinfo frame for processing below.
|
||||||
|
CDL_Frame => {
|
||||||
|
fn is_bootinfo_frame(obj: &CDL_Object) -> bool {
|
||||||
|
let frame_fill = &obj.frame_fill(0).unwrap();
|
||||||
|
frame_fill.type_ == CDL_FrameFill_BootInfo &&
|
||||||
|
frame_fill.get_bootinfo().type_ == CDL_FrameFill_BootInfo_BootInfo
|
||||||
|
}
|
||||||
|
if is_bootinfo_frame(obj) {
|
||||||
|
// NB: can instantiate multiple frames but only one
|
||||||
|
// CNode can receive the untypeds since we must move
|
||||||
|
// 'em from the rootserver (since they are "derived").
|
||||||
|
// XXX maybe just complain & ignore
|
||||||
|
info!("Found bootinfo Frame at {}", obj_id);
|
||||||
|
assert!(!is_objid_valid(bootinfo_frame));
|
||||||
|
bootinfo_frame = obj_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Look for a CNode associated with any bootinfo frame.
|
||||||
|
CDL_CNode => {
|
||||||
|
if obj.cnode_has_untyped_memory() {
|
||||||
|
if is_objid_valid(untyped_cnode) {
|
||||||
|
info!("Duplicate bootinfo cnode at {}, prev {}", obj_id, untyped_cnode);
|
||||||
|
}
|
||||||
|
untyped_cnode = obj_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
// Record the cslot assigned to the object.
|
// Record the cslot assigned to the object.
|
||||||
self.set_orig_cap(obj_id, free_slot);
|
self.set_orig_cap(obj_id, free_slot);
|
||||||
@@ -140,6 +175,15 @@ impl<'a> KataOsModel<'a> {
|
|||||||
roots.dedup();
|
roots.dedup();
|
||||||
self.vspace_roots = roots;
|
self.vspace_roots = roots;
|
||||||
|
|
||||||
|
// Record any CNode designated to receive the UntypedMemory caps when
|
||||||
|
// constructing their CSpace.
|
||||||
|
// NB: we conditionally assign based on there being a BootInfo frame
|
||||||
|
// because the UntypedMemory caps are not useful w/o the descriptors.
|
||||||
|
if is_objid_valid(bootinfo_frame) {
|
||||||
|
assert!(is_objid_valid(untyped_cnode));
|
||||||
|
self.untyped_cnode = untyped_cnode;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -19,7 +19,7 @@ use core::mem::size_of;
|
|||||||
use core::ptr;
|
use core::ptr;
|
||||||
use cpio::CpioNewcReader;
|
use cpio::CpioNewcReader;
|
||||||
use cstr_core::CStr;
|
use cstr_core::CStr;
|
||||||
use log::{debug, error, trace};
|
use log::{debug, info, error, trace};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use static_assertions::*;
|
use static_assertions::*;
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ const_assert!(seL4_WordBits == 32 || seL4_WordBits == 64);
|
|||||||
|
|
||||||
const CONFIG_CAPDL_LOADER_FILLS_PER_FRAME: usize = 1;
|
const CONFIG_CAPDL_LOADER_FILLS_PER_FRAME: usize = 1;
|
||||||
|
|
||||||
fn _BIT(bit_num: usize) -> usize { 1 << bit_num }
|
fn BIT(bit_num: usize) -> usize { 1 << bit_num }
|
||||||
fn ROUND_UP(a: usize, b: usize) -> usize {
|
fn ROUND_UP(a: usize, b: usize) -> usize {
|
||||||
if (a % b) == 0 {
|
if (a % b) == 0 {
|
||||||
a
|
a
|
||||||
@@ -193,6 +193,8 @@ pub struct KataOsModel<'a> {
|
|||||||
#[cfg(feature = "CONFIG_ARM_SMMU")]
|
#[cfg(feature = "CONFIG_ARM_SMMU")]
|
||||||
sid_number: usize,
|
sid_number: usize,
|
||||||
|
|
||||||
|
untyped_cnode: CDL_ObjID,
|
||||||
|
|
||||||
extended_bootinfo_table: [*const seL4_BootInfoHeader; SEL4_BOOTINFO_HEADER_NUM],
|
extended_bootinfo_table: [*const seL4_BootInfoHeader; SEL4_BOOTINFO_HEADER_NUM],
|
||||||
|
|
||||||
vspace_roots: SmallVec<[CDL_ObjID; 32]>, // NB: essentially #components
|
vspace_roots: SmallVec<[CDL_ObjID; 32]>, // NB: essentially #components
|
||||||
@@ -219,6 +221,8 @@ impl<'a> KataOsModel<'a> {
|
|||||||
#[cfg(feature = "CONFIG_ARM_SMMU")]
|
#[cfg(feature = "CONFIG_ARM_SMMU")]
|
||||||
sid_number: 0,
|
sid_number: 0,
|
||||||
|
|
||||||
|
untyped_cnode: CDL_ObjID::MAX,
|
||||||
|
|
||||||
extended_bootinfo_table: [ptr::null(); SEL4_BOOTINFO_HEADER_NUM],
|
extended_bootinfo_table: [ptr::null(); SEL4_BOOTINFO_HEADER_NUM],
|
||||||
|
|
||||||
vspace_roots: SmallVec::new(),
|
vspace_roots: SmallVec::new(),
|
||||||
@@ -255,6 +259,19 @@ impl<'a> KataOsModel<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handoff_capabilities(&mut self) -> seL4_Result {
|
||||||
|
// Hand-off capabilities needed to dynamically create seL4 objects.
|
||||||
|
// The MemoryManager needs the UntypedMemory slabs to instantiate
|
||||||
|
// objects. The ProcessManager needs various other object(s) to
|
||||||
|
// configure TCB's but those are expressed with capDL and moved
|
||||||
|
// as part of the work done by init_cspsace.
|
||||||
|
|
||||||
|
if is_objid_valid(self.untyped_cnode) {
|
||||||
|
self.handoff_untypeds(self.untyped_cnode)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn start_threads(&self) -> seL4_Result {
|
pub fn start_threads(&self) -> seL4_Result {
|
||||||
trace!("Starting threads...");
|
trace!("Starting threads...");
|
||||||
for (obj_id, obj) in self.spec.obj_slice().iter().enumerate() {
|
for (obj_id, obj) in self.spec.obj_slice().iter().enumerate() {
|
||||||
@@ -468,6 +485,7 @@ impl<'a> KataOsModel<'a> {
|
|||||||
let endpoint_obj = endpoint_cap.obj_id;
|
let endpoint_obj = endpoint_cap.obj_id;
|
||||||
let badge = endpoint_cap.cap_data();
|
let badge = endpoint_cap.cap_data();
|
||||||
let endpoint_cptr = if badge != 0 {
|
let endpoint_cptr = if badge != 0 {
|
||||||
|
// Endpoint needs badging, mint a new cap
|
||||||
self.mint_cap(endpoint_obj, badge)?
|
self.mint_cap(endpoint_obj, badge)?
|
||||||
} else {
|
} else {
|
||||||
self.get_orig_cap(endpoint_obj)
|
self.get_orig_cap(endpoint_obj)
|
||||||
@@ -554,7 +572,8 @@ impl<'a> KataOsModel<'a> {
|
|||||||
match bi.type_ {
|
match bi.type_ {
|
||||||
CDL_FrameFill_BootInfo_X86_VBE
|
CDL_FrameFill_BootInfo_X86_VBE
|
||||||
| CDL_FrameFill_BootInfo_X86_TSC_Freq
|
| CDL_FrameFill_BootInfo_X86_TSC_Freq
|
||||||
| CDL_FrameFill_BootInfo_FDT => {}
|
| CDL_FrameFill_BootInfo_FDT
|
||||||
|
| CDL_FrameFill_BootInfo_BootInfo => {}
|
||||||
_ => {
|
_ => {
|
||||||
panic!("Unsupported bootinfo frame fill type {:?}", bi.type_)
|
panic!("Unsupported bootinfo frame fill type {:?}", bi.type_)
|
||||||
}
|
}
|
||||||
@@ -566,8 +585,10 @@ impl<'a> KataOsModel<'a> {
|
|||||||
let header: *const seL4_BootInfoHeader =
|
let header: *const seL4_BootInfoHeader =
|
||||||
self.extended_bootinfo_table[usize::from(bi.type_)];
|
self.extended_bootinfo_table[usize::from(bi.type_)];
|
||||||
|
|
||||||
/* Check if the bootinfo has been found */
|
// Check if the bootinfo has been found.
|
||||||
if header.is_null() {
|
if bi.type_ == CDL_FrameFill_BootInfo_BootInfo {
|
||||||
|
self.fill_with_bootinfo(dest, max_len);
|
||||||
|
} else if header.is_null() {
|
||||||
/* No bootinfo.
|
/* No bootinfo.
|
||||||
* If we're at the start of a block, copy an empty header across, otherwise skip the copy */
|
* If we're at the start of a block, copy an empty header across, otherwise skip the copy */
|
||||||
if block_offset == 0 {
|
if block_offset == 0 {
|
||||||
@@ -595,6 +616,35 @@ impl<'a> KataOsModel<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fill with our BootInfo patched per the destination cnode.
|
||||||
|
fn fill_with_bootinfo(&self, dest: *mut u8, max_len: usize) {
|
||||||
|
unsafe {
|
||||||
|
ptr::copy_nonoverlapping(
|
||||||
|
ptr::addr_of!(self.bootinfo.extraLen) as *const u8, dest as _, max_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(is_objid_valid(self.untyped_cnode));
|
||||||
|
let cnode = &self.spec.obj_slice()[self.untyped_cnode];
|
||||||
|
assert_eq!(cnode.r#type(), CDL_CNode);
|
||||||
|
|
||||||
|
// NB: page-aligned so safe to deref.
|
||||||
|
let bootinfo = unsafe { &mut *(dest as *mut seL4_BootInfo) };
|
||||||
|
|
||||||
|
// UntypedMemory caps are appended to the specified slots.
|
||||||
|
bootinfo.untyped = sel4_sys::seL4_SlotRegion {
|
||||||
|
start: cnode.num_slots() + 1,
|
||||||
|
end: cnode.num_slots() + 1 +
|
||||||
|
(self.bootinfo.untyped.end - self.bootinfo.untyped.start),
|
||||||
|
};
|
||||||
|
// Update the empty region to support dynamic cap allocation.
|
||||||
|
bootinfo.empty = sel4_sys::seL4_SlotRegion {
|
||||||
|
start: bootinfo.untyped.end + 1,
|
||||||
|
end: BIT(cnode.size_bits()),
|
||||||
|
};
|
||||||
|
// NB: we could adjust the UntypedDesc's but they will not
|
||||||
|
// reflect rootserver resources that are released at exit
|
||||||
|
}
|
||||||
|
|
||||||
// Fill a frame's contents from a file in the cpio archive;
|
// Fill a frame's contents from a file in the cpio archive;
|
||||||
// in particular this loads each CAmkES component's executable.
|
// in particular this loads each CAmkES component's executable.
|
||||||
fn fill_frame_with_filedata(&self, base: usize, frame_fill: &CDL_FrameFill_Element_t) {
|
fn fill_frame_with_filedata(&self, base: usize, frame_fill: &CDL_FrameFill_Element_t) {
|
||||||
@@ -880,6 +930,53 @@ impl<'a> KataOsModel<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handoff_untypeds(&mut self, cnode_obj_id: CDL_ObjID) -> seL4_Result {
|
||||||
|
let num_untypeds = self.bootinfo.untyped.end - self.bootinfo.untyped.start;
|
||||||
|
|
||||||
|
// NB: UntypedMemory caps are appended to the CAmkES-generated slots
|
||||||
|
let dest_start = self.spec.obj_slice()[cnode_obj_id].num_slots() + 1;
|
||||||
|
|
||||||
|
info!("Hand-off {} untypeds from {} to {}",
|
||||||
|
num_untypeds,
|
||||||
|
self.bootinfo.untyped.start,
|
||||||
|
dest_start);
|
||||||
|
// NB: we let kernel tell us if the CNode is too small.
|
||||||
|
for ut in 0..num_untypeds {
|
||||||
|
self.handoff_cap(cnode_obj_id,
|
||||||
|
/*src_index=*/ self.bootinfo.untyped.start + ut,
|
||||||
|
/*dest_index=*/ dest_start + ut)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handoff_cap(
|
||||||
|
&mut self,
|
||||||
|
cnode_obj_id: CDL_ObjID,
|
||||||
|
src_index: seL4_CPtr,
|
||||||
|
dest_index: seL4_CPtr
|
||||||
|
) -> seL4_Result {
|
||||||
|
let cnode = &self.spec.obj_slice()[cnode_obj_id];
|
||||||
|
assert_eq!(cnode.r#type(), CDL_CNode);
|
||||||
|
|
||||||
|
let src_root = seL4_CapInitThreadCNode;
|
||||||
|
let src_depth = seL4_WordBits as u8;
|
||||||
|
|
||||||
|
// Blindly use the dup'd cap a la init_cnode_slot.
|
||||||
|
let dest_root = self.get_dup_cap(cnode_obj_id);
|
||||||
|
let dest_depth: u8 = cnode.size_bits.try_into().unwrap();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
seL4_CNode_Move(
|
||||||
|
dest_root,
|
||||||
|
dest_index,
|
||||||
|
dest_depth,
|
||||||
|
src_root,
|
||||||
|
src_index,
|
||||||
|
src_depth,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn init_cnode_slot(
|
fn init_cnode_slot(
|
||||||
&self,
|
&self,
|
||||||
mode: InitCnodeCmode,
|
mode: InitCnodeCmode,
|
||||||
|
@@ -377,4 +377,5 @@ pub const SEL4_BOOTINFO_HEADER_X86_ACPI_RSDP: usize = 3;
|
|||||||
pub const SEL4_BOOTINFO_HEADER_X86_FRAMEBUFFER: usize = 4;
|
pub const SEL4_BOOTINFO_HEADER_X86_FRAMEBUFFER: usize = 4;
|
||||||
pub const SEL4_BOOTINFO_HEADER_X86_TSC_FREQ: usize = 5;
|
pub const SEL4_BOOTINFO_HEADER_X86_TSC_FREQ: usize = 5;
|
||||||
pub const SEL4_BOOTINFO_HEADER_FDT: usize = 6;
|
pub const SEL4_BOOTINFO_HEADER_FDT: usize = 6;
|
||||||
pub const SEL4_BOOTINFO_HEADER_NUM: usize = SEL4_BOOTINFO_HEADER_FDT + 1;
|
pub const SEL4_BOOTINFO_HEADER_BOOTINFO: usize = 7; // Copy of rootserver's BootInfo
|
||||||
|
pub const SEL4_BOOTINFO_HEADER_NUM: usize = SEL4_BOOTINFO_HEADER_BOOTINFO + 1;
|
||||||
|
7
apps/system/interfaces/MemoryInterface.camkes
Normal file
7
apps/system/interfaces/MemoryInterface.camkes
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
procedure MemoryInterface {
|
||||||
|
include <MemoryManagerBindings.h>;
|
||||||
|
|
||||||
|
MemoryManagerError alloc(in char request[]);
|
||||||
|
MemoryManagerError free(in char request[]);
|
||||||
|
MemoryManagerError stats(out RawMemoryStatsData data);
|
||||||
|
};
|
33
apps/system/interfaces/MemoryManagerBindings.h
Normal file
33
apps/system/interfaces/MemoryManagerBindings.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#ifndef __MEMORY_MANAGER_BINDINGS_H__
|
||||||
|
#define __MEMORY_MANAGER_BINDINGS_H__
|
||||||
|
|
||||||
|
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
|
||||||
|
|
||||||
|
#define RAW_OBJ_DESC_DATA_SIZE 2048
|
||||||
|
|
||||||
|
#define RAW_MEMORY_STATS_DATA_SIZE 100
|
||||||
|
|
||||||
|
typedef enum MemoryManagerError {
|
||||||
|
MmeSuccess = 0,
|
||||||
|
MmeObjCountInvalid,
|
||||||
|
MmeObjTypeInvalid,
|
||||||
|
MmeObjCapInvalid,
|
||||||
|
MmeSerializeFailed,
|
||||||
|
MmeDeserializeFailed,
|
||||||
|
MmeUnknownError,
|
||||||
|
MmeAllocFailed,
|
||||||
|
MmeFreeFailed,
|
||||||
|
} MemoryManagerError;
|
||||||
|
|
||||||
|
typedef uint8_t RawMemoryStatsData[RAW_MEMORY_STATS_DATA_SIZE];
|
||||||
|
|
||||||
|
typedef struct MemoryManagerStats {
|
||||||
|
uintptr_t allocated_bytes;
|
||||||
|
uintptr_t free_bytes;
|
||||||
|
uintptr_t total_requested_bytes;
|
||||||
|
uintptr_t overhead_bytes;
|
||||||
|
uintptr_t allocated_objs;
|
||||||
|
uintptr_t total_requested_objs;
|
||||||
|
} MemoryManagerStats;
|
||||||
|
|
||||||
|
#endif /* __MEMORY_MANAGER_BINDINGS_H__ */
|
@@ -17,6 +17,7 @@ import "components/OpenTitanUARTDriver/OpenTitanUARTDriver.camkes";
|
|||||||
import "components/DebugConsole/DebugConsole.camkes";
|
import "components/DebugConsole/DebugConsole.camkes";
|
||||||
import "components/ProcessManager/ProcessManager.camkes";
|
import "components/ProcessManager/ProcessManager.camkes";
|
||||||
import "components/MlCoordinator/MlCoordinator.camkes";
|
import "components/MlCoordinator/MlCoordinator.camkes";
|
||||||
|
import "components/MemoryManager/MemoryManager.camkes";
|
||||||
import "components/StorageManager/StorageManager.camkes";
|
import "components/StorageManager/StorageManager.camkes";
|
||||||
import "components/SecurityCoordinator/SecurityCoordinator.camkes";
|
import "components/SecurityCoordinator/SecurityCoordinator.camkes";
|
||||||
|
|
||||||
@@ -54,6 +55,7 @@ assembly {
|
|||||||
component OpenTitanUART uart;
|
component OpenTitanUART uart;
|
||||||
component OpenTitanUARTDriver uart_driver;
|
component OpenTitanUARTDriver uart_driver;
|
||||||
|
|
||||||
|
component MemoryManager memory_manager;
|
||||||
component ProcessManager process_manager;
|
component ProcessManager process_manager;
|
||||||
component MlCoordinator ml_coordinator;
|
component MlCoordinator ml_coordinator;
|
||||||
component DebugConsole debug_console;
|
component DebugConsole debug_console;
|
||||||
@@ -98,6 +100,15 @@ assembly {
|
|||||||
connection seL4RPCCall shell_storage(from debug_console.storage,
|
connection seL4RPCCall shell_storage(from debug_console.storage,
|
||||||
to storage_manager.storage);
|
to storage_manager.storage);
|
||||||
|
|
||||||
|
// Connect the MemoryInterface to each component that needs to allocate
|
||||||
|
// global memory. Note this allocates a 4KB shared memory region to each
|
||||||
|
// component and copies data between components.
|
||||||
|
connection seL4RPCOverMultiSharedData multi_memory(
|
||||||
|
from debug_console.memory,
|
||||||
|
// TODO(sleffler): from process_manager.memory,
|
||||||
|
// TOOD(sleffler): from ml_coordinator.memory,
|
||||||
|
to memory_manager.memory);
|
||||||
|
|
||||||
// Connect the SecurityCoordinatorInterface to each component that needs
|
// Connect the SecurityCoordinatorInterface to each component that needs
|
||||||
// access to the Security Core. Note this allocates a 4KB shared memory
|
// access to the Security Core. Note this allocates a 4KB shared memory
|
||||||
// region to each component and copies data between components.
|
// region to each component and copies data between components.
|
||||||
@@ -124,6 +135,7 @@ assembly {
|
|||||||
connection seL4RPCOverMultiSharedData multi_logger(
|
connection seL4RPCOverMultiSharedData multi_logger(
|
||||||
from process_manager.logger,
|
from process_manager.logger,
|
||||||
from ml_coordinator.logger,
|
from ml_coordinator.logger,
|
||||||
|
from memory_manager.logger,
|
||||||
from security_coordinator.logger,
|
from security_coordinator.logger,
|
||||||
from storage_manager.logger,
|
from storage_manager.logger,
|
||||||
to debug_console.logger);
|
to debug_console.logger);
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
set(CAMKES_APP "system" CACHE STRING "The one and only CAmkES application in this project")
|
set(CAMKES_APP "system" CACHE STRING "The one and only CAmkES application in this project")
|
||||||
set(CAPDL_LOADER_APP "capdl-loader-app" CACHE STRING "")
|
#set(CAPDL_LOADER_APP "capdl-loader-app" CACHE STRING "")
|
||||||
#set(CAPDL_LOADER_APP "kata-os-rootserver" CACHE STRING "")
|
set(CAPDL_LOADER_APP "kata-os-rootserver" CACHE STRING "")
|
||||||
|
|
||||||
set(PLATFORM "sparrow" CACHE STRING "The one and only seL4 platform for Sparrow")
|
set(PLATFORM "sparrow" CACHE STRING "The one and only seL4 platform for Sparrow")
|
||||||
set(KernelSel4Arch "riscv32" CACHE STRING "Specifies 32-bit branch of the seL4 spike platform")
|
set(KernelSel4Arch "riscv32" CACHE STRING "Specifies 32-bit branch of the seL4 spike platform")
|
||||||
|
Reference in New Issue
Block a user