mirror of
https://github.com/AmbiML/sparrow-kata-full.git
synced 2025-09-15 22:48:19 +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(
|
||||
kata_process_manager
|
||||
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/components/ProcessManager
|
||||
LIB_FILENAME libkata_process_manager.a
|
||||
kata_memory_manager
|
||||
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/components/MemoryManager
|
||||
LIB_FILENAME libkata_memory_manager.a
|
||||
)
|
||||
|
||||
DeclareCAmkESComponent(ProcessManager
|
||||
LIBS kata_process_manager
|
||||
DeclareCAmkESComponent(MemoryManager
|
||||
LIBS kata_memory_manager
|
||||
INCLUDES interfaces
|
||||
)
|
||||
|
||||
@@ -52,6 +52,17 @@ DeclareCAmkESComponent(MlCoordinator
|
||||
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(
|
||||
kata_security_coordinator
|
||||
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/components/SecurityCoordinator
|
||||
|
@@ -2,6 +2,7 @@ import <LoggerInterface.camkes>;
|
||||
import <ProcessControlInterface.camkes>;
|
||||
import <PackageManagementInterface.camkes>;
|
||||
import <MlCoordinatorInterface.camkes>;
|
||||
import <MemoryInterface.camkes>;
|
||||
import <SecurityCoordinatorInterface.camkes>;
|
||||
import <StorageInterface.camkes>;
|
||||
|
||||
@@ -15,11 +16,12 @@ component DebugConsole {
|
||||
uses rust_read_inf uart_read;
|
||||
|
||||
provides LoggerInterface logger;
|
||||
uses ProcessControlInterface proc_ctrl;
|
||||
uses MemoryInterface memory;
|
||||
uses MlCoordinatorInterface mlcoord;
|
||||
uses PackageManagementInterface pkg_mgmt;
|
||||
uses ProcessControlInterface proc_ctrl;
|
||||
// TODO(b/200707300): for debugging
|
||||
uses SecurityCoordinatorInterface security;
|
||||
// TODO(b/200707300): for debugging
|
||||
uses StorageInterface storage;
|
||||
uses MlCoordinatorInterface mlcoord;
|
||||
}
|
||||
|
@@ -8,18 +8,17 @@ build = "build.rs"
|
||||
[build-dependencies]
|
||||
sel4-config = { path = "../../kata-os-common/src/sel4-config" }
|
||||
|
||||
[build-env]
|
||||
SEL4_OUT_DIR = "${ROOTDIR}out/kata/kernel"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
CONFIG_DEBUG_BUILD = []
|
||||
CONFIG_KERNEL_MCS = []
|
||||
|
||||
[dependencies]
|
||||
crc = { version = "1.4.0", default_features = false }
|
||||
hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
|
||||
kata-io = { path = "../kata-io" }
|
||||
kata-line-reader = { path = "../kata-line-reader" }
|
||||
kata-memory-interface = { path = "../../MemoryManager/kata-memory-interface" }
|
||||
kata-proc-interface = { path = "../../ProcessManager/kata-proc-interface" }
|
||||
kata-os-common = { path = "../../kata-os-common" }
|
||||
kata-security-interface = { path = "../../SecurityCoordinator/kata-security-interface" }
|
||||
|
@@ -1,4 +1,3 @@
|
||||
//extern crate sel4_config;
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
extern crate alloc;
|
||||
use alloc::string::String;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use core::fmt;
|
||||
use core::fmt::Write;
|
||||
@@ -10,6 +11,8 @@ use log;
|
||||
|
||||
use kata_io as io;
|
||||
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_uninstall;
|
||||
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_write;
|
||||
|
||||
use sel4_sys::seL4_CPtr;
|
||||
use sel4_sys::seL4_MinSchedContextBits;
|
||||
use sel4_sys::seL4_ObjectType::*;
|
||||
use sel4_sys::seL4_WordBits;
|
||||
|
||||
mod rz;
|
||||
|
||||
/// 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),
|
||||
"install" => install_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),
|
||||
"ps" => ps_command(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_error" => test_alloc_error_command(output),
|
||||
"test_panic" => test_panic_command(),
|
||||
"test_bootinfo" => test_bootinfo_command(output),
|
||||
"test_mlexecute" => test_mlexecute_command(),
|
||||
"test_mlcontinuous" => test_mlcontinuous_command(&mut args),
|
||||
"test_obj_alloc" => test_obj_alloc_command(output),
|
||||
"test_panic" => test_panic_command(),
|
||||
|
||||
_ => Err(CommandError::UnknownCommand),
|
||||
};
|
||||
@@ -237,14 +250,11 @@ fn add_command(
|
||||
args: &mut dyn Iterator<Item = &str>,
|
||||
output: &mut dyn io::Write,
|
||||
) -> Result<(), CommandError> {
|
||||
if let Some(x_str) = args.nth(0) {
|
||||
if let Some(y_str) = args.nth(0) {
|
||||
let x = x_str.parse::<f32>()?;
|
||||
let y = y_str.parse::<f32>()?;
|
||||
return Ok(writeln!(output, "{}", x + y)?);
|
||||
}
|
||||
}
|
||||
Err(CommandError::BadArgs)
|
||||
let x_str = args.next().ok_or(CommandError::BadArgs)?;
|
||||
let x = x_str.parse::<f32>()?;
|
||||
let y_str = args.next().ok_or(CommandError::BadArgs)?;
|
||||
let y = y_str.parse::<f32>()?;
|
||||
return Ok(writeln!(output, "{}", x + y)?);
|
||||
}
|
||||
|
||||
/// Implements a command that outputs the ANSI "clear console" sequence.
|
||||
@@ -285,122 +295,175 @@ fn uninstall_command(
|
||||
args: &mut dyn Iterator<Item = &str>,
|
||||
output: &mut dyn io::Write,
|
||||
) -> Result<(), CommandError> {
|
||||
if let Some(bundle_id) = args.nth(0) {
|
||||
match kata_pkg_mgmt_uninstall(bundle_id) {
|
||||
Ok(_) => {
|
||||
writeln!(output, "Bundle \"{}\" uninstalled.", bundle_id)?;
|
||||
}
|
||||
Err(status) => {
|
||||
writeln!(output, "uninstall failed: {:?}", status)?;
|
||||
}
|
||||
let bundle_id = args.next().ok_or(CommandError::BadArgs)?;
|
||||
match kata_pkg_mgmt_uninstall(bundle_id) {
|
||||
Ok(_) => {
|
||||
writeln!(output, "Bundle \"{}\" uninstalled.", bundle_id)?;
|
||||
}
|
||||
Err(status) => {
|
||||
writeln!(output, "uninstall failed: {:?}", status)?;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(CommandError::BadArgs)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn start_command(
|
||||
args: &mut dyn Iterator<Item = &str>,
|
||||
output: &mut dyn io::Write,
|
||||
) -> Result<(), CommandError> {
|
||||
if let Some(bundle_id) = args.nth(0) {
|
||||
match kata_proc_ctrl_start(bundle_id) {
|
||||
Ok(_) => {
|
||||
writeln!(output, "Bundle \"{}\" started.", bundle_id)?;
|
||||
}
|
||||
Err(status) => {
|
||||
writeln!(output, "start failed: {:?}", status)?;
|
||||
}
|
||||
let bundle_id = args.next().ok_or(CommandError::BadArgs)?;
|
||||
match kata_proc_ctrl_start(bundle_id) {
|
||||
Ok(_) => {
|
||||
writeln!(output, "Bundle \"{}\" started.", bundle_id)?;
|
||||
}
|
||||
Err(status) => {
|
||||
writeln!(output, "start failed: {:?}", status)?;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(CommandError::BadArgs)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stop_command(
|
||||
args: &mut dyn Iterator<Item = &str>,
|
||||
output: &mut dyn io::Write,
|
||||
) -> Result<(), CommandError> {
|
||||
if let Some(bundle_id) = args.nth(0) {
|
||||
match kata_proc_ctrl_stop(bundle_id) {
|
||||
Ok(_) => {
|
||||
writeln!(output, "Bundle \"{}\" stopped.", bundle_id)?;
|
||||
}
|
||||
Err(status) => {
|
||||
writeln!(output, "stop failed: {:?}", status)?;
|
||||
}
|
||||
let bundle_id = args.next().ok_or(CommandError::BadArgs)?;
|
||||
match kata_proc_ctrl_stop(bundle_id) {
|
||||
Ok(_) => {
|
||||
writeln!(output, "Bundle \"{}\" stopped.", bundle_id)?;
|
||||
}
|
||||
Err(status) => {
|
||||
writeln!(output, "stop failed: {:?}", status)?;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(CommandError::BadArgs)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn kvdelete_command(
|
||||
args: &mut dyn Iterator<Item = &str>,
|
||||
output: &mut dyn io::Write,
|
||||
) -> Result<(), CommandError> {
|
||||
if let Some(key) = args.nth(0) {
|
||||
match kata_storage_delete(key) {
|
||||
Ok(_) => {
|
||||
writeln!(output, "Delete key \"{}\".", key)?;
|
||||
}
|
||||
Err(status) => {
|
||||
writeln!(output, "Delete key \"{}\" failed: {:?}", key, status)?;
|
||||
}
|
||||
let key = args.next().ok_or(CommandError::BadArgs)?;
|
||||
match kata_storage_delete(key) {
|
||||
Ok(_) => {
|
||||
writeln!(output, "Delete key \"{}\".", key)?;
|
||||
}
|
||||
Err(status) => {
|
||||
writeln!(output, "Delete key \"{}\" failed: {:?}", key, status)?;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(CommandError::BadArgs)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn kvread_command(
|
||||
args: &mut dyn Iterator<Item = &str>,
|
||||
output: &mut dyn io::Write,
|
||||
) -> Result<(), CommandError> {
|
||||
if let Some(key) = args.nth(0) {
|
||||
match kata_storage_read(key) {
|
||||
Ok(value) => {
|
||||
writeln!(output, "Read key \"{}\" = {:?}.", key, value)?;
|
||||
}
|
||||
Err(status) => {
|
||||
writeln!(output, "Read key \"{}\" failed: {:?}", key, status)?;
|
||||
}
|
||||
let key = args.next().ok_or(CommandError::BadArgs)?;
|
||||
match kata_storage_read(key) {
|
||||
Ok(value) => {
|
||||
writeln!(output, "Read key \"{}\" = {:?}.", key, value)?;
|
||||
}
|
||||
Err(status) => {
|
||||
writeln!(output, "Read key \"{}\" failed: {:?}", key, status)?;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(CommandError::BadArgs)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn kvwrite_command(
|
||||
args: &mut dyn Iterator<Item = &str>,
|
||||
output: &mut dyn io::Write,
|
||||
) -> Result<(), CommandError> {
|
||||
if let Some(key) = args.nth(0) {
|
||||
let value = args.collect::<Vec<&str>>().join(" ");
|
||||
match kata_storage_write(key, value.as_bytes()) {
|
||||
Ok(_) => {
|
||||
writeln!(output, "Write key \"{}\" = {:?}.", key, value)?;
|
||||
}
|
||||
Err(status) => {
|
||||
writeln!(output, "Write key \"{}\" failed: {:?}", key, status)?;
|
||||
}
|
||||
let key = args.next().ok_or(CommandError::BadArgs)?;
|
||||
let value = args.collect::<Vec<&str>>().join(" ");
|
||||
match kata_storage_write(key, value.as_bytes()) {
|
||||
Ok(_) => {
|
||||
writeln!(output, "Write key \"{}\" = {:?}.", key, value)?;
|
||||
}
|
||||
Err(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.
|
||||
/// Shamelessly cribbed from https://os.phil-opp.com/heap-allocation/
|
||||
fn test_alloc_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
|
||||
extern crate alloc;
|
||||
use alloc::{boxed::Box, rc::Rc, vec};
|
||||
use alloc::{boxed::Box, rc::Rc};
|
||||
|
||||
// allocate a number on the heap
|
||||
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())?)
|
||||
}
|
||||
|
||||
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.
|
||||
fn test_panic_command() -> Result<(), CommandError> {
|
||||
panic!("testing");
|
||||
@@ -473,3 +556,77 @@ fn test_mlcontinuous_command(args: &mut dyn Iterator<Item = &str>) -> Result<(),
|
||||
}
|
||||
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 cstr_core;
|
||||
use sel4_sys::SEL4_BOOTINFO_HEADER_BOOTINFO;
|
||||
use sel4_sys::SEL4_BOOTINFO_HEADER_FDT;
|
||||
use sel4_sys::SEL4_BOOTINFO_HEADER_PADDING;
|
||||
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)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
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_TSC_Freq = SEL4_BOOTINFO_HEADER_X86_TSC_FREQ 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 {
|
||||
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)
|
||||
}
|
||||
|
||||
#[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
|
||||
// NB: return everything as seL4_Word to minimize conversions
|
||||
#[inline]
|
||||
@@ -804,6 +817,7 @@ pub union CDL_ObjectExtra {
|
||||
pub msiirq_extra: CDL_MSIIRQExtraInfo,
|
||||
pub armirq_extra: CDL_ARMIRQExtraInfo,
|
||||
pub frame_extra: CDL_FrameExtraInfo,
|
||||
pub cnode_extra: CDL_CNodeExtraInfo,
|
||||
|
||||
// Physical address; only defined for untyped objects.
|
||||
pub paddr: seL4_Word,
|
||||
|
@@ -2,9 +2,11 @@
|
||||
|
||||
use crate::KataOsModel;
|
||||
use capdl::kobject_t::KOBJECT_FRAME;
|
||||
use capdl::CDL_FrameFillType_t::*;
|
||||
use capdl::CDL_FrameFill_BootInfoEnum_t::*;
|
||||
use capdl::CDL_ObjectType::*;
|
||||
use capdl::*;
|
||||
use log::{debug, trace};
|
||||
use log::{debug, info, trace};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use sel4_sys::seL4_BootInfo;
|
||||
@@ -58,6 +60,10 @@ impl<'a> KataOsModel<'a> {
|
||||
// possibility of vpsace_roots spilling to the heap.
|
||||
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.
|
||||
// The exception is ASIDPools, where create_object only allocates
|
||||
// the backing untypeds.
|
||||
@@ -90,11 +96,40 @@ impl<'a> KataOsModel<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// Capture VSpace roots for later use.
|
||||
if obj.r#type() == CDL_TCB {
|
||||
if let Some(root_cap) = obj.get_cap_at(CDL_TCB_VTable_Slot) {
|
||||
roots.push(root_cap.obj_id);
|
||||
match obj.r#type() {
|
||||
// Capture VSpace roots & TCBs for later use.
|
||||
CDL_TCB => {
|
||||
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.
|
||||
self.set_orig_cap(obj_id, free_slot);
|
||||
@@ -140,6 +175,15 @@ impl<'a> KataOsModel<'a> {
|
||||
roots.dedup();
|
||||
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(())
|
||||
}
|
||||
|
||||
|
@@ -19,7 +19,7 @@ use core::mem::size_of;
|
||||
use core::ptr;
|
||||
use cpio::CpioNewcReader;
|
||||
use cstr_core::CStr;
|
||||
use log::{debug, error, trace};
|
||||
use log::{debug, info, error, trace};
|
||||
use smallvec::SmallVec;
|
||||
use static_assertions::*;
|
||||
|
||||
@@ -127,7 +127,7 @@ const_assert!(seL4_WordBits == 32 || seL4_WordBits == 64);
|
||||
|
||||
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 {
|
||||
if (a % b) == 0 {
|
||||
a
|
||||
@@ -193,6 +193,8 @@ pub struct KataOsModel<'a> {
|
||||
#[cfg(feature = "CONFIG_ARM_SMMU")]
|
||||
sid_number: usize,
|
||||
|
||||
untyped_cnode: CDL_ObjID,
|
||||
|
||||
extended_bootinfo_table: [*const seL4_BootInfoHeader; SEL4_BOOTINFO_HEADER_NUM],
|
||||
|
||||
vspace_roots: SmallVec<[CDL_ObjID; 32]>, // NB: essentially #components
|
||||
@@ -219,6 +221,8 @@ impl<'a> KataOsModel<'a> {
|
||||
#[cfg(feature = "CONFIG_ARM_SMMU")]
|
||||
sid_number: 0,
|
||||
|
||||
untyped_cnode: CDL_ObjID::MAX,
|
||||
|
||||
extended_bootinfo_table: [ptr::null(); SEL4_BOOTINFO_HEADER_NUM],
|
||||
|
||||
vspace_roots: SmallVec::new(),
|
||||
@@ -255,6 +259,19 @@ impl<'a> KataOsModel<'a> {
|
||||
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 {
|
||||
trace!("Starting threads...");
|
||||
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 badge = endpoint_cap.cap_data();
|
||||
let endpoint_cptr = if badge != 0 {
|
||||
// Endpoint needs badging, mint a new cap
|
||||
self.mint_cap(endpoint_obj, badge)?
|
||||
} else {
|
||||
self.get_orig_cap(endpoint_obj)
|
||||
@@ -554,7 +572,8 @@ impl<'a> KataOsModel<'a> {
|
||||
match bi.type_ {
|
||||
CDL_FrameFill_BootInfo_X86_VBE
|
||||
| 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_)
|
||||
}
|
||||
@@ -566,8 +585,10 @@ impl<'a> KataOsModel<'a> {
|
||||
let header: *const seL4_BootInfoHeader =
|
||||
self.extended_bootinfo_table[usize::from(bi.type_)];
|
||||
|
||||
/* Check if the bootinfo has been found */
|
||||
if header.is_null() {
|
||||
// Check if the bootinfo has been found.
|
||||
if bi.type_ == CDL_FrameFill_BootInfo_BootInfo {
|
||||
self.fill_with_bootinfo(dest, max_len);
|
||||
} else if header.is_null() {
|
||||
/* No bootinfo.
|
||||
* If we're at the start of a block, copy an empty header across, otherwise skip the copy */
|
||||
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;
|
||||
// in particular this loads each CAmkES component's executable.
|
||||
fn fill_frame_with_filedata(&self, base: usize, frame_fill: &CDL_FrameFill_Element_t) {
|
||||
@@ -880,6 +930,53 @@ impl<'a> KataOsModel<'a> {
|
||||
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(
|
||||
&self,
|
||||
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_TSC_FREQ: usize = 5;
|
||||
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/ProcessManager/ProcessManager.camkes";
|
||||
import "components/MlCoordinator/MlCoordinator.camkes";
|
||||
import "components/MemoryManager/MemoryManager.camkes";
|
||||
import "components/StorageManager/StorageManager.camkes";
|
||||
import "components/SecurityCoordinator/SecurityCoordinator.camkes";
|
||||
|
||||
@@ -54,6 +55,7 @@ assembly {
|
||||
component OpenTitanUART uart;
|
||||
component OpenTitanUARTDriver uart_driver;
|
||||
|
||||
component MemoryManager memory_manager;
|
||||
component ProcessManager process_manager;
|
||||
component MlCoordinator ml_coordinator;
|
||||
component DebugConsole debug_console;
|
||||
@@ -98,6 +100,15 @@ assembly {
|
||||
connection seL4RPCCall shell_storage(from debug_console.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
|
||||
// access to the Security Core. Note this allocates a 4KB shared memory
|
||||
// region to each component and copies data between components.
|
||||
@@ -124,6 +135,7 @@ assembly {
|
||||
connection seL4RPCOverMultiSharedData multi_logger(
|
||||
from process_manager.logger,
|
||||
from ml_coordinator.logger,
|
||||
from memory_manager.logger,
|
||||
from security_coordinator.logger,
|
||||
from storage_manager.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(CAPDL_LOADER_APP "capdl-loader-app" CACHE STRING "")
|
||||
#set(CAPDL_LOADER_APP "kata-os-rootserver" CACHE STRING "")
|
||||
#set(CAPDL_LOADER_APP "capdl-loader-app" 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(KernelSel4Arch "riscv32" CACHE STRING "Specifies 32-bit branch of the seL4 spike platform")
|
||||
|
Reference in New Issue
Block a user