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( RustAddLibrary(
kata_process_manager kata_memory_manager
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/components/ProcessManager SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/components/MemoryManager
LIB_FILENAME libkata_process_manager.a LIB_FILENAME libkata_memory_manager.a
) )
DeclareCAmkESComponent(ProcessManager DeclareCAmkESComponent(MemoryManager
LIBS kata_process_manager LIBS kata_memory_manager
INCLUDES interfaces INCLUDES interfaces
) )
@@ -52,6 +52,17 @@ DeclareCAmkESComponent(MlCoordinator
INCLUDES interfaces INCLUDES interfaces
) )
RustAddLibrary(
kata_process_manager
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/components/ProcessManager
LIB_FILENAME libkata_process_manager.a
)
DeclareCAmkESComponent(ProcessManager
LIBS kata_process_manager
INCLUDES interfaces
)
RustAddLibrary( RustAddLibrary(
kata_security_coordinator kata_security_coordinator
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/components/SecurityCoordinator SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/components/SecurityCoordinator

View File

@@ -2,6 +2,7 @@ import <LoggerInterface.camkes>;
import <ProcessControlInterface.camkes>; import <ProcessControlInterface.camkes>;
import <PackageManagementInterface.camkes>; import <PackageManagementInterface.camkes>;
import <MlCoordinatorInterface.camkes>; import <MlCoordinatorInterface.camkes>;
import <MemoryInterface.camkes>;
import <SecurityCoordinatorInterface.camkes>; import <SecurityCoordinatorInterface.camkes>;
import <StorageInterface.camkes>; import <StorageInterface.camkes>;
@@ -15,11 +16,12 @@ component DebugConsole {
uses rust_read_inf uart_read; uses rust_read_inf uart_read;
provides LoggerInterface logger; provides LoggerInterface logger;
uses ProcessControlInterface proc_ctrl; uses MemoryInterface memory;
uses MlCoordinatorInterface mlcoord;
uses PackageManagementInterface pkg_mgmt; uses PackageManagementInterface pkg_mgmt;
uses ProcessControlInterface proc_ctrl;
// TODO(b/200707300): for debugging // TODO(b/200707300): for debugging
uses SecurityCoordinatorInterface security; uses SecurityCoordinatorInterface security;
// TODO(b/200707300): for debugging // TODO(b/200707300): for debugging
uses StorageInterface storage; uses StorageInterface storage;
uses MlCoordinatorInterface mlcoord;
} }

View File

@@ -8,18 +8,17 @@ build = "build.rs"
[build-dependencies] [build-dependencies]
sel4-config = { path = "../../kata-os-common/src/sel4-config" } sel4-config = { path = "../../kata-os-common/src/sel4-config" }
[build-env]
SEL4_OUT_DIR = "${ROOTDIR}out/kata/kernel"
[features] [features]
default = [] default = []
CONFIG_DEBUG_BUILD = [] CONFIG_DEBUG_BUILD = []
CONFIG_KERNEL_MCS = []
[dependencies] [dependencies]
crc = { version = "1.4.0", default_features = false } crc = { version = "1.4.0", default_features = false }
hex = { version = "0.4.3", default-features = false, features = ["alloc"] } hex = { version = "0.4.3", default-features = false, features = ["alloc"] }
kata-io = { path = "../kata-io" } kata-io = { path = "../kata-io" }
kata-line-reader = { path = "../kata-line-reader" } kata-line-reader = { path = "../kata-line-reader" }
kata-memory-interface = { path = "../../MemoryManager/kata-memory-interface" }
kata-proc-interface = { path = "../../ProcessManager/kata-proc-interface" } kata-proc-interface = { path = "../../ProcessManager/kata-proc-interface" }
kata-os-common = { path = "../../kata-os-common" } kata-os-common = { path = "../../kata-os-common" }
kata-security-interface = { path = "../../SecurityCoordinator/kata-security-interface" } kata-security-interface = { path = "../../SecurityCoordinator/kata-security-interface" }

View File

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

View File

@@ -2,6 +2,7 @@
extern crate alloc; extern crate alloc;
use alloc::string::String; use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec; use alloc::vec::Vec;
use core::fmt; use core::fmt;
use core::fmt::Write; use core::fmt::Write;
@@ -10,6 +11,8 @@ use log;
use kata_io as io; use kata_io as io;
use kata_line_reader::LineReader; use kata_line_reader::LineReader;
use kata_os_common::sel4_sys;
use kata_memory_interface::*;
use kata_proc_interface::kata_pkg_mgmt_install; use kata_proc_interface::kata_pkg_mgmt_install;
use kata_proc_interface::kata_pkg_mgmt_uninstall; use kata_proc_interface::kata_pkg_mgmt_uninstall;
use kata_proc_interface::kata_proc_ctrl_get_running_bundles; use kata_proc_interface::kata_proc_ctrl_get_running_bundles;
@@ -19,6 +22,11 @@ use kata_storage_interface::kata_storage_delete;
use kata_storage_interface::kata_storage_read; use kata_storage_interface::kata_storage_read;
use kata_storage_interface::kata_storage_write; use kata_storage_interface::kata_storage_write;
use sel4_sys::seL4_CPtr;
use sel4_sys::seL4_MinSchedContextBits;
use sel4_sys::seL4_ObjectType::*;
use sel4_sys::seL4_WordBits;
mod rz; mod rz;
/// Error type indicating why a command line is not runnable. /// Error type indicating why a command line is not runnable.
@@ -108,6 +116,9 @@ fn dispatch_command(cmdline: &str, input: &mut dyn io::BufRead, output: &mut dyn
"kvwrite" => kvwrite_command(&mut args, output), "kvwrite" => kvwrite_command(&mut args, output),
"install" => install_command(&mut args, output), "install" => install_command(&mut args, output),
"loglevel" => loglevel_command(&mut args, output), "loglevel" => loglevel_command(&mut args, output),
"malloc" => malloc_command(&mut args, output),
"mfree" => mfree_command(&mut args, output),
"mstats" => mstats_command(&mut args, output),
"rz" => rz_command(input, output), "rz" => rz_command(input, output),
"ps" => ps_command(output), "ps" => ps_command(output),
"scecho" => scecho_command(cmdline, output), "scecho" => scecho_command(cmdline, output),
@@ -117,9 +128,11 @@ fn dispatch_command(cmdline: &str, input: &mut dyn io::BufRead, output: &mut dyn
"test_alloc" => test_alloc_command(output), "test_alloc" => test_alloc_command(output),
"test_alloc_error" => test_alloc_error_command(output), "test_alloc_error" => test_alloc_error_command(output),
"test_panic" => test_panic_command(), "test_bootinfo" => test_bootinfo_command(output),
"test_mlexecute" => test_mlexecute_command(), "test_mlexecute" => test_mlexecute_command(),
"test_mlcontinuous" => test_mlcontinuous_command(&mut args), "test_mlcontinuous" => test_mlcontinuous_command(&mut args),
"test_obj_alloc" => test_obj_alloc_command(output),
"test_panic" => test_panic_command(),
_ => Err(CommandError::UnknownCommand), _ => Err(CommandError::UnknownCommand),
}; };
@@ -237,14 +250,11 @@ fn add_command(
args: &mut dyn Iterator<Item = &str>, args: &mut dyn Iterator<Item = &str>,
output: &mut dyn io::Write, output: &mut dyn io::Write,
) -> Result<(), CommandError> { ) -> Result<(), CommandError> {
if let Some(x_str) = args.nth(0) { let x_str = args.next().ok_or(CommandError::BadArgs)?;
if let Some(y_str) = args.nth(0) { let x = x_str.parse::<f32>()?;
let x = x_str.parse::<f32>()?; let y_str = args.next().ok_or(CommandError::BadArgs)?;
let y = y_str.parse::<f32>()?; let y = y_str.parse::<f32>()?;
return Ok(writeln!(output, "{}", x + y)?); return Ok(writeln!(output, "{}", x + y)?);
}
}
Err(CommandError::BadArgs)
} }
/// Implements a command that outputs the ANSI "clear console" sequence. /// Implements a command that outputs the ANSI "clear console" sequence.
@@ -285,122 +295,175 @@ fn uninstall_command(
args: &mut dyn Iterator<Item = &str>, args: &mut dyn Iterator<Item = &str>,
output: &mut dyn io::Write, output: &mut dyn io::Write,
) -> Result<(), CommandError> { ) -> Result<(), CommandError> {
if let Some(bundle_id) = args.nth(0) { let bundle_id = args.next().ok_or(CommandError::BadArgs)?;
match kata_pkg_mgmt_uninstall(bundle_id) { match kata_pkg_mgmt_uninstall(bundle_id) {
Ok(_) => { Ok(_) => {
writeln!(output, "Bundle \"{}\" uninstalled.", bundle_id)?; writeln!(output, "Bundle \"{}\" uninstalled.", bundle_id)?;
} }
Err(status) => { Err(status) => {
writeln!(output, "uninstall failed: {:?}", status)?; writeln!(output, "uninstall failed: {:?}", status)?;
}
} }
Ok(())
} else {
Err(CommandError::BadArgs)
} }
Ok(())
} }
fn start_command( fn start_command(
args: &mut dyn Iterator<Item = &str>, args: &mut dyn Iterator<Item = &str>,
output: &mut dyn io::Write, output: &mut dyn io::Write,
) -> Result<(), CommandError> { ) -> Result<(), CommandError> {
if let Some(bundle_id) = args.nth(0) { let bundle_id = args.next().ok_or(CommandError::BadArgs)?;
match kata_proc_ctrl_start(bundle_id) { match kata_proc_ctrl_start(bundle_id) {
Ok(_) => { Ok(_) => {
writeln!(output, "Bundle \"{}\" started.", bundle_id)?; writeln!(output, "Bundle \"{}\" started.", bundle_id)?;
} }
Err(status) => { Err(status) => {
writeln!(output, "start failed: {:?}", status)?; writeln!(output, "start failed: {:?}", status)?;
}
} }
Ok(())
} else {
Err(CommandError::BadArgs)
} }
Ok(())
} }
fn stop_command( fn stop_command(
args: &mut dyn Iterator<Item = &str>, args: &mut dyn Iterator<Item = &str>,
output: &mut dyn io::Write, output: &mut dyn io::Write,
) -> Result<(), CommandError> { ) -> Result<(), CommandError> {
if let Some(bundle_id) = args.nth(0) { let bundle_id = args.next().ok_or(CommandError::BadArgs)?;
match kata_proc_ctrl_stop(bundle_id) { match kata_proc_ctrl_stop(bundle_id) {
Ok(_) => { Ok(_) => {
writeln!(output, "Bundle \"{}\" stopped.", bundle_id)?; writeln!(output, "Bundle \"{}\" stopped.", bundle_id)?;
} }
Err(status) => { Err(status) => {
writeln!(output, "stop failed: {:?}", status)?; writeln!(output, "stop failed: {:?}", status)?;
}
} }
Ok(())
} else {
Err(CommandError::BadArgs)
} }
Ok(())
} }
fn kvdelete_command( fn kvdelete_command(
args: &mut dyn Iterator<Item = &str>, args: &mut dyn Iterator<Item = &str>,
output: &mut dyn io::Write, output: &mut dyn io::Write,
) -> Result<(), CommandError> { ) -> Result<(), CommandError> {
if let Some(key) = args.nth(0) { let key = args.next().ok_or(CommandError::BadArgs)?;
match kata_storage_delete(key) { match kata_storage_delete(key) {
Ok(_) => { Ok(_) => {
writeln!(output, "Delete key \"{}\".", key)?; writeln!(output, "Delete key \"{}\".", key)?;
} }
Err(status) => { Err(status) => {
writeln!(output, "Delete key \"{}\" failed: {:?}", key, status)?; writeln!(output, "Delete key \"{}\" failed: {:?}", key, status)?;
}
} }
Ok(())
} else {
Err(CommandError::BadArgs)
} }
Ok(())
} }
fn kvread_command( fn kvread_command(
args: &mut dyn Iterator<Item = &str>, args: &mut dyn Iterator<Item = &str>,
output: &mut dyn io::Write, output: &mut dyn io::Write,
) -> Result<(), CommandError> { ) -> Result<(), CommandError> {
if let Some(key) = args.nth(0) { let key = args.next().ok_or(CommandError::BadArgs)?;
match kata_storage_read(key) { match kata_storage_read(key) {
Ok(value) => { Ok(value) => {
writeln!(output, "Read key \"{}\" = {:?}.", key, value)?; writeln!(output, "Read key \"{}\" = {:?}.", key, value)?;
} }
Err(status) => { Err(status) => {
writeln!(output, "Read key \"{}\" failed: {:?}", key, status)?; writeln!(output, "Read key \"{}\" failed: {:?}", key, status)?;
}
} }
Ok(())
} else {
Err(CommandError::BadArgs)
} }
Ok(())
} }
fn kvwrite_command( fn kvwrite_command(
args: &mut dyn Iterator<Item = &str>, args: &mut dyn Iterator<Item = &str>,
output: &mut dyn io::Write, output: &mut dyn io::Write,
) -> Result<(), CommandError> { ) -> Result<(), CommandError> {
if let Some(key) = args.nth(0) { let key = args.next().ok_or(CommandError::BadArgs)?;
let value = args.collect::<Vec<&str>>().join(" "); let value = args.collect::<Vec<&str>>().join(" ");
match kata_storage_write(key, value.as_bytes()) { match kata_storage_write(key, value.as_bytes()) {
Ok(_) => { Ok(_) => {
writeln!(output, "Write key \"{}\" = {:?}.", key, value)?; writeln!(output, "Write key \"{}\" = {:?}.", key, value)?;
} }
Err(status) => { Err(status) => {
writeln!(output, "Write key \"{}\" failed: {:?}", key, status)?; writeln!(output, "Write key \"{}\" failed: {:?}", key, status)?;
}
} }
Ok(())
} else {
Err(CommandError::BadArgs)
} }
Ok(())
}
fn malloc_command(
args: &mut dyn Iterator<Item = &str>,
output: &mut dyn io::Write,
) -> Result<(), CommandError> {
let space_str = args.next().ok_or(CommandError::BadArgs)?;
let space_bytes = space_str.parse::<usize>()?;
match kata_frame_alloc(space_bytes) {
Ok(frames) => {
writeln!(output, "Allocated {:?}", frames)?;
}
Err(status) => {
writeln!(output, "malloc failed: {:?}", status)?;
}
}
Ok(())
}
fn mfree_command(
args: &mut dyn Iterator<Item = &str>,
output: &mut dyn io::Write,
) -> Result<(), CommandError> {
extern "C" { static SELF_CNODE: seL4_CPtr; }
let cptr_str = args.next().ok_or(CommandError::BadArgs)?;
let count_str = args.next().ok_or(CommandError::BadArgs)?;
let frames = ObjDescBundle::new(
unsafe { SELF_CNODE },
seL4_WordBits as u8,
vec![
ObjDesc::new(
sel4_sys::seL4_RISCV_4K_Page,
count_str.parse::<usize>()?,
cptr_str.parse::<usize>()? as seL4_CPtr,
),
],
);
match kata_object_free_toplevel(&frames) {
Ok(_) => {
writeln!(output, "Free'd {:?}", frames)?;
}
Err(status) => {
writeln!(output, "mfree failed: {:?}", status)?;
}
}
Ok(())
}
fn mstats(output: &mut dyn io::Write, stats: &MemoryManagerStats)
-> Result<(), CommandError>
{
writeln!(output, "{} bytes in-use, {} bytes free, {} bytes requested, {} overhead",
stats.allocated_bytes,
stats.free_bytes,
stats.total_requested_bytes,
stats.overhead_bytes)?;
writeln!(output, "{} objs in-use, {} objs requested",
stats.allocated_objs,
stats.total_requested_objs)?;
Ok(())
}
fn mstats_command(
_args: &mut dyn Iterator<Item = &str>,
output: &mut dyn io::Write,
) -> Result<(), CommandError> {
match kata_memory_stats() {
Ok(stats) => { mstats(output, &stats)?; }
Err(status) => { writeln!(output, "stats failed: {:?}", status)?; }
}
Ok(())
} }
/// Implements a command that tests facilities that use the global allocator. /// Implements a command that tests facilities that use the global allocator.
/// Shamelessly cribbed from https://os.phil-opp.com/heap-allocation/ /// Shamelessly cribbed from https://os.phil-opp.com/heap-allocation/
fn test_alloc_command(output: &mut dyn io::Write) -> Result<(), CommandError> { fn test_alloc_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
extern crate alloc; extern crate alloc;
use alloc::{boxed::Box, rc::Rc, vec}; use alloc::{boxed::Box, rc::Rc};
// allocate a number on the heap // allocate a number on the heap
let heap_value = Box::new(41); let heap_value = Box::new(41);
@@ -443,6 +506,26 @@ fn test_alloc_error_command(output: &mut dyn io::Write) -> Result<(), CommandErr
Ok(writeln!(output, "vec at {:p}", vec.as_slice())?) Ok(writeln!(output, "vec at {:p}", vec.as_slice())?)
} }
fn test_bootinfo_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
use kata_os_common::sel4_sys::seL4_BootInfo;
extern "C" {
fn sel4runtime_bootinfo() -> *const seL4_BootInfo;
}
let bootinfo_ref = unsafe { &*sel4runtime_bootinfo() };
writeln!(output, "{}:{} empty slots {}:{} untyped",
bootinfo_ref.empty.start, bootinfo_ref.empty.end,
bootinfo_ref.untyped.start, bootinfo_ref.untyped.end)?;
// NB: seL4_DebugCapIdentify is only available in debug builds
#[cfg(feature = "CONFIG_DEBUG_BUILD")]
for ut in bootinfo_ref.untyped.start..bootinfo_ref.untyped.end {
let cap_tag = unsafe { kata_os_common::sel4_sys::seL4_DebugCapIdentify(ut) };
assert_eq!(cap_tag, 2,
"expected untyped (2), got {} for cap at {}", cap_tag, ut);
}
Ok(())
}
/// Implements a command that tests panic handling. /// Implements a command that tests panic handling.
fn test_panic_command() -> Result<(), CommandError> { fn test_panic_command() -> Result<(), CommandError> {
panic!("testing"); panic!("testing");
@@ -473,3 +556,77 @@ fn test_mlcontinuous_command(args: &mut dyn Iterator<Item = &str>) -> Result<(),
} }
Err(CommandError::BadArgs) Err(CommandError::BadArgs)
} }
fn test_obj_alloc_command(output: &mut dyn io::Write) -> Result<(), CommandError> {
let before_stats = kata_memory_stats().expect("before stats");
mstats(output, &before_stats)?;
fn check_alloc(output: &mut dyn io::Write,
name: &str,
res: Result<ObjDescBundle, MemoryManagerError>) {
match res {
Ok(obj) => {
if let Err(e) = kata_object_free_toplevel(&obj) {
let _ = writeln!(output, "free {} {:?} failed: {:?}", name, obj, e);
}
}
Err(e) => {
let _ = writeln!(output, "alloc {} failed: {:?}", name, e);
}
}
}
// NB: alloc+free immediately so we don't run out of top-level CNode slots
check_alloc(output, "untyped", kata_untyped_alloc(12)); // NB: 4KB
check_alloc(output, "tcb", kata_tcb_alloc());
check_alloc(output, "endpoint", kata_endpoint_alloc());
check_alloc(output, "notification", kata_notification_alloc());
check_alloc(output, "cnode", kata_cnode_alloc(5)); // NB: 32 slots
check_alloc(output, "frame", kata_frame_alloc(4096));
// check_alloc(output, "large frame", kata_frame_alloc(1024*1024));
check_alloc(output, "page table", kata_page_table_alloc());
#[cfg(feature = "CONFIG_KERNEL_MCS")]
check_alloc(output, "sched context",
kata_sched_context_alloc(seL4_MinSchedContextBits));
#[cfg(feature = "CONFIG_KERNEL_MCS")]
check_alloc(output, "reply", kata_reply_alloc());
let after_stats = kata_memory_stats().expect("after stats");
mstats(output, &after_stats)?;
assert_eq!(before_stats.allocated_bytes, after_stats.allocated_bytes);
assert_eq!(before_stats.free_bytes, after_stats.free_bytes);
// Batch allocate into a private CNode as we might to build a process.
const CNODE_DEPTH: usize = 7; // 128 slots
let cnode = kata_cnode_alloc(CNODE_DEPTH).unwrap(); // XXX handle error
let objs = ObjDescBundle::new(
cnode.objs[0].cptr,
CNODE_DEPTH as u8,
vec![
ObjDesc::new(seL4_TCBObject, 1, 0), // 1 tcb
ObjDesc::new(seL4_EndpointObject, 2, 1), // 2 endpoiints
ObjDesc::new(seL4_ReplyObject, 2, 3), // 2 replys
ObjDesc::new(seL4_SchedContextObject, // 1 sched context
seL4_MinSchedContextBits, 5),
ObjDesc::new(seL4_RISCV_4K_Page, 10, 6), // 10 4K pages
],
);
match kata_object_alloc(&objs) {
Ok(_) => {
writeln!(output, "Batch alloc ok: {:?}", objs)?;
if let Err(e) = kata_object_free(&objs) {
writeln!(output, "Batch free err: {:?}", e)?;
}
}
Err(e) => {
writeln!(output, "Batch alloc err: {:?} {:?}", objs, e)?;
}
}
if let Err(e) = kata_object_free_toplevel(&cnode) {
writeln!(output, "Cnode free err: {:?} {:?}", cnode, e)?;
}
Ok(writeln!(output, "All tests passed!")?)
}

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 core::mem::size_of;
use cstr_core; use cstr_core;
use sel4_sys::SEL4_BOOTINFO_HEADER_BOOTINFO;
use sel4_sys::SEL4_BOOTINFO_HEADER_FDT; use sel4_sys::SEL4_BOOTINFO_HEADER_FDT;
use sel4_sys::SEL4_BOOTINFO_HEADER_PADDING; use sel4_sys::SEL4_BOOTINFO_HEADER_PADDING;
use sel4_sys::SEL4_BOOTINFO_HEADER_X86_ACPI_RSDP; use sel4_sys::SEL4_BOOTINFO_HEADER_X86_ACPI_RSDP;
@@ -461,6 +462,12 @@ impl From<CDL_ObjectType> for seL4_ObjectType {
} }
} }
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct CDL_CNodeExtraInfo {
pub has_untyped_memory: bool,
}
#[repr(C)] #[repr(C)]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct CDL_TCBExtraInfo { pub struct CDL_TCBExtraInfo {
@@ -536,6 +543,7 @@ pub enum CDL_FrameFill_BootInfoEnum_t {
CDL_FrameFill_BootInfo_X86_Framebuffer = SEL4_BOOTINFO_HEADER_X86_FRAMEBUFFER as isize, CDL_FrameFill_BootInfo_X86_Framebuffer = SEL4_BOOTINFO_HEADER_X86_FRAMEBUFFER as isize,
CDL_FrameFill_BootInfo_X86_TSC_Freq = SEL4_BOOTINFO_HEADER_X86_TSC_FREQ as isize, CDL_FrameFill_BootInfo_X86_TSC_Freq = SEL4_BOOTINFO_HEADER_X86_TSC_FREQ as isize,
CDL_FrameFill_BootInfo_FDT = SEL4_BOOTINFO_HEADER_FDT as isize, CDL_FrameFill_BootInfo_FDT = SEL4_BOOTINFO_HEADER_FDT as isize,
CDL_FrameFill_BootInfo_BootInfo = SEL4_BOOTINFO_HEADER_BOOTINFO as isize,
} }
impl From<CDL_FrameFill_BootInfoEnum_t> for usize { impl From<CDL_FrameFill_BootInfoEnum_t> for usize {
fn from(bi_type: CDL_FrameFill_BootInfoEnum_t) -> usize { fn from(bi_type: CDL_FrameFill_BootInfoEnum_t) -> usize {
@@ -666,6 +674,11 @@ impl<'a> CDL_Object {
self.paddr().map_or(false, |v| v != 0) self.paddr().map_or(false, |v| v != 0)
} }
#[inline]
pub fn cnode_has_untyped_memory(&self) -> bool {
unsafe { self.extra.cnode_extra.has_untyped_memory }
}
// TODO(sleffler): maybe assert type_ before referencing union members // TODO(sleffler): maybe assert type_ before referencing union members
// NB: return everything as seL4_Word to minimize conversions // NB: return everything as seL4_Word to minimize conversions
#[inline] #[inline]
@@ -804,6 +817,7 @@ pub union CDL_ObjectExtra {
pub msiirq_extra: CDL_MSIIRQExtraInfo, pub msiirq_extra: CDL_MSIIRQExtraInfo,
pub armirq_extra: CDL_ARMIRQExtraInfo, pub armirq_extra: CDL_ARMIRQExtraInfo,
pub frame_extra: CDL_FrameExtraInfo, pub frame_extra: CDL_FrameExtraInfo,
pub cnode_extra: CDL_CNodeExtraInfo,
// Physical address; only defined for untyped objects. // Physical address; only defined for untyped objects.
pub paddr: seL4_Word, pub paddr: seL4_Word,

View File

@@ -2,9 +2,11 @@
use crate::KataOsModel; use crate::KataOsModel;
use capdl::kobject_t::KOBJECT_FRAME; use capdl::kobject_t::KOBJECT_FRAME;
use capdl::CDL_FrameFillType_t::*;
use capdl::CDL_FrameFill_BootInfoEnum_t::*;
use capdl::CDL_ObjectType::*; use capdl::CDL_ObjectType::*;
use capdl::*; use capdl::*;
use log::{debug, trace}; use log::{debug, info, trace};
use smallvec::SmallVec; use smallvec::SmallVec;
use sel4_sys::seL4_BootInfo; use sel4_sys::seL4_BootInfo;
@@ -58,6 +60,10 @@ impl<'a> KataOsModel<'a> {
// possibility of vpsace_roots spilling to the heap. // possibility of vpsace_roots spilling to the heap.
let mut roots = SmallVec::new(); let mut roots = SmallVec::new();
// Record objects that receive special treatment later on.
let mut bootinfo_frame: CDL_ObjID = CDL_ObjID::MAX;
let mut untyped_cnode: CDL_ObjID = CDL_ObjID::MAX;
// First, allocate most objects and record the cslot locations. // First, allocate most objects and record the cslot locations.
// The exception is ASIDPools, where create_object only allocates // The exception is ASIDPools, where create_object only allocates
// the backing untypeds. // the backing untypeds.
@@ -90,11 +96,40 @@ impl<'a> KataOsModel<'a> {
} }
} }
// Capture VSpace roots for later use. match obj.r#type() {
if obj.r#type() == CDL_TCB { // Capture VSpace roots & TCBs for later use.
if let Some(root_cap) = obj.get_cap_at(CDL_TCB_VTable_Slot) { CDL_TCB => {
roots.push(root_cap.obj_id); if let Some(root_cap) = obj.get_cap_at(CDL_TCB_VTable_Slot) {
roots.push(root_cap.obj_id);
}
} }
// Capture one bootinfo frame for processing below.
CDL_Frame => {
fn is_bootinfo_frame(obj: &CDL_Object) -> bool {
let frame_fill = &obj.frame_fill(0).unwrap();
frame_fill.type_ == CDL_FrameFill_BootInfo &&
frame_fill.get_bootinfo().type_ == CDL_FrameFill_BootInfo_BootInfo
}
if is_bootinfo_frame(obj) {
// NB: can instantiate multiple frames but only one
// CNode can receive the untypeds since we must move
// 'em from the rootserver (since they are "derived").
// XXX maybe just complain & ignore
info!("Found bootinfo Frame at {}", obj_id);
assert!(!is_objid_valid(bootinfo_frame));
bootinfo_frame = obj_id;
}
}
// Look for a CNode associated with any bootinfo frame.
CDL_CNode => {
if obj.cnode_has_untyped_memory() {
if is_objid_valid(untyped_cnode) {
info!("Duplicate bootinfo cnode at {}, prev {}", obj_id, untyped_cnode);
}
untyped_cnode = obj_id;
}
}
_ => {}
} }
// Record the cslot assigned to the object. // Record the cslot assigned to the object.
self.set_orig_cap(obj_id, free_slot); self.set_orig_cap(obj_id, free_slot);
@@ -140,6 +175,15 @@ impl<'a> KataOsModel<'a> {
roots.dedup(); roots.dedup();
self.vspace_roots = roots; self.vspace_roots = roots;
// Record any CNode designated to receive the UntypedMemory caps when
// constructing their CSpace.
// NB: we conditionally assign based on there being a BootInfo frame
// because the UntypedMemory caps are not useful w/o the descriptors.
if is_objid_valid(bootinfo_frame) {
assert!(is_objid_valid(untyped_cnode));
self.untyped_cnode = untyped_cnode;
}
Ok(()) Ok(())
} }

View File

@@ -19,7 +19,7 @@ use core::mem::size_of;
use core::ptr; use core::ptr;
use cpio::CpioNewcReader; use cpio::CpioNewcReader;
use cstr_core::CStr; use cstr_core::CStr;
use log::{debug, error, trace}; use log::{debug, info, error, trace};
use smallvec::SmallVec; use smallvec::SmallVec;
use static_assertions::*; use static_assertions::*;
@@ -127,7 +127,7 @@ const_assert!(seL4_WordBits == 32 || seL4_WordBits == 64);
const CONFIG_CAPDL_LOADER_FILLS_PER_FRAME: usize = 1; const CONFIG_CAPDL_LOADER_FILLS_PER_FRAME: usize = 1;
fn _BIT(bit_num: usize) -> usize { 1 << bit_num } fn BIT(bit_num: usize) -> usize { 1 << bit_num }
fn ROUND_UP(a: usize, b: usize) -> usize { fn ROUND_UP(a: usize, b: usize) -> usize {
if (a % b) == 0 { if (a % b) == 0 {
a a
@@ -193,6 +193,8 @@ pub struct KataOsModel<'a> {
#[cfg(feature = "CONFIG_ARM_SMMU")] #[cfg(feature = "CONFIG_ARM_SMMU")]
sid_number: usize, sid_number: usize,
untyped_cnode: CDL_ObjID,
extended_bootinfo_table: [*const seL4_BootInfoHeader; SEL4_BOOTINFO_HEADER_NUM], extended_bootinfo_table: [*const seL4_BootInfoHeader; SEL4_BOOTINFO_HEADER_NUM],
vspace_roots: SmallVec<[CDL_ObjID; 32]>, // NB: essentially #components vspace_roots: SmallVec<[CDL_ObjID; 32]>, // NB: essentially #components
@@ -219,6 +221,8 @@ impl<'a> KataOsModel<'a> {
#[cfg(feature = "CONFIG_ARM_SMMU")] #[cfg(feature = "CONFIG_ARM_SMMU")]
sid_number: 0, sid_number: 0,
untyped_cnode: CDL_ObjID::MAX,
extended_bootinfo_table: [ptr::null(); SEL4_BOOTINFO_HEADER_NUM], extended_bootinfo_table: [ptr::null(); SEL4_BOOTINFO_HEADER_NUM],
vspace_roots: SmallVec::new(), vspace_roots: SmallVec::new(),
@@ -255,6 +259,19 @@ impl<'a> KataOsModel<'a> {
Ok(()) Ok(())
} }
pub fn handoff_capabilities(&mut self) -> seL4_Result {
// Hand-off capabilities needed to dynamically create seL4 objects.
// The MemoryManager needs the UntypedMemory slabs to instantiate
// objects. The ProcessManager needs various other object(s) to
// configure TCB's but those are expressed with capDL and moved
// as part of the work done by init_cspsace.
if is_objid_valid(self.untyped_cnode) {
self.handoff_untypeds(self.untyped_cnode)?;
}
Ok(())
}
pub fn start_threads(&self) -> seL4_Result { pub fn start_threads(&self) -> seL4_Result {
trace!("Starting threads..."); trace!("Starting threads...");
for (obj_id, obj) in self.spec.obj_slice().iter().enumerate() { for (obj_id, obj) in self.spec.obj_slice().iter().enumerate() {
@@ -468,6 +485,7 @@ impl<'a> KataOsModel<'a> {
let endpoint_obj = endpoint_cap.obj_id; let endpoint_obj = endpoint_cap.obj_id;
let badge = endpoint_cap.cap_data(); let badge = endpoint_cap.cap_data();
let endpoint_cptr = if badge != 0 { let endpoint_cptr = if badge != 0 {
// Endpoint needs badging, mint a new cap
self.mint_cap(endpoint_obj, badge)? self.mint_cap(endpoint_obj, badge)?
} else { } else {
self.get_orig_cap(endpoint_obj) self.get_orig_cap(endpoint_obj)
@@ -554,7 +572,8 @@ impl<'a> KataOsModel<'a> {
match bi.type_ { match bi.type_ {
CDL_FrameFill_BootInfo_X86_VBE CDL_FrameFill_BootInfo_X86_VBE
| CDL_FrameFill_BootInfo_X86_TSC_Freq | CDL_FrameFill_BootInfo_X86_TSC_Freq
| CDL_FrameFill_BootInfo_FDT => {} | CDL_FrameFill_BootInfo_FDT
| CDL_FrameFill_BootInfo_BootInfo => {}
_ => { _ => {
panic!("Unsupported bootinfo frame fill type {:?}", bi.type_) panic!("Unsupported bootinfo frame fill type {:?}", bi.type_)
} }
@@ -566,8 +585,10 @@ impl<'a> KataOsModel<'a> {
let header: *const seL4_BootInfoHeader = let header: *const seL4_BootInfoHeader =
self.extended_bootinfo_table[usize::from(bi.type_)]; self.extended_bootinfo_table[usize::from(bi.type_)];
/* Check if the bootinfo has been found */ // Check if the bootinfo has been found.
if header.is_null() { if bi.type_ == CDL_FrameFill_BootInfo_BootInfo {
self.fill_with_bootinfo(dest, max_len);
} else if header.is_null() {
/* No bootinfo. /* No bootinfo.
* If we're at the start of a block, copy an empty header across, otherwise skip the copy */ * If we're at the start of a block, copy an empty header across, otherwise skip the copy */
if block_offset == 0 { if block_offset == 0 {
@@ -595,6 +616,35 @@ impl<'a> KataOsModel<'a> {
} }
} }
// Fill with our BootInfo patched per the destination cnode.
fn fill_with_bootinfo(&self, dest: *mut u8, max_len: usize) {
unsafe {
ptr::copy_nonoverlapping(
ptr::addr_of!(self.bootinfo.extraLen) as *const u8, dest as _, max_len)
}
assert!(is_objid_valid(self.untyped_cnode));
let cnode = &self.spec.obj_slice()[self.untyped_cnode];
assert_eq!(cnode.r#type(), CDL_CNode);
// NB: page-aligned so safe to deref.
let bootinfo = unsafe { &mut *(dest as *mut seL4_BootInfo) };
// UntypedMemory caps are appended to the specified slots.
bootinfo.untyped = sel4_sys::seL4_SlotRegion {
start: cnode.num_slots() + 1,
end: cnode.num_slots() + 1 +
(self.bootinfo.untyped.end - self.bootinfo.untyped.start),
};
// Update the empty region to support dynamic cap allocation.
bootinfo.empty = sel4_sys::seL4_SlotRegion {
start: bootinfo.untyped.end + 1,
end: BIT(cnode.size_bits()),
};
// NB: we could adjust the UntypedDesc's but they will not
// reflect rootserver resources that are released at exit
}
// Fill a frame's contents from a file in the cpio archive; // Fill a frame's contents from a file in the cpio archive;
// in particular this loads each CAmkES component's executable. // in particular this loads each CAmkES component's executable.
fn fill_frame_with_filedata(&self, base: usize, frame_fill: &CDL_FrameFill_Element_t) { fn fill_frame_with_filedata(&self, base: usize, frame_fill: &CDL_FrameFill_Element_t) {
@@ -880,6 +930,53 @@ impl<'a> KataOsModel<'a> {
Ok(()) Ok(())
} }
fn handoff_untypeds(&mut self, cnode_obj_id: CDL_ObjID) -> seL4_Result {
let num_untypeds = self.bootinfo.untyped.end - self.bootinfo.untyped.start;
// NB: UntypedMemory caps are appended to the CAmkES-generated slots
let dest_start = self.spec.obj_slice()[cnode_obj_id].num_slots() + 1;
info!("Hand-off {} untypeds from {} to {}",
num_untypeds,
self.bootinfo.untyped.start,
dest_start);
// NB: we let kernel tell us if the CNode is too small.
for ut in 0..num_untypeds {
self.handoff_cap(cnode_obj_id,
/*src_index=*/ self.bootinfo.untyped.start + ut,
/*dest_index=*/ dest_start + ut)?;
}
Ok(())
}
fn handoff_cap(
&mut self,
cnode_obj_id: CDL_ObjID,
src_index: seL4_CPtr,
dest_index: seL4_CPtr
) -> seL4_Result {
let cnode = &self.spec.obj_slice()[cnode_obj_id];
assert_eq!(cnode.r#type(), CDL_CNode);
let src_root = seL4_CapInitThreadCNode;
let src_depth = seL4_WordBits as u8;
// Blindly use the dup'd cap a la init_cnode_slot.
let dest_root = self.get_dup_cap(cnode_obj_id);
let dest_depth: u8 = cnode.size_bits.try_into().unwrap();
unsafe {
seL4_CNode_Move(
dest_root,
dest_index,
dest_depth,
src_root,
src_index,
src_depth,
)
}
}
fn init_cnode_slot( fn init_cnode_slot(
&self, &self,
mode: InitCnodeCmode, mode: InitCnodeCmode,

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_FRAMEBUFFER: usize = 4;
pub const SEL4_BOOTINFO_HEADER_X86_TSC_FREQ: usize = 5; pub const SEL4_BOOTINFO_HEADER_X86_TSC_FREQ: usize = 5;
pub const SEL4_BOOTINFO_HEADER_FDT: usize = 6; pub const SEL4_BOOTINFO_HEADER_FDT: usize = 6;
pub const SEL4_BOOTINFO_HEADER_NUM: usize = SEL4_BOOTINFO_HEADER_FDT + 1; pub const SEL4_BOOTINFO_HEADER_BOOTINFO: usize = 7; // Copy of rootserver's BootInfo
pub const SEL4_BOOTINFO_HEADER_NUM: usize = SEL4_BOOTINFO_HEADER_BOOTINFO + 1;

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/DebugConsole/DebugConsole.camkes";
import "components/ProcessManager/ProcessManager.camkes"; import "components/ProcessManager/ProcessManager.camkes";
import "components/MlCoordinator/MlCoordinator.camkes"; import "components/MlCoordinator/MlCoordinator.camkes";
import "components/MemoryManager/MemoryManager.camkes";
import "components/StorageManager/StorageManager.camkes"; import "components/StorageManager/StorageManager.camkes";
import "components/SecurityCoordinator/SecurityCoordinator.camkes"; import "components/SecurityCoordinator/SecurityCoordinator.camkes";
@@ -54,6 +55,7 @@ assembly {
component OpenTitanUART uart; component OpenTitanUART uart;
component OpenTitanUARTDriver uart_driver; component OpenTitanUARTDriver uart_driver;
component MemoryManager memory_manager;
component ProcessManager process_manager; component ProcessManager process_manager;
component MlCoordinator ml_coordinator; component MlCoordinator ml_coordinator;
component DebugConsole debug_console; component DebugConsole debug_console;
@@ -98,6 +100,15 @@ assembly {
connection seL4RPCCall shell_storage(from debug_console.storage, connection seL4RPCCall shell_storage(from debug_console.storage,
to storage_manager.storage); to storage_manager.storage);
// Connect the MemoryInterface to each component that needs to allocate
// global memory. Note this allocates a 4KB shared memory region to each
// component and copies data between components.
connection seL4RPCOverMultiSharedData multi_memory(
from debug_console.memory,
// TODO(sleffler): from process_manager.memory,
// TOOD(sleffler): from ml_coordinator.memory,
to memory_manager.memory);
// Connect the SecurityCoordinatorInterface to each component that needs // Connect the SecurityCoordinatorInterface to each component that needs
// access to the Security Core. Note this allocates a 4KB shared memory // access to the Security Core. Note this allocates a 4KB shared memory
// region to each component and copies data between components. // region to each component and copies data between components.
@@ -124,6 +135,7 @@ assembly {
connection seL4RPCOverMultiSharedData multi_logger( connection seL4RPCOverMultiSharedData multi_logger(
from process_manager.logger, from process_manager.logger,
from ml_coordinator.logger, from ml_coordinator.logger,
from memory_manager.logger,
from security_coordinator.logger, from security_coordinator.logger,
from storage_manager.logger, from storage_manager.logger,
to debug_console.logger); to debug_console.logger);

View File

@@ -1,6 +1,6 @@
set(CAMKES_APP "system" CACHE STRING "The one and only CAmkES application in this project") set(CAMKES_APP "system" CACHE STRING "The one and only CAmkES application in this project")
set(CAPDL_LOADER_APP "capdl-loader-app" CACHE STRING "") #set(CAPDL_LOADER_APP "capdl-loader-app" CACHE STRING "")
#set(CAPDL_LOADER_APP "kata-os-rootserver" CACHE STRING "") set(CAPDL_LOADER_APP "kata-os-rootserver" CACHE STRING "")
set(PLATFORM "sparrow" CACHE STRING "The one and only seL4 platform for Sparrow") set(PLATFORM "sparrow" CACHE STRING "The one and only seL4 platform for Sparrow")
set(KernelSel4Arch "riscv32" CACHE STRING "Specifies 32-bit branch of the seL4 spike platform") set(KernelSel4Arch "riscv32" CACHE STRING "Specifies 32-bit branch of the seL4 spike platform")