doc: update docs
This commit is contained in:
@@ -76,6 +76,12 @@ endif
|
||||
clean:
|
||||
cargo clean
|
||||
|
||||
doc:
|
||||
cd kernel && cargo doc --all-features --target=../etc/$(ARCH)-$(MACH).json
|
||||
|
||||
doc-open:
|
||||
cd kernel && cargo doc --open --all-features --target=../etc/$(ARCH)-$(MACH).json
|
||||
|
||||
clippy:
|
||||
cd kernel && cargo clippy $(CARGO_BUILD_OPTS)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! aarch64 common boot logic
|
||||
|
||||
use crate::arch::{aarch64::asm::CPACR_EL1, machine};
|
||||
use crate::arch::{aarch64::reg::CPACR_EL1, machine};
|
||||
use crate::dev::{fdt::DeviceTree, irq::IntSource, Device};
|
||||
use crate::mem::{
|
||||
self, heap,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![allow(missing_docs)]
|
||||
//! Thread context
|
||||
|
||||
use crate::mem::{
|
||||
self,
|
||||
@@ -11,9 +11,12 @@ struct Stack {
|
||||
sp: usize,
|
||||
}
|
||||
|
||||
/// Structure representing thread context
|
||||
#[repr(C)]
|
||||
pub struct Context {
|
||||
pub k_sp: usize, // 0x00
|
||||
/// Thread's kernel stack pointer
|
||||
pub k_sp: usize, // 0x00
|
||||
/// Thread's translation table physical address with ASID
|
||||
pub ttbr0: usize, // 0x08
|
||||
|
||||
stack_base_phys: usize,
|
||||
@@ -21,6 +24,7 @@ pub struct Context {
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// Constructs a new kernel-space thread context
|
||||
pub fn kernel(entry: usize, arg: usize, ttbr0: usize, ustack: usize) -> Self {
|
||||
let mut stack = Stack::new(8);
|
||||
|
||||
@@ -51,11 +55,22 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs initial thread entry
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: does not check if any context has already been activated
|
||||
/// before, so must only be called once.
|
||||
pub unsafe extern "C" fn enter(&mut self) -> ! {
|
||||
__aa64_ctx_switch_to(self);
|
||||
panic!("This code should not run");
|
||||
}
|
||||
|
||||
/// Performs context switch from `self` to `to`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: does not check if `self` is actually an active context.
|
||||
pub unsafe extern "C" fn switch(&mut self, to: &mut Context) {
|
||||
__aa64_ctx_switch(to, self);
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
use cortex_a::registers::DAIF;
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
|
||||
pub mod asm;
|
||||
pub mod boot;
|
||||
pub mod context;
|
||||
pub mod exception;
|
||||
pub mod irq;
|
||||
pub mod reg;
|
||||
pub mod timer;
|
||||
|
||||
cfg_if! {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//! Assembly intrinsics for AArch64 platform
|
||||
#![allow(missing_docs)]
|
||||
//! CPACR_EL1 register
|
||||
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
@@ -8,15 +7,24 @@ use tock_registers::{
|
||||
|
||||
register_bitfields! {
|
||||
u64,
|
||||
#[allow(missing_docs)]
|
||||
/// EL1 Architectural Feature Access Control Register
|
||||
pub CPACR_EL1 [
|
||||
/// Enable EL0 and EL1 SIMD/FP accesses to EL1
|
||||
FPEN OFFSET(20) NUMBITS(2) [
|
||||
/// Trap both EL0 and EL1
|
||||
TrapAll = 0,
|
||||
/// Trap EL0
|
||||
TrapEl0 = 1,
|
||||
/// Trap EL1
|
||||
TrapEl1 = 2,
|
||||
/// Do not trap any SIMD/FP instructions
|
||||
TrapNone = 3
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
/// CPACR_EL1 register
|
||||
pub struct Reg;
|
||||
|
||||
impl Readable for Reg {
|
||||
@@ -45,4 +53,5 @@ impl Writeable for Reg {
|
||||
}
|
||||
}
|
||||
|
||||
/// CPACR_EL1 register
|
||||
pub const CPACR_EL1: Reg = Reg;
|
||||
@@ -0,0 +1,4 @@
|
||||
//! AArch64 architectural registers
|
||||
|
||||
pub mod cpacr_el1;
|
||||
pub use cpacr_el1::CPACR_EL1;
|
||||
+8
-1
@@ -1,8 +1,15 @@
|
||||
//! Debug output module.
|
||||
//!
|
||||
//! The module provides [debug!] and [debugln!] macros
|
||||
//! The module provides [print!] and [println!] macros
|
||||
//! which can be used in similar way to print! and
|
||||
//! println! from std.
|
||||
//!
|
||||
//! Level-specific debugging macros are provided as well:
|
||||
//!
|
||||
//! * [debugln!]
|
||||
//! * [infoln!]
|
||||
//! * [warnln!]
|
||||
//! * [errorln!]
|
||||
|
||||
use crate::dev::serial::SerialDevice;
|
||||
use core::fmt;
|
||||
|
||||
@@ -13,14 +13,14 @@ pub trait IntController: Device {
|
||||
/// Implementation-specific definition for "IRQ line"
|
||||
type IrqNumber;
|
||||
|
||||
/// Binds a handler [IntSource] to a specific [irq] line
|
||||
/// Binds a handler [IntSource] to a specific `irq` line
|
||||
fn register_handler(
|
||||
&self,
|
||||
irq: Self::IrqNumber,
|
||||
handler: &'static (dyn IntSource + Sync),
|
||||
) -> Result<(), Errno>;
|
||||
|
||||
/// Enables/unmasks [irq] line
|
||||
/// Enables/unmasks `irq` line
|
||||
fn enable_irq(&self, irq: Self::IrqNumber) -> Result<(), Errno>;
|
||||
|
||||
/// Handles all pending IRQs for this interrupt controller
|
||||
|
||||
@@ -87,7 +87,7 @@ pub trait PciHostDevice: Device {
|
||||
}
|
||||
|
||||
impl PciAddress {
|
||||
/// Constructs a [PCIAddress] instance from its components
|
||||
/// Constructs a [PciAddress] instance from its components
|
||||
#[inline(always)]
|
||||
pub const fn new(bus: u8, dev: u8, func: u8) -> Self {
|
||||
Self {
|
||||
@@ -95,25 +95,25 @@ impl PciAddress {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `bus` field of [PCIAddress]
|
||||
/// Returns `bus` field of [PciAddress]
|
||||
#[inline(always)]
|
||||
pub const fn bus(self) -> u8 {
|
||||
(self.value >> 8) as u8
|
||||
}
|
||||
|
||||
/// Returns `dev` field of [PCIAddress]
|
||||
/// Returns `dev` field of [PciAddress]
|
||||
#[inline(always)]
|
||||
pub const fn dev(self) -> u8 {
|
||||
((self.value >> 3) as u8) & 0x1F
|
||||
}
|
||||
|
||||
/// Returns `func` field of [PCIAddress]
|
||||
/// Returns `func` field of [PciAddress]
|
||||
#[inline(always)]
|
||||
pub const fn func(self) -> u8 {
|
||||
(self.value as u8) & 0x7
|
||||
}
|
||||
|
||||
/// Returns a new [PCIAddress], constructed from `self`, but with
|
||||
/// Returns a new [PciAddress], constructed from `self`, but with
|
||||
/// specified `func` number
|
||||
#[inline(always)]
|
||||
pub const fn with_func(self, func: u8) -> Self {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! Kernel heap allocation facilities
|
||||
|
||||
use crate::sync::IrqSafeNullLock;
|
||||
use crate::util::InitOnce;
|
||||
use core::alloc::{GlobalAlloc, Layout};
|
||||
@@ -49,6 +51,11 @@ static SYSTEM_ALLOC: SystemAlloc = SystemAlloc;
|
||||
|
||||
static HEAP: InitOnce<IrqSafeNullLock<Heap>> = InitOnce::new();
|
||||
|
||||
/// Initializes kernel heap with virtual `base` address and `size`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: accepts arbitrary `base` and `size` parameters.
|
||||
pub unsafe fn init(base: usize, size: usize) {
|
||||
let heap = Heap { base, size, ptr: 0 };
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//! Memory management and functions module
|
||||
#![allow(missing_docs)]
|
||||
|
||||
pub mod heap;
|
||||
pub mod phys;
|
||||
@@ -7,13 +6,20 @@ pub mod virt;
|
||||
|
||||
/// Virtual offset applied to kernel address space
|
||||
pub const KERNEL_OFFSET: usize = 0xFFFFFF8000000000;
|
||||
|
||||
/// Default page size used by the kernel
|
||||
pub const PAGE_SIZE: usize = 4096;
|
||||
|
||||
/// Returns input `addr` with [KERNEL_OFFSET] applied.
|
||||
///
|
||||
/// Will panic if `addr` is not mapped by kernel's
|
||||
/// direct translation tables.
|
||||
pub fn virtualize(addr: usize) -> usize {
|
||||
// TODO remove this function
|
||||
assert!(addr < (256 << 30));
|
||||
addr + KERNEL_OFFSET
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the physical address of kernel's end in memory.
|
||||
pub fn kernel_end_phys() -> usize {
|
||||
extern "C" {
|
||||
static __kernel_end: u8;
|
||||
@@ -21,9 +27,6 @@ pub fn kernel_end_phys() -> usize {
|
||||
unsafe { &__kernel_end as *const _ as usize - KERNEL_OFFSET }
|
||||
}
|
||||
|
||||
///
|
||||
pub const PAGE_SIZE: usize = 4096;
|
||||
|
||||
/// See memcpy(3p).
|
||||
///
|
||||
/// # Safety
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! Physical memory management facilities
|
||||
|
||||
use crate::mem::PAGE_SIZE;
|
||||
use core::mem::size_of;
|
||||
use error::Errno;
|
||||
@@ -12,33 +14,46 @@ type ManagerImpl = SimpleManager;
|
||||
|
||||
const MAX_PAGES: usize = 1024 * 1024;
|
||||
|
||||
/// These describe what a memory page is used for
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
pub enum PageUsage {
|
||||
/// The page cannot be allocated/used
|
||||
Reserved,
|
||||
/// The page can be allocated and is unused at the moment
|
||||
Available,
|
||||
/// Kernel data page
|
||||
Kernel,
|
||||
/// Kernel heap page
|
||||
KernelHeap,
|
||||
/// Translation tables
|
||||
Paging,
|
||||
/// Userspace page
|
||||
UserStack,
|
||||
}
|
||||
|
||||
/// Data structure representing a single physical memory page
|
||||
pub struct PageInfo {
|
||||
refcount: usize,
|
||||
usage: PageUsage,
|
||||
}
|
||||
|
||||
/// Page-aligned physical memory region
|
||||
#[derive(Clone)]
|
||||
pub struct MemoryRegion {
|
||||
/// Start address (page-aligned)
|
||||
pub start: usize,
|
||||
/// End address (page-aligned)
|
||||
pub end: usize,
|
||||
}
|
||||
|
||||
/// Wrapper for single-region physical memory initialization
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone)]
|
||||
pub struct SimpleMemoryIterator {
|
||||
inner: Option<MemoryRegion>,
|
||||
}
|
||||
impl SimpleMemoryIterator {
|
||||
/// Constructs a new instance of [Self]
|
||||
pub const fn new(reg: MemoryRegion) -> Self {
|
||||
Self { inner: Some(reg) }
|
||||
}
|
||||
@@ -50,6 +65,7 @@ impl Iterator for SimpleMemoryIterator {
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates a contiguous range of `count` physical memory pages.
|
||||
pub fn alloc_contiguous_pages(pu: PageUsage, count: usize) -> Result<usize, Errno> {
|
||||
MANAGER
|
||||
.lock()
|
||||
@@ -58,6 +74,7 @@ pub fn alloc_contiguous_pages(pu: PageUsage, count: usize) -> Result<usize, Errn
|
||||
.alloc_contiguous_pages(pu, count)
|
||||
}
|
||||
|
||||
/// Allocates a single physical memory page.
|
||||
pub fn alloc_page(pu: PageUsage) -> Result<usize, Errno> {
|
||||
MANAGER.lock().as_mut().unwrap().alloc_page(pu)
|
||||
}
|
||||
@@ -84,6 +101,8 @@ fn find_contiguous<T: Iterator<Item = MemoryRegion>>(iter: T, count: usize) -> O
|
||||
None
|
||||
}
|
||||
|
||||
/// Initializes physical memory manager using an iterator of available
|
||||
/// physical memory ranges
|
||||
pub unsafe fn init_from_iter<T: Iterator<Item = MemoryRegion> + Clone>(iter: T) {
|
||||
let mut mem_base = usize::MAX;
|
||||
for reg in iter.clone() {
|
||||
@@ -122,6 +141,9 @@ pub unsafe fn init_from_iter<T: Iterator<Item = MemoryRegion> + Clone>(iter: T)
|
||||
*MANAGER.lock() = Some(manager);
|
||||
}
|
||||
|
||||
/// Initializes physical memory manager using a single memory region.
|
||||
///
|
||||
/// See [init_from_iter].
|
||||
pub unsafe fn init_from_region(base: usize, size: usize) {
|
||||
let iter = SimpleMemoryIterator::new(MemoryRegion {
|
||||
start: base,
|
||||
|
||||
@@ -2,11 +2,15 @@ use crate::mem::{kernel_end_phys, PAGE_SIZE};
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ptr::null_mut;
|
||||
|
||||
/// Data structure representing a region of unusable memory
|
||||
pub struct ReservedRegion {
|
||||
/// Start address (page aligned)
|
||||
pub start: usize,
|
||||
/// End address (page aligned)
|
||||
pub end: usize,
|
||||
next: *mut ReservedRegion,
|
||||
}
|
||||
/// Struct for iterating over reserved memory regions
|
||||
pub struct ReservedRegionIterator {
|
||||
ptr: *mut ReservedRegion,
|
||||
}
|
||||
@@ -22,8 +26,9 @@ impl Iterator for ReservedRegionIterator {
|
||||
}
|
||||
}
|
||||
impl ReservedRegion {
|
||||
/// Constructs a new instance of [Self]
|
||||
pub const fn new(start: usize, end: usize) -> ReservedRegion {
|
||||
//assert!(start.is_paligned() && end.is_paligned());
|
||||
assert!(start & 0xFFF == 0 && end & 0xFFF == 0);
|
||||
ReservedRegion {
|
||||
start,
|
||||
end,
|
||||
@@ -34,6 +39,12 @@ impl ReservedRegion {
|
||||
static mut RESERVED_REGIONS_HEAD: *mut ReservedRegion = null_mut();
|
||||
static mut RESERVED_REGION_KERNEL: MaybeUninit<ReservedRegion> = MaybeUninit::uninit();
|
||||
static mut RESERVED_REGION_PAGES: MaybeUninit<ReservedRegion> = MaybeUninit::uninit();
|
||||
|
||||
/// Adds a `region` to reserved memory region list.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: `region` is passed as a raw pointer.
|
||||
pub unsafe fn reserve(usage: &str, region: *mut ReservedRegion) {
|
||||
infoln!(
|
||||
"Reserving {:?} region: {:#x}..{:#x}",
|
||||
@@ -44,6 +55,7 @@ pub unsafe fn reserve(usage: &str, region: *mut ReservedRegion) {
|
||||
(*region).next = RESERVED_REGIONS_HEAD;
|
||||
RESERVED_REGIONS_HEAD = region;
|
||||
}
|
||||
|
||||
pub(super) unsafe fn reserve_kernel() {
|
||||
RESERVED_REGION_KERNEL.write(ReservedRegion::new(0, kernel_end_phys()));
|
||||
reserve("kernel", RESERVED_REGION_KERNEL.as_mut_ptr());
|
||||
@@ -52,6 +64,9 @@ pub(super) unsafe fn reserve_pages(base: usize, count: usize) {
|
||||
RESERVED_REGION_PAGES.write(ReservedRegion::new(base, base + count * PAGE_SIZE));
|
||||
reserve("pages", RESERVED_REGION_PAGES.as_mut_ptr());
|
||||
}
|
||||
|
||||
/// Returns `true` if physical memory referred to by `page` cannot be
|
||||
/// used and/or allocated
|
||||
pub fn is_reserved(page: usize) -> bool {
|
||||
unsafe {
|
||||
let mut iter = RESERVED_REGIONS_HEAD;
|
||||
|
||||
@@ -1,17 +1,4 @@
|
||||
//
|
||||
//#[repr(C, align(0x1000))]
|
||||
//pub struct OldTable([u64; 512]);
|
||||
//
|
||||
//#[no_mangle]
|
||||
//static mut KERNEL_TTBR1: OldTable = OldTable([0; 512]);
|
||||
//// 1GiB
|
||||
//static mut KERNEL_L1: OldTable = OldTable([0; 512]);
|
||||
//// 2MiB
|
||||
//static mut KERNEL_L2: OldTable = OldTable([0; 512]);
|
||||
//static mut COUNT: usize = 0;
|
||||
//static mut BIG_COUNT: usize = 1;
|
||||
//static mut HUGE_COUNT: usize = 1;
|
||||
//
|
||||
//! Fixed-size table group for device MMIO mappings
|
||||
|
||||
use crate::mem::{
|
||||
self,
|
||||
@@ -22,6 +9,8 @@ use error::Errno;
|
||||
|
||||
const DEVICE_MAP_OFFSET: usize = mem::KERNEL_OFFSET + (256usize << 30);
|
||||
|
||||
/// Fixed-layout group of tables describing device MMIO and kernel identity
|
||||
/// mappings
|
||||
#[repr(C, align(0x1000))]
|
||||
pub struct FixedTableGroup {
|
||||
l0: Table,
|
||||
@@ -34,6 +23,8 @@ pub struct FixedTableGroup {
|
||||
}
|
||||
|
||||
impl FixedTableGroup {
|
||||
/// Constructs a new instance of [Self], initialized with non-present mapping
|
||||
/// entries
|
||||
pub const fn empty() -> Self {
|
||||
Self {
|
||||
l0: Table::empty(),
|
||||
@@ -46,6 +37,10 @@ impl FixedTableGroup {
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates a virtual memory range from this table group for requested
|
||||
/// `phys`..`phys + count * PAGE_SIZE` physical memory region and maps it.
|
||||
///
|
||||
/// TODO: only allows 4K, 2M and 1G mappings.
|
||||
pub fn map_region(&mut self, phys: usize, count: usize) -> Result<usize, Errno> {
|
||||
// TODO generalize region allocation
|
||||
let phys_page = phys & !0xFFF;
|
||||
@@ -101,6 +96,7 @@ impl FixedTableGroup {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets up initial mappings for 4K, 2M and 1G device memory page translation
|
||||
pub fn init_device_map(&mut self) {
|
||||
let l1_phys = (&self.l1 as *const _) as usize - mem::KERNEL_OFFSET;
|
||||
let l2_phys = (&self.l2 as *const _) as usize - mem::KERNEL_OFFSET;
|
||||
|
||||
+19
-11
@@ -1,4 +1,4 @@
|
||||
#![allow(missing_docs)]
|
||||
//! Virtual memory and translation table management
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::Deref;
|
||||
@@ -15,7 +15,10 @@ pub use fixed::FixedTableGroup;
|
||||
#[no_mangle]
|
||||
static mut KERNEL_TTBR1: FixedTableGroup = FixedTableGroup::empty();
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Structure representing a region of memory used for MMIO/device access
|
||||
// TODO: this shouldn't be trivially-cloneable and should instead incorporate
|
||||
// refcount and properly implement Drop trait
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub struct DeviceMemory {
|
||||
name: &'static str,
|
||||
@@ -23,16 +26,24 @@ pub struct DeviceMemory {
|
||||
count: usize,
|
||||
}
|
||||
|
||||
/// Structure implementing `Deref<T>` for convenient MMIO register access.
|
||||
///
|
||||
/// See [DeviceMemory].
|
||||
pub struct DeviceMemoryIo<T> {
|
||||
mmio: DeviceMemory,
|
||||
_0: PhantomData<T>,
|
||||
}
|
||||
impl DeviceMemory {
|
||||
/// Returns base address of this MMIO region
|
||||
#[inline(always)]
|
||||
pub const fn base(&self) -> usize {
|
||||
self.base
|
||||
}
|
||||
|
||||
/// Allocates a virtual memory region and maps it to contiguous region
|
||||
/// `phys`..`phys + count * PAGE_SIZE` for MMIO use.
|
||||
///
|
||||
/// See [FixedTableGroup::map_region]
|
||||
pub fn map(name: &'static str, phys: usize, count: usize) -> Result<Self, Errno> {
|
||||
let base = unsafe { KERNEL_TTBR1.map_region(phys, count) }?;
|
||||
debugln!(
|
||||
@@ -44,18 +55,10 @@ impl DeviceMemory {
|
||||
);
|
||||
Ok(Self { name, base, count })
|
||||
}
|
||||
|
||||
pub unsafe fn clone(&self) -> Self {
|
||||
// TODO maybe add refcount and remove "unsafe"?
|
||||
Self {
|
||||
name: self.name,
|
||||
base: self.base,
|
||||
count: self.count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DeviceMemoryIo<T> {
|
||||
/// Constructs a new [DeviceMemoryIo<T>] from existing `mmio` region
|
||||
pub const fn new(mmio: DeviceMemory) -> Self {
|
||||
Self {
|
||||
mmio,
|
||||
@@ -63,6 +66,9 @@ impl<T> DeviceMemoryIo<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates and maps device MMIO memory.
|
||||
///
|
||||
/// See [DeviceMemory::map]
|
||||
pub unsafe fn map(name: &'static str, phys: usize, count: usize) -> Result<Self, Errno> {
|
||||
DeviceMemory::map(name, phys, count).map(Self::new)
|
||||
}
|
||||
@@ -77,6 +83,8 @@ impl<T> Deref for DeviceMemoryIo<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets up device mapping tables and disable lower-half
|
||||
/// identity-mapped translation
|
||||
pub fn enable() -> Result<(), Errno> {
|
||||
unsafe {
|
||||
KERNEL_TTBR1.init_device_map();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! Translation table manipulation facilities
|
||||
|
||||
use crate::mem::{
|
||||
self,
|
||||
phys::{self, PageUsage},
|
||||
@@ -5,32 +7,49 @@ use crate::mem::{
|
||||
use core::ops::{Index, IndexMut};
|
||||
use error::Errno;
|
||||
|
||||
/// Transparent wrapper structure representing a single
|
||||
/// translation table entry
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
pub struct Entry(u64);
|
||||
|
||||
/// Structure describing a single level of translation mappings
|
||||
#[repr(C, align(0x1000))]
|
||||
pub struct Table {
|
||||
entries: [Entry; 512],
|
||||
}
|
||||
|
||||
/// Wrapper for top-most level of address translation tables
|
||||
#[repr(transparent)]
|
||||
pub struct Space(Table);
|
||||
|
||||
bitflags! {
|
||||
/// Attributes attached to each translation [Entry]
|
||||
pub struct MapAttributes: u64 {
|
||||
// TODO use 2 lower bits to determine mapping size?
|
||||
/// nG bit -- determines whether a TLB entry associated with this mapping
|
||||
/// applies only to current ASID or all ASIDs.
|
||||
const NOT_GLOBAL = 1 << 11;
|
||||
/// AF bit -- must be set by software, otherwise Access Error exception is
|
||||
/// generated when the page is accessed
|
||||
const ACCESS = 1 << 10;
|
||||
/// The memory region is outer-shareable
|
||||
const SH_OUTER = 2 << 8;
|
||||
/// This page is used for device-MMIO mapping and uses MAIR attribute #1
|
||||
const DEVICE = 1 << 2;
|
||||
|
||||
/// UXN bit -- if set, page may not be used for instruction fetching from EL0
|
||||
const UXN = 1 << 54;
|
||||
/// PXN bit -- if set, page may not be used for instruction fetching from EL1
|
||||
const PXN = 1 << 53;
|
||||
}
|
||||
}
|
||||
|
||||
impl Table {
|
||||
/// Returns next-level translation table reference for `index`, if one is present.
|
||||
/// If `index` represents a `Block`-type mapping, will return an error.
|
||||
/// If `index` does not map to any translation table, will try to allocate, init and
|
||||
/// map a new one, returning it after doing so.
|
||||
pub fn next_level_table_or_alloc(&mut self, index: usize) -> Result<&'static mut Table, Errno> {
|
||||
let entry = self[index];
|
||||
if entry.is_present() {
|
||||
@@ -48,6 +67,7 @@ impl Table {
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs and fills a [Table] with non-present mappings
|
||||
pub const fn empty() -> Table {
|
||||
Table {
|
||||
entries: [Entry::invalid(); 512],
|
||||
@@ -74,32 +94,45 @@ impl Entry {
|
||||
const TABLE: u64 = 1 << 1;
|
||||
const PHYS_MASK: u64 = 0x0000FFFFFFFFF000;
|
||||
|
||||
/// Constructs a single non-present mapping
|
||||
pub const fn invalid() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
|
||||
/// Constructs a `Block`-type memory mapping
|
||||
pub const fn block(phys: usize, attrs: MapAttributes) -> Self {
|
||||
Self((phys as u64 & Self::PHYS_MASK) | attrs.bits() | Self::PRESENT)
|
||||
}
|
||||
|
||||
/// Constructs a `Table` or `Page`-type mapping depending on translation level
|
||||
/// this entry is used at
|
||||
pub const fn table(phys: usize, attrs: MapAttributes) -> Self {
|
||||
Self((phys as u64 & Self::PHYS_MASK) | attrs.bits() | Self::PRESENT | Self::TABLE)
|
||||
}
|
||||
|
||||
/// Returns `true` if this entry is not invalid
|
||||
pub const fn is_present(self) -> bool {
|
||||
self.0 & Self::PRESENT != 0
|
||||
}
|
||||
|
||||
/// Returns `true` if this entry is a `Table` or `Page`-type mapping
|
||||
pub const fn is_table(self) -> bool {
|
||||
self.0 & Self::TABLE != 0
|
||||
}
|
||||
|
||||
/// Returns the target address of this translation entry.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Does not check if the entry is actually valid.
|
||||
pub const unsafe fn address_unchecked(self) -> usize {
|
||||
(self.0 & Self::PHYS_MASK) as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl Space {
|
||||
/// Creates a new virtual address space and fills it with [Entry::invalid()]
|
||||
/// mappings. Does physical memory page allocation.
|
||||
pub fn alloc_empty() -> Result<&'static mut Self, Errno> {
|
||||
let phys = phys::alloc_page(PageUsage::Paging)?;
|
||||
let res = unsafe { &mut *(mem::virtualize(phys) as *mut Self) };
|
||||
@@ -107,6 +140,9 @@ impl Space {
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Inserts a single `virt` -> `phys` translation entry to this address space.
|
||||
///
|
||||
/// TODO: only works with 4K-sized pages at this moment.
|
||||
pub fn map(&mut self, virt: usize, phys: usize, flags: MapAttributes) -> Result<(), Errno> {
|
||||
let l0i = virt >> 30;
|
||||
let l1i = (virt >> 21) & 0x1FF;
|
||||
|
||||
+31
-1
@@ -1,4 +1,4 @@
|
||||
#![allow(missing_docs)]
|
||||
//! Process and thread manipulation facilities
|
||||
|
||||
use crate::mem::{
|
||||
self,
|
||||
@@ -14,8 +14,10 @@ use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
pub use crate::arch::platform::context::{self, Context};
|
||||
|
||||
/// Wrapper type for a process struct reference
|
||||
pub type ProcessRef = Rc<UnsafeCell<Process>>;
|
||||
|
||||
/// Structure describing an operating system process
|
||||
#[allow(dead_code)]
|
||||
pub struct Process {
|
||||
ctx: Context,
|
||||
@@ -24,12 +26,15 @@ pub struct Process {
|
||||
}
|
||||
|
||||
struct SchedulerInner {
|
||||
// TODO the process list itself is not a scheduler-related thing so maybe
|
||||
// move it outside?
|
||||
processes: BTreeMap<u32, ProcessRef>,
|
||||
queue: VecDeque<u32>,
|
||||
idle: u32,
|
||||
current: Option<u32>,
|
||||
}
|
||||
|
||||
/// Process scheduler state and queues
|
||||
pub struct Scheduler {
|
||||
inner: InitOnce<IrqSafeNullLock<SchedulerInner>>,
|
||||
}
|
||||
@@ -96,18 +101,31 @@ impl SchedulerInner {
|
||||
}
|
||||
|
||||
impl Scheduler {
|
||||
/// Constructs a new kernel-space process with `entry` and `arg`.
|
||||
/// Returns resulting process ID
|
||||
// TODO see the first TODO here
|
||||
pub fn new_kernel(&self, entry: usize, arg: usize) -> u32 {
|
||||
self.inner.get().lock().new_kernel(entry, arg)
|
||||
}
|
||||
|
||||
/// Initializes inner data structure:
|
||||
///
|
||||
/// * idle thread
|
||||
/// * process list/queue data structs
|
||||
pub fn init(&self) {
|
||||
self.inner.init(IrqSafeNullLock::new(SchedulerInner::new()));
|
||||
}
|
||||
|
||||
/// Schedules a thread for execution
|
||||
pub fn enqueue(&self, pid: u32) {
|
||||
self.inner.get().lock().queue.push_back(pid);
|
||||
}
|
||||
|
||||
/// Performs initial process entry.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: may only be called once, repeated calls will cause UB.
|
||||
pub unsafe fn enter(&self) -> ! {
|
||||
let thread = {
|
||||
let mut inner = self.inner.get().lock();
|
||||
@@ -124,6 +142,8 @@ impl Scheduler {
|
||||
(*thread.get()).ctx.enter();
|
||||
}
|
||||
|
||||
/// Switches to the next task scheduled for execution. If there're
|
||||
/// none present in the queue, switches to the idle task.
|
||||
pub fn switch(&self) {
|
||||
let (from, to) = {
|
||||
let mut inner = self.inner.get().lock();
|
||||
@@ -175,6 +195,9 @@ extern "C" fn f0(a: usize) -> ! {
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs a task switch.
|
||||
///
|
||||
/// See [Scheduler::switch]
|
||||
pub fn switch() {
|
||||
SCHED.switch();
|
||||
}
|
||||
@@ -183,6 +206,13 @@ static SCHED: Scheduler = Scheduler {
|
||||
inner: InitOnce::new(),
|
||||
};
|
||||
|
||||
/// Sets up initial process and enters it.
|
||||
///
|
||||
/// See [Scheduler::enter]
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: May only be called once.
|
||||
pub unsafe fn enter() -> ! {
|
||||
SCHED.init();
|
||||
for i in 0..4 {
|
||||
|
||||
+3
-4
@@ -4,14 +4,13 @@ use crate::arch::platform::{irq_mask_save, irq_restore};
|
||||
use core::cell::UnsafeCell;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
/// Same as [NullLock], but ensures IRQs are disabled while
|
||||
/// the lock is held
|
||||
/// Lock structure ensuring IRQs are disabled when inner value is accessed
|
||||
pub struct IrqSafeNullLock<T: ?Sized> {
|
||||
value: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
/// Same as [NullLockGuard], but reverts IRQ mask back to normal
|
||||
/// when dropped
|
||||
/// Guard-structure wrapping a reference to value owned by [IrqSafeNullLock].
|
||||
/// Restores saved IRQ state when dropped.
|
||||
pub struct IrqSafeNullLockGuard<'a, T: ?Sized> {
|
||||
value: &'a mut T,
|
||||
irq_state: u64,
|
||||
|
||||
+10
-2
@@ -1,15 +1,18 @@
|
||||
#![allow(missing_docs)]
|
||||
//! Various utilities used by the kernel
|
||||
|
||||
use core::cell::UnsafeCell;
|
||||
use core::mem::MaybeUninit;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
/// Wrapper structure to guarantee single initialization
|
||||
/// of a value
|
||||
pub struct InitOnce<T> {
|
||||
state: AtomicBool,
|
||||
inner: UnsafeCell<MaybeUninit<T>>,
|
||||
}
|
||||
|
||||
impl<T> InitOnce<T> {
|
||||
/// Constructs a new instance of [InitOnce<T>]
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
state: AtomicBool::new(false),
|
||||
@@ -17,21 +20,26 @@ impl<T> InitOnce<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this [InitOnce<T>] can be used
|
||||
#[inline(always)]
|
||||
pub fn is_initialized(&self) -> bool {
|
||||
self.state.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
/// Returns the initialized value. Will panic if the value has not
|
||||
/// yet been initialized.
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub fn get(&self) -> &mut T {
|
||||
assert!(self.is_initialized(), "Access to uninitialized InitOnce<T>");
|
||||
unsafe { (*self.inner.get()).assume_init_mut() }
|
||||
}
|
||||
|
||||
/// Initializes the storage with `value`. Will panic if the storage has
|
||||
/// already been initialized.
|
||||
pub fn init(&self, value: T) {
|
||||
assert!(
|
||||
self.state
|
||||
.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
|
||||
.compare_exchange_weak(false, true, Ordering::Release, Ordering::Relaxed)
|
||||
.is_ok(),
|
||||
"Double-initialization of InitOnce<T>"
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user