Compare commits

..

21 Commits

Author SHA1 Message Date
bf6297c5ac feat: root: add load root_server 2024-06-15 17:50:25 +08:00
61fefde82c fix: root: alloc_objects: range boundary judge error 2024-06-15 17:49:10 +08:00
705fb217fd feat: root: create_objects done 2024-06-15 17:47:37 +08:00
fe30d8d28c feat: arch: riscv: vspace: map_range: pass alloc_page as closure 2024-06-15 17:46:02 +08:00
eb70cd7b73 chore: root: log object slot 2024-06-15 17:44:59 +08:00
2bf8e2341d chore: tools: parse_backtrace: just append info 2024-06-15 16:16:30 +08:00
0af7025264 feat: objects: untyped: use bits instead of size 2024-06-15 16:14:33 +08:00
e3be1c56cd feat: tools: add address parser 2024-06-15 14:42:54 +08:00
7757a174ce fix: backtrace: latest Rust will make ra = 0xffffffff when getting the first frame 2024-06-14 22:37:33 +08:00
27deb3302a fix: utils: addr: add missing methods 2024-06-14 21:15:57 +08:00
ad4da2a7e4 feat: riscv: vspace: implement copy_kernel_pagetable and drop KERNEL_PAGETABLE_SIZE 2024-06-14 21:15:08 +08:00
97392a5788 feat: riscv: vspace: always use virtual address with page table 2024-06-14 21:13:11 +08:00
8824345b17 fix: riscv: ld: linker will not increment dot when handling .tbss, manually set dot to TLS_END for getting the correct KERNEL_END 2024-06-14 21:11:42 +08:00
c451aa4c76 fix: vspace: entry: PTEFlags -> MapAttr: RWX must all be empty when indicating a page table 2024-06-14 21:06:09 +08:00
42a7ea89c5 feat: vspace: table: allow to get table vaddr 2024-06-14 16:44:13 +08:00
03aa0edcc8 feat: vspace: entry: add clear api 2024-06-14 16:25:16 +08:00
8fe47c5728 feat: root: initial root server setup logic 2024-06-14 15:36:55 +08:00
da05567fbf chore: uapi: cap: rename PageTable to Table 2024-06-14 15:34:24 +08:00
302de5eae1 feat: uapi: add initial cspace layout 2024-06-14 15:33:19 +08:00
a37efbd13f chore: makefile: fmt 2024-06-14 15:31:39 +08:00
f4bb68a6f4 fix: sync with latest Rust 2024-06-14 14:29:46 +08:00
27 changed files with 536 additions and 65 deletions

44
Cargo.lock generated
View File

@ -19,9 +19,9 @@ dependencies = [
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.2.0" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]] [[package]]
name = "bit_field" name = "bit_field"
@ -56,9 +56,9 @@ dependencies = [
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.4.0" version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
] ]
@ -69,6 +69,12 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
[[package]]
name = "elf"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b"
[[package]] [[package]]
name = "embedded-hal" name = "embedded-hal"
version = "1.0.0" version = "1.0.0"
@ -83,9 +89,9 @@ checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67"
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.28" version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
dependencies = [ dependencies = [
"crc32fast", "crc32fast",
"miniz_oxide", "miniz_oxide",
@ -98,6 +104,8 @@ dependencies = [
"allocator", "allocator",
"bitflags 2.5.0", "bitflags 2.5.0",
"cfg-if", "cfg-if",
"cpio",
"elf",
"fdt", "fdt",
"log", "log",
"num-derive", "num-derive",
@ -114,9 +122,9 @@ dependencies = [
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.11" version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"scopeguard", "scopeguard",
@ -130,9 +138,9 @@ checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.7.2" version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae"
dependencies = [ dependencies = [
"adler", "adler",
] ]
@ -150,18 +158,18 @@ dependencies = [
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.18" version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [ dependencies = [
"autocfg", "autocfg",
] ]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.79" version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -196,9 +204,9 @@ dependencies = [
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.15" version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
[[package]] [[package]]
name = "sbi-rt" name = "sbi-rt"
@ -238,9 +246,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.58" version = "2.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View File

@ -41,6 +41,9 @@ clippy:
cargo clippy -p utils cargo clippy -p utils
cargo clippy --no-deps $(CARGO_TARGET_ARGS) --manifest-path=kernel/Cargo.toml cargo clippy --no-deps $(CARGO_TARGET_ARGS) --manifest-path=kernel/Cargo.toml
fmt:
cargo fmt --all
test: test:
cargo test -p allocator cargo test -p allocator
cargo test -p utils cargo test -p utils
@ -64,4 +67,4 @@ qemu: kernel
qemu-gdb: kernel qemu-gdb: kernel
$(QEMU) $(QEMU_ARGS) -s -S $(QEMU) $(QEMU_ARGS) -s -S
.PHONY: kernel clippy test kernel-test-dump kernel-asm build-target clean qemu qemu-gdb .PHONY: kernel clippy fmt test kernel-test-dump kernel-asm build-target clean qemu qemu-gdb

View File

@ -20,12 +20,15 @@ riscv = []
"riscv.board.virt" = ["riscv.riscv64"] "riscv.board.virt" = ["riscv.riscv64"]
[dependencies] [dependencies]
uapi = { path = "../uapi" }
allocator = { path = "../lib/allocator" } allocator = { path = "../lib/allocator" }
cpio = { path = "../lib/cpio" }
tracer = { path = "../lib/tracer" }
uapi = { path = "../uapi" }
utils = { path = "../lib/utils", default-features = false } utils = { path = "../lib/utils", default-features = false }
bitflags = "2.5" bitflags = "2.5"
cfg-if = "1.0" cfg-if = "1.0"
elf = { version = "0.7", default-features = false }
fdt = "0.1" fdt = "0.1"
log = "0.4" log = "0.4"
num-derive = "0.4" num-derive = "0.4"

View File

@ -18,7 +18,11 @@ impl FrameWalker {
fn is_valid(&self) -> bool { fn is_valid(&self) -> bool {
let fp_valid = unsafe { BSS_START.as_virt_addr() <= self.current_fp && self.current_fp < BSS_END.as_virt_addr() }; let fp_valid = unsafe { BSS_START.as_virt_addr() <= self.current_fp && self.current_fp < BSS_END.as_virt_addr() };
let pc_valid = unsafe { TEXT_START.as_virt_addr() <= self.current_pc && self.current_pc < TEXT_END.as_virt_addr() }; let pc_valid = unsafe { TEXT_START.as_virt_addr() <= self.current_pc && self.current_pc < TEXT_END.as_virt_addr() };
fp_valid && pc_valid
// pc might be 0xffff_ffff in the first frame
let first = self.current_pc == VirtAddr(usize::MAX);
fp_valid && (pc_valid || first)
} }
pub fn new() -> Self { pub fn new() -> Self {

View File

@ -34,7 +34,7 @@ unsafe extern "C" fn _start(hart_id: usize, fdt_addr: usize) -> ! {
ld t0, 0(t0) ld t0, 0(t0)
# load and relocate registers # load and relocate registers
lla tp, TSS_START lla tp, TLS_START
lla sp, {stack} + {stack_size} lla sp, {stack} + {stack_size}
add gp, gp, t0 add gp, gp, t0
add tp, tp, t0 add tp, tp, t0
@ -72,7 +72,7 @@ unsafe fn pre_main(hart_id: usize, fdt_addr: usize) {
setup_memory(fdt_addr); setup_memory(fdt_addr);
setup_kernel_paging(); setup_kernel_paging();
// TODO: on secondary cpu, we should copy existing kernel page table and TSS, then remap TSS // TODO: on secondary cpu, we should copy existing kernel page table and TLS, then remap TLS
install_kernel_pagetable(); install_kernel_pagetable();
// after kernel paging, board level early console is broken (no address mapping) // after kernel paging, board level early console is broken (no address mapping)

View File

@ -22,8 +22,8 @@ extern "C" {
pub static BOOT_STACK_END: ExternSymbol; pub static BOOT_STACK_END: ExternSymbol;
pub static BSS_END: ExternSymbol; pub static BSS_END: ExternSymbol;
pub static TSS_START: ExternSymbol; pub static TLS_START: ExternSymbol;
pub static TSS_END: ExternSymbol; pub static TLS_END: ExternSymbol;
pub static TDATA_START: ExternSymbol; pub static TDATA_START: ExternSymbol;
pub static TDATA_END: ExternSymbol; pub static TDATA_END: ExternSymbol;
@ -33,6 +33,7 @@ extern "C" {
} }
pub const PAGE_SIZE: usize = 4 * KIB; pub const PAGE_SIZE: usize = 4 * KIB;
pub const PAGE_BITS: usize = PAGE_SIZE.ilog2() as usize;
pub const PAGE_LAYOUT: Layout = unsafe { Layout::from_size_align_unchecked(PAGE_SIZE, PAGE_SIZE) }; pub const PAGE_LAYOUT: Layout = unsafe { Layout::from_size_align_unchecked(PAGE_SIZE, PAGE_SIZE) };
#[inline(always)] #[inline(always)]
@ -53,6 +54,11 @@ pub fn zero_bss() {
} }
} }
/* NOTE:
* `kernel_phys_to_virt` and `kernel_virt_to_phys` should only be used when interacting with `ExternSymbol`,
* for other cases, use `mmap_phys_to_virt` and `mmap_virt_to_phys` instead.
*/
pub unsafe fn kernel_phys_to_virt(phys: PhysAddr) -> VirtAddr { pub unsafe fn kernel_phys_to_virt(phys: PhysAddr) -> VirtAddr {
VirtAddr(phys.as_usize() + KERNEL_OFFSET) VirtAddr(phys.as_usize() + KERNEL_OFFSET)
} }

View File

@ -50,8 +50,8 @@ SECTIONS {
} }
. = ALIGN(PAGE_SIZE); . = ALIGN(PAGE_SIZE);
.tss : AT(ADDR(.tss) - __kernel_offset) { .tls : AT(ADDR(.tls) - __kernel_offset) {
TSS_START = .; TLS_START = .;
. = ALIGN(8); . = ALIGN(8);
TDATA_START = .; TDATA_START = .;
@ -63,9 +63,10 @@ SECTIONS {
*(.tbss .tbss.*) *(.tbss .tbss.*)
TBSS_END = .; TBSS_END = .;
TSS_END = .; TLS_END = .;
} }
. = TLS_END;
. = ALIGN(PAGE_SIZE); . = ALIGN(PAGE_SIZE);
KERNEL_END = .; KERNEL_END = .;

View File

@ -55,8 +55,8 @@ SECTIONS {
} }
. = ALIGN(PAGE_SIZE); . = ALIGN(PAGE_SIZE);
.tss : AT(ADDR(.tss) - __kernel_offset) { .tls : AT(ADDR(.tls) - __kernel_offset) {
TSS_START = .; TLS_START = .;
. = ALIGN(8); . = ALIGN(8);
TDATA_START = .; TDATA_START = .;
@ -68,9 +68,10 @@ SECTIONS {
*(.tbss .tbss.*) *(.tbss .tbss.*)
TBSS_END = .; TBSS_END = .;
TSS_END = .; TLS_END = .;
} }
. = TLS_END;
. = ALIGN(PAGE_SIZE); . = ALIGN(PAGE_SIZE);
KERNEL_END = .; KERNEL_END = .;

View File

@ -36,7 +36,10 @@ impl From<PTEFlags> for MapAttr {
if flags.contains(PTEFlags::USER_ACCESSIBLE) { if flags.contains(PTEFlags::USER_ACCESSIBLE) {
attr.insert(Self::USER_ACCESSIBLE); attr.insert(Self::USER_ACCESSIBLE);
} }
if flags.contains(PTEFlags::VALID) && !flags.contains(PTEFlags::READABLE | PTEFlags::WRITABLE | PTEFlags::EXECUTABLE) if flags.contains(PTEFlags::VALID)
&& !flags.contains(PTEFlags::READABLE)
&& !flags.contains(PTEFlags::WRITABLE)
&& !flags.contains(PTEFlags::EXECUTABLE)
{ {
attr.insert(Self::PAGE_TABLE); attr.insert(Self::PAGE_TABLE);
} }
@ -117,6 +120,10 @@ impl EntryOps for Entry {
self.0 = (self.0 & !PTEFlags::all().bits()) | flags.bits(); self.0 = (self.0 & !PTEFlags::all().bits()) | flags.bits();
} }
fn clear(&mut self) {
self.0 = 0;
}
fn is_valid(&self) -> bool { fn is_valid(&self) -> bool {
self.0 & PTEFlags::VALID.bits() != 0 self.0 & PTEFlags::VALID.bits() != 0
} }

View File

@ -1,5 +1,5 @@
use super::{alloc_page, install_pagetable, map_range, RAM_ALLOCATOR}; use super::{alloc_page, install_pagetable, map_range, RAM_ALLOCATOR};
use crate::{arch::layout::*, vspace::*}; use crate::{arch::layout::*, objects::*, vspace::*};
use log::{debug, info, trace}; use log::{debug, info, trace};
use spin::Mutex; use spin::Mutex;
use utils::{addr::*, size::*}; use utils::{addr::*, size::*};
@ -10,8 +10,6 @@ use super::ALLOC_COUNT;
#[thread_local] #[thread_local]
static KERNEL_PAGETABLE: Mutex<Option<Table<Level0>>> = Mutex::new(None); static KERNEL_PAGETABLE: Mutex<Option<Table<Level0>>> = Mutex::new(None);
pub const KERNEL_PAGETABLE_SIZE: usize = 3;
pub unsafe fn setup_memory(fdt_addr: usize) { pub unsafe fn setup_memory(fdt_addr: usize) {
info!("Setting up memory"); info!("Setting up memory");
let fdt = unsafe { fdt::Fdt::from_ptr(fdt_addr as *const u8).unwrap() }; let fdt = unsafe { fdt::Fdt::from_ptr(fdt_addr as *const u8).unwrap() };
@ -101,7 +99,7 @@ pub unsafe fn setup_memory(fdt_addr: usize) {
} }
} }
trace!("Memory setup complete, memory info:\n{:#x?}", mem); trace!("Memory setup complete, memory info:\n{:X?}", mem);
} }
pub unsafe fn setup_kernel_paging() { pub unsafe fn setup_kernel_paging() {
@ -109,7 +107,7 @@ pub unsafe fn setup_kernel_paging() {
assert!(!is_kernel_pagetable_allocated(), "Kernel pagetable already allocated"); assert!(!is_kernel_pagetable_allocated(), "Kernel pagetable already allocated");
let root_pt = alloc_page(); let root_pt = alloc_page();
let mut kernel_pt = Table::<Level0>::new(root_pt.as_usize().into()); let mut kernel_pt = Table::<Level0>::new(mmap_phys_to_virt(root_pt));
macro_rules! map_section { macro_rules! map_section {
(($($section:ident),+),$attr:expr) => { (($($section:ident),+),$attr:expr) => {
@ -117,7 +115,7 @@ pub unsafe fn setup_kernel_paging() {
let start = concat_idents!($section, _START).as_virt_addr().align_down(PAGE_SIZE); let start = concat_idents!($section, _START).as_virt_addr().align_down(PAGE_SIZE);
let end = concat_idents!($section, _END).as_virt_addr().align_up(PAGE_SIZE); let end = concat_idents!($section, _END).as_virt_addr().align_up(PAGE_SIZE);
debug!("Mapping section {}:\t[{:?}, {:?}]", stringify!($section), start, end); debug!("Mapping section {}:\t[{:?}, {:?}]", stringify!($section), start, end);
map_range(&mut kernel_pt, start, kernel_virt_to_phys(start), (end - start).as_usize(), $attr); map_range(alloc_page, &mut kernel_pt, start, kernel_virt_to_phys(start), (end - start).as_usize(), $attr);
)+ )+
}; };
} }
@ -126,12 +124,13 @@ pub unsafe fn setup_kernel_paging() {
map_section!((RODATA), MapAttr::READABLE); map_section!((RODATA), MapAttr::READABLE);
map_section!((DATA, BSS), MapAttr::READABLE | MapAttr::WRITABLE); map_section!((DATA, BSS), MapAttr::READABLE | MapAttr::WRITABLE);
// TODO: every core must have a separate TSS section // TODO: every core must have a separate TLS section
map_section!((TSS), MapAttr::READABLE | MapAttr::WRITABLE); map_section!((TLS), MapAttr::READABLE | MapAttr::WRITABLE);
// map 4 GiB physical memory // map 4 GiB physical memory
// TODO: walk fdt to get all memory region? put it in driver init // TODO: walk fdt to get all memory region? put it in driver init
map_range( map_range(
alloc_page,
&mut kernel_pt, &mut kernel_pt,
mmap_phys_to_virt(PhysAddr(0)), mmap_phys_to_virt(PhysAddr(0)),
PhysAddr(0), PhysAddr(0),
@ -143,7 +142,6 @@ pub unsafe fn setup_kernel_paging() {
{ {
let count = ALLOC_COUNT.load(core::sync::atomic::Ordering::Acquire); let count = ALLOC_COUNT.load(core::sync::atomic::Ordering::Acquire);
trace!("Kernel page table size: {:?}", count); trace!("Kernel page table size: {:?}", count);
assert!(KERNEL_PAGETABLE_SIZE == count, "Kernel page table size mismatch");
} }
*KERNEL_PAGETABLE.lock() = Some(kernel_pt); *KERNEL_PAGETABLE.lock() = Some(kernel_pt);
@ -152,10 +150,10 @@ pub unsafe fn setup_kernel_paging() {
pub unsafe fn install_kernel_pagetable() { pub unsafe fn install_kernel_pagetable() {
info!("Setting up new kernel pagetable"); info!("Setting up new kernel pagetable");
let kernel_pt = KERNEL_PAGETABLE.lock(); let vaddr = KERNEL_PAGETABLE.lock().as_ref().expect("No kernel pagetable found").vaddr();
let kernel_pt = kernel_pt.as_ref().expect("No kernel pagetable found"); let paddr = mmap_virt_to_phys(vaddr);
install_pagetable(kernel_pt) install_pagetable(paddr)
} }
#[inline] #[inline]
@ -166,5 +164,23 @@ pub fn is_kernel_pagetable_allocated() -> bool {
unsafe fn map_kernel(from: VirtAddr, to: PhysAddr, size: usize, attr: MapAttr) { unsafe fn map_kernel(from: VirtAddr, to: PhysAddr, size: usize, attr: MapAttr) {
let mut guard = KERNEL_PAGETABLE.lock(); let mut guard = KERNEL_PAGETABLE.lock();
let pt = guard.as_mut().unwrap(); let pt = guard.as_mut().unwrap();
map_range(pt, from, to, size, attr); map_range(alloc_page, pt, from, to, size, attr);
}
pub fn copy_kernel_pagetable(root: &CapEntry) {
let kernel_pt = KERNEL_PAGETABLE.lock();
let from = kernel_pt.as_ref().expect("No kernel pagetable found");
let mut root = TableCap::try_from(root).expect("Invalid vspace cap");
let to: Table<Level0> = root.as_object_mut();
for (i, (from, to)) in from.entries.iter().zip(to.entries.iter_mut()).enumerate() {
if !from.is_valid() {
to.clear();
continue;
}
to.set_attr(from.attr());
to.set_paddr(from.paddr());
}
} }

View File

@ -11,6 +11,10 @@ impl<'a, T: TableLevel> TableOps<'a, T> for Table<'a, T> {
} }
} }
fn vaddr(&self) -> VirtAddr {
VirtAddr::from(self.entries.as_ptr())
}
fn map(&mut self, from: VirtAddr, to: PhysAddr, attr: MapAttr) -> PageResult { fn map(&mut self, from: VirtAddr, to: PhysAddr, attr: MapAttr) -> PageResult {
if !from.is_aligned(T::LEVEL_SIZE) || !to.is_aligned(PAGE_SIZE) { if !from.is_aligned(T::LEVEL_SIZE) || !to.is_aligned(PAGE_SIZE) {
return Err(PageError::NotAligned); return Err(PageError::NotAligned);

View File

@ -28,7 +28,14 @@ pub fn alloc_page() -> PhysAddr {
addr addr
} }
pub fn map_range(pt: &mut Table<Level0>, from: VirtAddr, to: PhysAddr, size: usize, attr: MapAttr) { pub fn map_range(
mut alloc: impl FnMut() -> PhysAddr,
pt: &mut Table<Level0>,
from: VirtAddr,
to: PhysAddr,
size: usize,
attr: MapAttr,
) {
let mut virt_start = from; let mut virt_start = from;
let mut phys_start = to; let mut phys_start = to;
let virt_end = from + size; let virt_end = from + size;
@ -56,7 +63,7 @@ pub fn map_range(pt: &mut Table<Level0>, from: VirtAddr, to: PhysAddr, size: usi
unsafe { unsafe {
Table::<$level>::new(mmap_phys_to_virt( Table::<$level>::new(mmap_phys_to_virt(
$pt.lookup_mut($addr).map(|e| e.paddr()).unwrap_or_else(|| { $pt.lookup_mut($addr).map(|e| e.paddr()).unwrap_or_else(|| {
let page = alloc_page(); let page = alloc();
let addr = $addr.align_down(<$level as TableLevel>::PREVIOUS::LEVEL_SIZE); let addr = $addr.align_down(<$level as TableLevel>::PREVIOUS::LEVEL_SIZE);
debug!("Creating new pagetable:\t{:?} -> {:?}", addr, page); debug!("Creating new pagetable:\t{:?} -> {:?}", addr, page);
$pt.map(addr, page, MapAttr::PAGE_TABLE).unwrap(); $pt.map(addr, page, MapAttr::PAGE_TABLE).unwrap();
@ -89,9 +96,7 @@ fn page_table_mode() -> riscv::register::satp::Mode {
return riscv::register::satp::Mode::Sv57; return riscv::register::satp::Mode::Sv57;
} }
pub unsafe fn install_pagetable(pt: &Table<Level0>) { pub unsafe fn install_pagetable(addr: PhysAddr) {
let root_pt = PhysAddr::from(pt.entries.as_ptr()); riscv::register::satp::set(page_table_mode(), 0, addr.extract_ppn());
riscv::register::satp::set(page_table_mode(), 0, root_pt.extract_ppn());
riscv::asm::sfence_vma_all(); riscv::asm::sfence_vma_all();
} }

View File

@ -15,9 +15,9 @@ fn panic(info: &PanicInfo) -> ! {
location.file(), location.file(),
location.line(), location.line(),
location.column(), location.column(),
info.message().unwrap(), info.message(),
), ),
None => error!("[lang] Panicked: {}", info.message().unwrap()), None => error!("[lang] Panicked: {}", info.message()),
} }
unsafe { dump_backtrace() }; unsafe { dump_backtrace() };

View File

@ -27,6 +27,7 @@ mod lang;
mod logging; mod logging;
mod objects; mod objects;
mod plat; mod plat;
mod root;
mod scheduler; mod scheduler;
mod vspace; mod vspace;

View File

@ -75,7 +75,7 @@ impl Debug for CapEntry {
ObjectType::CNode => write!(f, "{:?}", CNodeCap::try_from(self)), ObjectType::CNode => write!(f, "{:?}", CNodeCap::try_from(self)),
ObjectType::Frame => write!(f, "{:?}", FrameCap::try_from(self)), ObjectType::Frame => write!(f, "{:?}", FrameCap::try_from(self)),
ObjectType::Null => write!(f, "{:?}", NullCap::try_from(self)), ObjectType::Null => write!(f, "{:?}", NullCap::try_from(self)),
ObjectType::PageTable => write!(f, "{:?}", TableCap::try_from(self)), ObjectType::Table => write!(f, "{:?}", TableCap::try_from(self)),
ObjectType::TCB => write!(f, "{:?}", TcbCap::try_from(self)), ObjectType::TCB => write!(f, "{:?}", TcbCap::try_from(self)),
ObjectType::Untyped => write!(f, "{:?}", UntypedCap::try_from(self)), ObjectType::Untyped => write!(f, "{:?}", UntypedCap::try_from(self)),
_ => write!(f, "UnknownCap"), _ => write!(f, "UnknownCap"),

View File

@ -52,10 +52,8 @@ impl<'a> FrameCap<'a> {
const VM_RIGHT_MASK: usize = MASK!(Self::VM_RIGHT_BITS); const VM_RIGHT_MASK: usize = MASK!(Self::VM_RIGHT_BITS);
const VM_RIGHT_OFFSET: usize = 0; const VM_RIGHT_OFFSET: usize = 0;
pub fn mint(ptr: PhysAddr, size: usize, attr: MapAttr, is_device: bool) -> RawCap { pub fn mint(ptr: PhysAddr, size_bits: usize, attr: MapAttr, is_device: bool) -> RawCap {
let size_bits = size.ilog2() as usize; debug_assert!(size_bits <= MASK!(Self::FRAME_SIZE_BITS)); // 1<<63
debug_assert!(size_bits <= FrameCap::FRAME_SIZE_BITS);
assert!(size >= PAGE_SIZE);
let arg0 = 0 let arg0 = 0
| ((attr.bits() & Self::VM_RIGHT_MASK) << Self::VM_RIGHT_OFFSET) | ((attr.bits() & Self::VM_RIGHT_MASK) << Self::VM_RIGHT_OFFSET)

View File

@ -10,7 +10,7 @@ use utils::{addr::*, MASK};
/// TableObject is an object that represents a page table /// TableObject is an object that represents a page table
pub struct TableObject([u8]); pub struct TableObject([u8]);
impl KernelObject for TableObject { impl KernelObject for TableObject {
const OBJ_TYPE: ObjectType = ObjectType::PageTable; const OBJ_TYPE: ObjectType = ObjectType::Table;
} }
/* /*
@ -40,7 +40,7 @@ impl<'a> TableCap<'a> {
const IS_MAPPED_OFFSET: usize = 0; const IS_MAPPED_OFFSET: usize = 0;
pub fn mint(ptr: PhysAddr) -> RawCap { pub fn mint(ptr: PhysAddr) -> RawCap {
RawCap::new(0, 0, ptr, ObjectType::PageTable) RawCap::new(0, 0, ptr, ObjectType::Table)
} }
pub fn is_mapped(&self) -> bool { pub fn is_mapped(&self) -> bool {

View File

@ -1,7 +1,8 @@
use super::cap::RawCap; use super::cap::RawCap;
use super::cnode::{CNodeCap, CNodeObject}; use super::cnode::{CNodeCap, CNodeObject};
use super::null::NullCap; use super::null::NullCap;
use super::{Cap, KernelObject}; use super::{Cap, FrameCap, KernelObject, TableCap, TcbCap};
use crate::vspace::MapAttr;
use core::fmt::Debug; use core::fmt::Debug;
use uapi::cap::ObjectType; use uapi::cap::ObjectType;
use uapi::error::{SysError, SysResult}; use uapi::error::{SysError, SysResult};
@ -54,7 +55,7 @@ impl UntypedCap<'_> {
}); });
} }
fn is_device(&self) -> bool { pub fn is_device(&self) -> bool {
(self.cte.cap.get().args[1] >> 6) & 1 == 1 (self.cte.cap.get().args[1] >> 6) & 1 == 1
} }
@ -107,7 +108,6 @@ impl UntypedCap<'_> {
// Check whether we are handling device memory // Check whether we are handling device memory
if self.is_device() && obj_type != ObjectType::Frame && obj_type != ObjectType::Untyped { if self.is_device() && obj_type != ObjectType::Frame && obj_type != ObjectType::Untyped {
// TODO: we might split frame into tera, giga, mega, kilo...
return Err(SysError::InvalidArgument); return Err(SysError::InvalidArgument);
} }
@ -117,6 +117,9 @@ impl UntypedCap<'_> {
let new_cap = match obj_type { let new_cap = match obj_type {
ObjectType::Untyped => UntypedCap::mint(0, obj_type.bits(user_obj_bits), self.is_device(), addr), ObjectType::Untyped => UntypedCap::mint(0, obj_type.bits(user_obj_bits), self.is_device(), addr),
ObjectType::CNode => CNodeCap::mint(user_obj_bits, 0, 0, addr), ObjectType::CNode => CNodeCap::mint(user_obj_bits, 0, 0, addr),
ObjectType::TCB => TcbCap::mint(addr),
ObjectType::Table => TableCap::mint(addr),
ObjectType::Frame => FrameCap::mint(addr, user_obj_bits, MapAttr::empty(), self.is_device()),
// TODO: other object types // TODO: other object types
_ => return Err(SysError::InvalidArgument), _ => return Err(SysError::InvalidArgument),
}; };

359
kernel/src/root.rs Normal file
View File

@ -0,0 +1,359 @@
use crate::arch::layout::{mmap_phys_to_virt, mmap_virt_to_phys, PAGE_BITS, PAGE_SIZE};
use crate::arch::vspace::{copy_kernel_pagetable, map_range, RAM_ALLOCATOR};
use crate::objects::*;
use crate::vspace::*;
use core::{cmp::min, ops::Range};
use elf::{abi::*, endian::AnyEndian, ElfBytes};
use fdt::Fdt;
use log::{debug, trace};
use spin::Lazy;
use uapi::{cspace::CNodeSlot, error::SysError};
use utils::{addr::*, bin::prev_power_of_two};
const ROOT_CNODE_SIZE: usize = 128;
static mut ROOT_CNODE: Lazy<[CapEntry; ROOT_CNODE_SIZE]> =
Lazy::new(|| core::array::from_fn(|i| CapEntry::new(NullCap::mint())));
pub fn setup_root_server(fdt: &Fdt) {
// Root Server is using statically allocated ROOT_CNODE
// 1. Setup CNodeCap -> self reference
let cnode_entry = {
let radix_bits = ROOT_CNODE_SIZE.ilog2() as usize;
let guard_bits = 0;
let cap_guard = 0;
let ptr = unsafe { mmap_virt_to_phys(VirtAddr::from(ROOT_CNODE.as_ptr())) };
let cap = CNodeCap::mint(radix_bits, guard_bits, cap_guard, ptr);
CapEntry::new(cap)
};
let mut cnode = CNodeCap::try_from(&cnode_entry).unwrap();
let cnode_obj = cnode.as_object_mut();
cnode_obj[CNodeSlot::CNodeCap as usize] = cnode_entry.clone();
// 2. insert current free memory [CNodeSlot::UntypedCap, memory_idx)
let memory_idx = create_untyped_memory(cnode_obj);
// 3. insert device memory [memory_idx, device_idx)
let device_idx = create_untyped_device(cnode_obj, memory_idx, fdt);
// 4. create objects
create_objects(cnode_obj, device_idx);
// 5. load root server
let root_server_idx = load_root_server(cnode_obj, device_idx, fdt);
// 6. map other memory (fdt, initrd)
// 7. setup threads
}
fn create_untyped_memory(cnode_obj: &mut CNodeObject) -> usize {
let mut memory_idx = CNodeSlot::UntypedCap as usize;
let mut ram = RAM_ALLOCATOR.lock();
ram.blocks_mut()
.iter_mut()
.filter(|b| b.is_some())
.map(|b| b.take().unwrap())
.for_each(|b| {
let start = {
let start = b.start_addr();
let aligned = start.align_up(PAGE_SIZE);
if aligned != start {
debug!("[root] wasting memory: {:#x?} -> {:#x?}", start, aligned);
}
aligned
};
let bits = {
let size = b.size();
let aligned = align_down(size, PAGE_SIZE);
if aligned != size {
debug!("[root] wasting memory: {:#x?} -> {:#x?}", start + aligned, start + size);
}
if aligned != 0 {
aligned.ilog2() as usize
} else {
0
}
};
if bits == 0 {
return;
}
trace!(
"[root] creating untyped memory: {}: {:?} -> {:?}, slot #{}",
memory_idx,
start,
start + (1 << bits),
memory_idx
);
let cap = UntypedCap::mint(0, bits, false, start);
let cap_entry = CapEntry::new(cap);
cnode_obj[memory_idx] = cap_entry;
memory_idx += 1;
});
assert!(ram.blocks().iter().all(|b| b.is_none()));
memory_idx
}
fn create_untyped_device(cnode_obj: &mut CNodeObject, memory_idx: usize, fdt: &Fdt) -> usize {
let mut device_idx = memory_idx;
let memory = fdt.memory();
fdt.all_nodes().filter_map(|n| n.reg()).for_each(|r| {
r.filter(|r| r.size.is_some_and(|s| s > 0))
.filter(|r| {
memory.regions().all(|m| {
let m_start = m.starting_address as usize;
let m_end = m_start + m.size.unwrap();
let r_start = r.starting_address as usize;
let r_end = r_start + r.size.unwrap();
r_end <= m_start || m_end <= r_start
})
})
.for_each(|region| {
let start = PhysAddr::from(region.starting_address);
let size = region.size.unwrap();
let bits = size.next_power_of_two().ilog2() as usize;
if (1 << bits) > size {
debug!(
"[root] allocating more device memory: {:#x} -> {:#x}",
start + size,
start + (1 << bits)
);
}
trace!(
"[root] creating untyped device: {}: {:?} -> {:?}, slot #{}",
device_idx,
start,
start + (1 << bits),
device_idx
);
let cap = UntypedCap::mint(0, bits, true, start);
let cap_entry = CapEntry::new(cap);
cnode_obj[device_idx] = cap_entry;
device_idx += 1;
})
});
device_idx
}
fn alloc_objects<T: KernelObject + ?Sized>(
cnode_obj: &mut CNodeObject,
end_idx: usize,
user_obj_bits: usize,
range: Range<usize>,
) {
let untyped_start = CNodeSlot::UntypedCap as usize;
let (memory, slots) = {
let (prologue, remain) = cnode_obj.split_at_mut(untyped_start);
let (memory, epilogue) = remain.split_at_mut(end_idx - untyped_start);
match range {
r if r.end <= untyped_start => (memory, &mut prologue[r]),
r if r.start >= end_idx => (memory, &mut epilogue[(r.start - end_idx)..(r.end - end_idx)]),
_ => panic!("init: slot range overlaps with untyped memory"),
}
};
for untyped in memory {
let mut untyped = UntypedCap::try_from(&*untyped).expect("init: untyped cap is invalid");
if untyped.is_device() {
// all non-device memory are placed before device memory
break;
}
match untyped.retype(T::OBJ_TYPE, user_obj_bits, slots) {
Ok(()) => return,
Err(SysError::RangeError) => continue,
_ => panic!("init: retype failed"),
}
}
panic!("init: untyped memory is exhausted");
}
fn create_objects(cnode_obj: &mut CNodeObject, end_idx: usize) {
// tcb
{
let tcb_idx = CNodeSlot::TcbCap as usize;
trace!("[root] allocating tcb object at slot #{}", tcb_idx);
alloc_objects::<TcbObject>(cnode_obj, end_idx, 0, tcb_idx..tcb_idx + 1);
}
// vspace
{
// root
let vspace_idx = CNodeSlot::VSpaceCap as usize;
trace!("[root] allocating vspace object at slot #{}", vspace_idx);
alloc_objects::<TableObject>(cnode_obj, end_idx, PAGE_BITS, vspace_idx..vspace_idx + 1);
// copy pagetable
let root = &cnode_obj[vspace_idx];
copy_kernel_pagetable(root);
}
// bootinfo
{
let bootinfo_idx = CNodeSlot::BootInfoFrameCap as usize;
trace!("[root] allocating bootinfo object at slot #{}", bootinfo_idx);
alloc_objects::<FrameObject>(cnode_obj, end_idx, PAGE_BITS, bootinfo_idx..bootinfo_idx + 1);
}
}
fn load_root_server(cnode_obj: &mut CNodeObject, end_idx: usize, fdt: &Fdt) -> usize {
let table: Table<Level0> = TableCap::try_from(&cnode_obj[CNodeSlot::VSpaceCap as usize])
.unwrap()
.as_object_mut();
let mut rootfs = unsafe {
let chosen = fdt.find_node("/chosen").expect("/chosen is required");
let initrd_start = chosen
.properties()
.find(|n| n.name == "linux,initrd-start")
.expect("linux,initrd-start is required")
.as_usize()
.unwrap();
let initrd_end = chosen
.properties()
.find(|n| n.name == "linux,initrd-end")
.expect("linux,initrd-end is required")
.as_usize()
.unwrap();
let vaddr = mmap_phys_to_virt(PhysAddr(initrd_start));
let len = initrd_end - initrd_start;
let data = core::slice::from_raw_parts(vaddr.as_const_ptr(), len);
cpio::Reader::new(data)
};
let root_entry = rootfs
.find_map(|entry| {
if let Ok(entry) = entry {
if entry.filename().unwrap_or_default() == "root" {
return Some(entry);
}
}
None
})
.expect("init: root server not found");
let root_start = VirtAddr::from(root_entry.data().as_ptr());
let root_elf = ElfBytes::<AnyEndian>::minimal_parse(root_entry.data()).expect("init: invalid root server");
let mut nxt_slot = end_idx;
root_elf
.segments()
.expect("init: root server has no segments")
.iter()
.filter(|phdr| phdr.p_type == PT_LOAD)
.filter(|phdr| phdr.p_memsz > 0)
.for_each(|phdr| {
debug!(
"[initrd] loading segment:\t[{:#x?}, {:#x?}]",
phdr.p_vaddr,
phdr.p_vaddr + phdr.p_memsz
);
let seg_start = root_start + phdr.p_offset as usize;
let seg_addr = VirtAddr::from(phdr.p_vaddr);
// Currently, we only support loading segments that are aligned to PAGE_SIZE
assert!(seg_addr.is_aligned(PAGE_SIZE), "p_vaddr must be aligned");
assert!(phdr.p_align as usize == PAGE_SIZE, "p_align must be aligned");
assert!(is_aligned(phdr.p_memsz as usize, PAGE_SIZE), "p_memsz must be aligned");
debug_assert!(phdr.p_filesz <= phdr.p_memsz, "invalid ELF segment");
let copy = |offset: usize, dest: &mut [u8]| {
if offset > phdr.p_filesz as usize {
dest.fill(0);
} else {
let len = min(phdr.p_filesz as usize - offset, dest.len());
let src = &root_entry.data()[offset..offset + len];
dest[..len].copy_from_slice(src);
dest[len..].fill(0);
}
};
// allocate frames
let mut cur = 0;
while cur < phdr.p_memsz as usize {
let remain = phdr.p_memsz as usize - cur;
// calculate block size
let block_size = prev_power_of_two(remain as u64) as usize;
let block_bits = block_size.ilog2() as usize;
debug_assert!(block_size >= PAGE_SIZE);
// allocate frame
trace!("[initrd] allocating frame at slot #{}", nxt_slot);
alloc_objects::<FrameObject>(cnode_obj, end_idx, block_bits, nxt_slot..nxt_slot + 1);
nxt_slot += 1;
// copy data
let mut frame = FrameCap::try_from(&cnode_obj[nxt_slot - 1]).unwrap();
let mem = frame.as_object_mut();
copy(cur, mem);
// map frame to vspace
let root = &cnode_obj[CNodeSlot::VSpaceCap as usize];
let root_vaddr = TableCap::try_from(root).unwrap().as_object::<Level0>().vaddr();
let mut root = unsafe { Table::new(root_vaddr) }; // use unsafe to workaround borrow checker
let vaddr = seg_addr + cur;
let paddr = unsafe { mmap_virt_to_phys(VirtAddr::from(mem as *const _ as *const u8)) };
let attr = {
let mut attr = MapAttr::USER_ACCESSIBLE;
if phdr.p_flags & PF_X != 0 {
attr |= MapAttr::EXECUTABLE;
}
if phdr.p_flags & PF_W != 0 {
attr |= MapAttr::WRITABLE;
}
if phdr.p_flags & PF_R != 0 {
attr |= MapAttr::READABLE;
}
attr
};
let alloc_page = || {
trace!("[initrd] allocating pagetable at slot #{}", nxt_slot);
alloc_objects::<TableObject>(cnode_obj, end_idx, PAGE_BITS, nxt_slot..nxt_slot + 1);
nxt_slot += 1;
let page = &cnode_obj[nxt_slot - 1];
let page = TableCap::try_from(page).unwrap();
let table: Table<Level0> = page.as_object(); // Any level is fine
unsafe { mmap_virt_to_phys(table.vaddr()) }
};
map_range(alloc_page, &mut root, vaddr, paddr, block_size, attr);
// update pointer
cur += block_size;
}
});
nxt_slot
}

View File

@ -23,6 +23,7 @@ pub trait EntryOps: Clone + Copy + Debug {
fn set_paddr(&mut self, addr: PhysAddr); fn set_paddr(&mut self, addr: PhysAddr);
fn set_attr(&mut self, attr: MapAttr); fn set_attr(&mut self, attr: MapAttr);
fn clear(&mut self);
fn is_valid(&self) -> bool; fn is_valid(&self) -> bool;
fn is_leaf(&self) -> bool; fn is_leaf(&self) -> bool;

View File

@ -5,6 +5,7 @@ pub trait TableOps<'a, T: TableLevel> {
/// # Safety /// # Safety
/// `location` must be a page-aligned virtual address and will not be dropped. /// `location` must be a page-aligned virtual address and will not be dropped.
unsafe fn new(location: VirtAddr) -> Self; unsafe fn new(location: VirtAddr) -> Self;
fn vaddr(&self) -> VirtAddr;
// following methods only works at current level // following methods only works at current level

View File

@ -423,3 +423,19 @@ impl Step for VirtAddr {
start.as_usize().checked_sub(count).map(VirtAddr) start.as_usize().checked_sub(count).map(VirtAddr)
} }
} }
impl PhysAddr {
/// # Safety
/// Only provided for the sake of completeness. Use `{mmap, kernel}_phys_to_virt` instead.
pub unsafe fn to_virt_addr(&self) -> VirtAddr {
VirtAddr(self.0)
}
}
impl VirtAddr {
/// # Safety
/// Only provided for the sake of completeness. Use `{mmap, kernel}_virt_to_phys` instead.
pub unsafe fn to_phys_addr(&self) -> PhysAddr {
PhysAddr(self.0)
}
}

View File

@ -4,3 +4,9 @@ macro_rules! MASK {
((1 << $bits) - 1) ((1 << $bits) - 1)
}; };
} }
pub const fn prev_power_of_two(n: u64) -> u64 {
// Taken from https://internals.rust-lang.org/t/add-prev-power-of-two/14281
let bit = 63 - (n | 1).leading_zeros();
(1 << bit) & n
}

17
tools/parse_backtrace.sh Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
if [[ $# -ne 1 ]]; then
echo "Usage: $0 <ELF_FILE>"
exit 1
fi
ELF_FILE=$1
while IFS= read -r line; do
if [[ $line =~ PC:\ VirtAddr\((0x[0-9a-fA-F]+)\) ]]; then
addr=${BASH_REMATCH[1]}
func=$(addr2line -Cfpe "$ELF_FILE" "$addr")
line+=" $func"
fi
echo "$line"
done

View File

@ -7,7 +7,7 @@ pub enum ObjectType {
Reply = 4, Reply = 4,
Notification = 5, Notification = 5,
Frame = 6, Frame = 6,
PageTable = 7, Table = 7,
Interrupt = 8, Interrupt = 8,
Untyped = 9, Untyped = 9,
} }
@ -36,7 +36,7 @@ impl ObjectType {
ObjectType::Reply => 0, // TODO: fill it! ObjectType::Reply => 0, // TODO: fill it!
ObjectType::Notification => 0, // TODO: fill it! ObjectType::Notification => 0, // TODO: fill it!
ObjectType::Frame => user_obj_bits, ObjectType::Frame => user_obj_bits,
ObjectType::PageTable => user_obj_bits, // arch dependent page table size ObjectType::Table => user_obj_bits, // arch dependent page table size
ObjectType::Interrupt => 0, // TODO: fill it! ObjectType::Interrupt => 0, // TODO: fill it!
ObjectType::Untyped => user_obj_bits, ObjectType::Untyped => user_obj_bits,
} }

10
uapi/src/cspace.rs Normal file
View File

@ -0,0 +1,10 @@
#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive, ToPrimitive)]
pub enum CNodeSlot {
NullCap = 0,
TcbCap,
CNodeCap,
VSpaceCap,
BootInfoFrameCap,
UntypedCap,
// IrqControlCap
}

View File

@ -5,6 +5,7 @@
extern crate num_derive; extern crate num_derive;
pub mod cap; pub mod cap;
pub mod cspace;
pub mod error; pub mod error;
pub mod fault; pub mod fault;
pub mod syscall; pub mod syscall;