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:
Sam Leffler
2022-03-29 18:49:55 +00:00
parent 4e5b8a4423
commit 18c7660244
24 changed files with 1601 additions and 101 deletions

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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" }

View File

@@ -1,4 +1,3 @@
//extern crate sel4_config;
use std::env;
fn main() {

View File

@@ -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!")?)
}

View 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

View 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;
}

View 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"]

View File

@@ -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"]

View File

@@ -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(),
}
}
}

View File

@@ -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"

View File

@@ -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);
}
}

View File

@@ -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),
}
}

View File

@@ -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"

View File

@@ -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()
}
}

View File

@@ -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(),
})
}
}

View File

@@ -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,

View File

@@ -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(())
}

View File

@@ -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,

View File

@@ -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;

View 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);
};

View 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__ */

View File

@@ -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);

View File

@@ -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")