Skip to content

Commit

Permalink
Add serial-device example
Browse files Browse the repository at this point in the history
Signed-off-by: Nick Spinale <[email protected]>
  • Loading branch information
nspin committed Mar 20, 2024
1 parent 03a32c5 commit 466c8de
Show file tree
Hide file tree
Showing 7 changed files with 361 additions and 0 deletions.
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ members = [
"crates/examples/root-task/example-root-task",
"crates/examples/root-task/example-root-task-without-runtime",
"crates/examples/root-task/hello",
"crates/examples/root-task/serial-device",
"crates/examples/root-task/spawn-task",
"crates/examples/root-task/spawn-task/child",
"crates/examples/root-task/spawn-thread",
Expand Down
18 changes: 18 additions & 0 deletions crates/examples/root-task/serial-device/Cargo.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#
# Copyright 2024, Colias Group, LLC
#
# SPDX-License-Identifier: BSD-2-Clause
#

{ mk, versions, localCrates }:

mk {
package.name = "serial-device";
dependencies = {
inherit (versions) tock-registers;
inherit (localCrates)
sel4
sel4-root-task
;
};
}
22 changes: 22 additions & 0 deletions crates/examples/root-task/serial-device/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#
# Copyright 2023, Colias Group, LLC
#
# SPDX-License-Identifier: BSD-2-Clause
#
#
# This file is generated from './Cargo.nix'. You can edit this file directly
# if you are not using this project's Cargo manifest management tools.
# See 'hacking/cargo-manifest-management/README.md' for more information.
#

[package]
name = "serial-device"
version = "0.1.0"
authors = ["Nick Spinale <[email protected]>"]
edition = "2021"
license = "BSD-2-Clause"

[dependencies]
sel4 = { path = "../../../sel4" }
sel4-root-task = { path = "../../../sel4-root-task" }
tock-registers = "0.8.1"
87 changes: 87 additions & 0 deletions crates/examples/root-task/serial-device/src/device.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//
// Copyright 2024, Colias Group, LLC
//
// SPDX-License-Identifier: BSD-2-Clause
//

use core::ops::Deref;

use tock_registers::interfaces::{Readable, Writeable};
use tock_registers::registers::{ReadOnly, ReadWrite, WriteOnly};
use tock_registers::{register_bitfields, register_structs};

register_structs! {
#[allow(non_snake_case)]
pub RegisterBlock {
(0x000 => DR: ReadWrite<u8>),
(0x001 => _reserved0),
(0x018 => FR: ReadOnly<u32, FR::Register>),
(0x01c => _reserved1),
(0x038 => IMSC: ReadWrite<u32, IMSC::Register>),
(0x03c => _reserved2),
(0x044 => ICR: WriteOnly<u32, ICR::Register>),
(0x048 => @END),
}
}

register_bitfields! {
u32,

FR [
TXFF OFFSET(5) NUMBITS(1) [],
RXFE OFFSET(4) NUMBITS(1) [],
],

IMSC [
RXIM OFFSET(4) NUMBITS(1) [],
],

ICR [
ALL OFFSET(0) NUMBITS(11) [],
],
}

pub struct Device {
ptr: *mut RegisterBlock,
}

impl Device {
pub unsafe fn new(ptr: *mut RegisterBlock) -> Self {
Self { ptr }
}

fn ptr(&self) -> *mut RegisterBlock {
self.ptr
}

pub fn init(&self) {
self.IMSC.write(IMSC::RXIM::SET);
}

pub fn put_char(&self, c: u8) {
while self.FR.matches_all(FR::TXFF::SET) {
core::hint::spin_loop();
}
self.DR.set(c)
}

pub fn get_char(&self) -> Option<u8> {
if self.FR.matches_all(FR::RXFE::CLEAR) {
Some(self.DR.get())
} else {
None
}
}

pub fn clear_all_interrupts(&self) {
self.ICR.write(ICR::ALL::SET);
}
}

impl Deref for Device {
type Target = RegisterBlock;

fn deref(&self) -> &Self::Target {
unsafe { &*self.ptr() }
}
}
213 changes: 213 additions & 0 deletions crates/examples/root-task/serial-device/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
//
// Copyright 2024, Colias Group, LLC
//
// SPDX-License-Identifier: BSD-2-Clause
//

#![no_std]
#![no_main]

use core::ptr;

use sel4::CapTypeForObjectOfFixedSize;
use sel4_root_task::{root_task, Never};

mod device;

use device::{Device, RegisterBlock};

const SERIAL_DEVICE_IRQ: usize = 33;

const SERIAL_DEVICE_BASE_ADDR: usize = 0x0900_0000;

#[root_task]
fn main(bootinfo: &sel4::BootInfoPtr) -> sel4::Result<Never> {
let mut empty_slots = bootinfo
.empty()
.range()
.map(sel4::init_thread::Slot::from_index);

let kernel_ut = find_largest_kernel_untyped(bootinfo);

let irq_handler_cap = empty_slots
.next()
.unwrap()
.downcast::<sel4::cap_type::IrqHandler>()
.cap();

sel4::init_thread::slot::IRQ_CONTROL
.cap()
.irq_control_get(
SERIAL_DEVICE_IRQ.try_into().unwrap(),
&sel4::init_thread::slot::CNODE
.cap()
.relative(irq_handler_cap),
)
.unwrap();

let irq_notification_slot = empty_slots
.next()
.unwrap()
.downcast::<sel4::cap_type::Notification>();

kernel_ut
.untyped_retype(
&sel4::ObjectBlueprint::Notification,
&sel4::init_thread::slot::CNODE.cap().relative_self(),
irq_notification_slot.index(),
1,
)
.unwrap();

let irq_notification_cap = irq_notification_slot.cap();

irq_handler_cap
.irq_handler_set_notification(irq_notification_cap)
.unwrap();

let (device_ut_ix, device_ut_desc) = bootinfo
.untyped_list()
.iter()
.enumerate()
.find(|(_i, desc)| {
(desc.paddr()..(desc.paddr() + (1 << desc.size_bits())))
.contains(&SERIAL_DEVICE_BASE_ADDR)
})
.unwrap();

assert!(device_ut_desc.is_device());

let device_ut_cap = bootinfo.untyped().index(device_ut_ix).cap();

trim_untyped(
device_ut_cap,
device_ut_desc.paddr(),
SERIAL_DEVICE_BASE_ADDR,
empty_slots.next().unwrap(),
empty_slots.next().unwrap(),
);

let serial_device_frame_slot = empty_slots
.next()
.unwrap()
.downcast::<sel4::cap_type::Granule>();

device_ut_cap
.untyped_retype(
&sel4::cap_type::Granule::object_blueprint(),
&sel4::init_thread::slot::CNODE.cap().relative_self(),
serial_device_frame_slot.index(),
1,
)
.unwrap();

let serial_device_frame_cap = serial_device_frame_slot.cap();

let serial_device_frame_addr = init_free_page_addr(bootinfo);

serial_device_frame_cap
.frame_map(
sel4::init_thread::slot::VSPACE.cap(),
serial_device_frame_addr,
sel4::CapRights::read_write(),
sel4::VmAttributes::default(),
)
.unwrap();

let serial_device = unsafe { Device::new(serial_device_frame_addr as *mut RegisterBlock) };

serial_device.init();

for c in b"echo> ".iter() {
serial_device.put_char(*c);
}

loop {
serial_device.clear_all_interrupts();
irq_handler_cap.irq_handler_ack().unwrap();

irq_notification_cap.wait();

while let Some(c) = serial_device.get_char() {
serial_device.put_char(b'[');
serial_device.put_char(c);
serial_device.put_char(b']');
}
}
}

// // //

fn find_largest_kernel_untyped(bootinfo: &sel4::BootInfo) -> sel4::cap::Untyped {
let (ut_ix, _desc) = bootinfo
.untyped_list()
.iter()
.enumerate()
.filter(|(_i, desc)| !desc.is_device())
.max_by_key(|(_i, desc)| desc.size_bits())
.unwrap();

bootinfo.untyped().index(ut_ix).cap()
}

// // //

fn trim_untyped(
ut: sel4::cap::Untyped,
ut_paddr: usize,
target_paddr: usize,
free_slot_a: sel4::init_thread::Slot,
free_slot_b: sel4::init_thread::Slot,
) {
let rel_a = sel4::init_thread::slot::CNODE
.cap()
.relative(free_slot_a.cptr());
let rel_b = sel4::init_thread::slot::CNODE
.cap()
.relative(free_slot_b.cptr());
let mut cur_paddr = ut_paddr;
while cur_paddr != target_paddr {
let size_bits = (target_paddr - cur_paddr).ilog2().try_into().unwrap();
ut.untyped_retype(
&sel4::ObjectBlueprint::Untyped { size_bits },
&sel4::init_thread::slot::CNODE.cap().relative_self(),
free_slot_b.index(),
1,
)
.unwrap();
rel_a.delete().unwrap();
rel_a.move_(&rel_b).unwrap();
cur_paddr += 1 << size_bits;
}
}

// // //

#[repr(C, align(4096))]
struct FreePagePlaceHolder(#[allow(dead_code)] [u8; GRANULE_SIZE]);

static mut FREE_PAGE_PLACEHOLDER: FreePagePlaceHolder = FreePagePlaceHolder([0; GRANULE_SIZE]);

fn init_free_page_addr(bootinfo: &sel4::BootInfo) -> usize {
let addr = unsafe { ptr::addr_of!(FREE_PAGE_PLACEHOLDER) as usize };
get_user_image_frame_slot(bootinfo, addr)
.cap()
.frame_unmap()
.unwrap();
addr
}

fn get_user_image_frame_slot(
bootinfo: &sel4::BootInfo,
addr: usize,
) -> sel4::init_thread::Slot<sel4::cap_type::Granule> {
extern "C" {
static __executable_start: usize;
}
let user_image_addr = unsafe { ptr::addr_of!(__executable_start) as usize };
bootinfo
.user_image_frames()
.index(addr / GRANULE_SIZE - user_image_addr / GRANULE_SIZE)
}

const GRANULE_SIZE: usize = sel4::FrameObjectType::GRANULE.bytes();
11 changes: 11 additions & 0 deletions hacking/nix/scope/world/instances/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ in rec {
examples.root-task.example-root-task-without-runtime
examples.root-task.spawn-thread
examples.root-task.spawn-task
examples.root-task.serial-device
];

allAutomationScripts = map
Expand Down Expand Up @@ -345,6 +346,16 @@ in rec {
};
});

serial-device = maybe (haveFullRuntime && hostPlatform.isAarch) (mkInstance {
rootTask = mkTask {
rootCrate = crates.serial-device;
release = false;
};
extraPlatformArgs = lib.optionalAttrs canSimulate {
canAutomateSimply = true;
};
});

spawn-thread = maybe haveFullRuntime (mkInstance {
rootTask = mkTask {
rootCrate = crates.spawn-thread;
Expand Down

0 comments on commit 466c8de

Please sign in to comment.