From af5529a84fccf84fa92a58101e37ad9d37ec372b Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Mon, 21 Aug 2023 17:26:44 +0300 Subject: [PATCH] x86-64: sync x86-64 up with the new kernel structure --- Cargo.toml | 7 +- lib/device-api/src/input.rs | 11 + lib/device-api/src/interrupt.rs | 25 +- lib/device-api/src/lib.rs | 7 + lib/kernel-util/Cargo.toml | 8 + lib/kernel-util/src/lib.rs | 5 + lib/kernel-util/src/sync.rs | 0 {src => lib/kernel-util/src}/util.rs | 0 lib/memfs/src/dir.rs | 19 +- lib/memfs/src/file.rs | 26 +- lib/memfs/src/lib.rs | 4 +- lib/vfs/Cargo.toml | 1 + lib/vfs/src/char.rs | 17 +- lib/vfs/src/node.rs | 64 +-- src/arch/aarch64/boot/mod.rs | 1 - src/arch/aarch64/cpu.rs | 5 +- src/arch/aarch64/devtree.rs | 299 ------------- src/arch/aarch64/gic/mod.rs | 12 +- src/arch/aarch64/mod.rs | 46 +- src/arch/aarch64/smp.rs | 2 +- src/arch/aarch64/timer.rs | 8 +- src/arch/mod.rs | 49 ++- src/arch/x86_64/apic/ioapic.rs | 154 +++---- src/arch/x86_64/apic/local.rs | 77 ++-- src/arch/x86_64/apic/mod.rs | 45 +- src/arch/x86_64/boot/mod.rs | 90 ++-- src/arch/x86_64/cpu.rs | 6 +- src/arch/x86_64/mod.rs | 583 ++++++++++++++----------- src/arch/x86_64/peripherals/hpet.rs | 78 ++-- src/arch/x86_64/peripherals/ps2/mod.rs | 86 ++-- src/arch/x86_64/peripherals/serial.rs | 34 +- src/debug.rs | 3 +- src/device/bus/mod.rs | 1 + src/device/bus/simple_bus.rs | 6 +- src/device/devtree.rs | 454 +++++++++++++++++++ src/device/display/console.rs | 40 +- src/device/display/fb_console.rs | 13 +- src/device/display/linear_fb.rs | 5 +- src/device/display/mod.rs | 2 + src/device/mod.rs | 168 +------ src/device/power/arm_psci.rs | 6 +- src/device/power/mod.rs | 10 +- src/device/power/sunxi_rwdog.rs | 8 +- src/device/serial/mod.rs | 10 +- src/device/serial/pl011.rs | 18 +- src/device/serial/sunxi_uart.rs | 18 +- src/device/timer.rs | 13 - src/device/tty.rs | 25 +- src/fs/devfs.rs | 12 +- src/fs/mod.rs | 12 +- src/init.rs | 4 +- src/main.rs | 9 +- src/mem/mod.rs | 58 +-- src/mem/phys/mod.rs | 2 +- src/mem/phys/reserved.rs | 2 +- src/task/process.rs | 2 +- src/task/sched.rs | 2 +- 57 files changed, 1357 insertions(+), 1315 deletions(-) create mode 100644 lib/device-api/src/input.rs create mode 100644 lib/kernel-util/Cargo.toml create mode 100644 lib/kernel-util/src/lib.rs create mode 100644 lib/kernel-util/src/sync.rs rename {src => lib/kernel-util/src}/util.rs (100%) create mode 100644 src/device/devtree.rs delete mode 100644 src/device/timer.rs diff --git a/Cargo.toml b/Cargo.toml index 7d68103e..004e61c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" } vfs = { path = "lib/vfs" } memfs = { path = "lib/memfs" } device-api = { path = "lib/device-api", features = ["derive"] } +kernel-util = { path = "lib/kernel-util" } atomic_enum = "0.2.0" bitflags = "2.3.3" @@ -21,12 +22,10 @@ tock-registers = "0.8.1" cfg-if = "1.0.0" git-version = "0.3.5" -aarch64-cpu = "9.3.1" - bitmap-font = { version = "0.3.0", optional = true } embedded-graphics = { version = "0.8.0", optional = true } -fdt-rs = { version = "0.4.3", default-features = false } +log = "0.4.20" [dependencies.elf] version = "0.7.2" @@ -35,6 +34,8 @@ default-features = false features = ["no_std_stream"] [target.'cfg(target_arch = "aarch64")'.dependencies] +aarch64-cpu = "9.3.1" +fdt-rs = { version = "0.4.3", default-features = false } [target.'cfg(target_arch = "x86_64")'.dependencies] yboot-proto = { git = "https://git.alnyan.me/yggdrasil/yboot-proto.git" } diff --git a/lib/device-api/src/input.rs b/lib/device-api/src/input.rs new file mode 100644 index 00000000..2df3fbc3 --- /dev/null +++ b/lib/device-api/src/input.rs @@ -0,0 +1,11 @@ +use yggdrasil_abi::error::Error; + +use crate::Device; + +pub trait KeyboardConsumer { + fn handle_character(&self, data: u8) -> Result<(), Error>; +} + +pub trait KeyboardProducer: Device { + fn connect(&self, to: &'static dyn KeyboardConsumer); +} diff --git a/lib/device-api/src/interrupt.rs b/lib/device-api/src/interrupt.rs index decc14c8..618f34dc 100644 --- a/lib/device-api/src/interrupt.rs +++ b/lib/device-api/src/interrupt.rs @@ -27,11 +27,11 @@ pub enum IpiDeliveryTarget { OtherCpus, } -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub enum IrqNumber { - Private(u32), - Shared(u32), -} +// #[derive(Clone, Copy, PartialEq, Eq, Debug)] +// pub enum IrqNumber { +// Private(u32), +// Shared(u32), +// } #[derive(Default, Clone, Copy, Debug)] pub struct IrqOptions { @@ -44,17 +44,19 @@ pub trait InterruptTable { } pub trait ExternalInterruptController { + type IrqNumber; + /// Performs IRQ delivery method configuration and registers a handler to execute when it is /// fired fn register_irq( &self, - irq: IrqNumber, + irq: Self::IrqNumber, options: IrqOptions, handler: &'static dyn InterruptHandler, ) -> Result<(), Error>; /// Enables the specified IRQ (unmasks it) - fn enable_irq(&self, irq: IrqNumber) -> Result<(), Error>; + fn enable_irq(&self, irq: Self::IrqNumber) -> Result<(), Error>; /// Handles a single pending interrupt on this controller. /// The function is intended for interrupt controllers which have internal registers to track @@ -107,6 +109,15 @@ impl FixedInterruptTable { self.entries[index] = Some(handler); Ok(()) } + + pub fn insert_least_loaded( + &mut self, + handler: &'static dyn InterruptHandler, + ) -> Result { + let index = self.entries.iter().position(|p| p.is_none()).unwrap(); + self.entries[index].replace(handler); + Ok(index) + } } impl InterruptTable for FixedInterruptTable { diff --git a/lib/device-api/src/lib.rs b/lib/device-api/src/lib.rs index 32427ea0..927b1f45 100644 --- a/lib/device-api/src/lib.rs +++ b/lib/device-api/src/lib.rs @@ -4,6 +4,7 @@ extern crate alloc; pub mod bus; pub mod device; +pub mod input; pub mod interrupt; pub mod manager; pub mod serial; @@ -24,5 +25,11 @@ pub trait CpuBringupDevice: Device { } pub trait ResetDevice: Device { + /// Performs a system reset. + /// + /// # Safety + /// + /// The kernel must ensure it is actually safe to perform a reset, no critical operations + /// are aborted and no data is lost. unsafe fn reset(&self) -> !; } diff --git a/lib/kernel-util/Cargo.toml b/lib/kernel-util/Cargo.toml new file mode 100644 index 00000000..496f8d0d --- /dev/null +++ b/lib/kernel-util/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "kernel-util" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/lib/kernel-util/src/lib.rs b/lib/kernel-util/src/lib.rs new file mode 100644 index 00000000..06f7b372 --- /dev/null +++ b/lib/kernel-util/src/lib.rs @@ -0,0 +1,5 @@ +#![no_std] +#![feature(maybe_uninit_slice)] + +pub mod sync; +pub mod util; diff --git a/lib/kernel-util/src/sync.rs b/lib/kernel-util/src/sync.rs new file mode 100644 index 00000000..e69de29b diff --git a/src/util.rs b/lib/kernel-util/src/util.rs similarity index 100% rename from src/util.rs rename to lib/kernel-util/src/util.rs diff --git a/lib/memfs/src/dir.rs b/lib/memfs/src/dir.rs index bb7571ba..c9280b88 100644 --- a/lib/memfs/src/dir.rs +++ b/lib/memfs/src/dir.rs @@ -1,4 +1,4 @@ -use core::marker::PhantomData; +use core::{cell::RefCell, marker::PhantomData}; use alloc::boxed::Box; @@ -15,12 +15,12 @@ pub(crate) struct DirectoryNode { } impl VnodeImpl for DirectoryNode { - fn create(&mut self, at: &VnodeRef, name: &str, kind: VnodeKind) -> Result { + fn create(&self, at: &VnodeRef, name: &str, kind: VnodeKind) -> Result { let child = Vnode::new(name, kind); match kind { VnodeKind::Directory => child.set_data(Box::new(Self { _pd: PhantomData })), VnodeKind::Regular => child.set_data(Box::new(FileNode { - data: BVec::::new(), + data: RefCell::new(BVec::::new()), })), _ => todo!(), } @@ -28,24 +28,19 @@ impl VnodeImpl for DirectoryNode { Ok(child) } - fn open( - &mut self, - _node: &VnodeRef, - _opts: OpenOptions, - _mode: FileMode, - ) -> Result { + fn open(&self, _node: &VnodeRef, _opts: OpenOptions, _mode: FileMode) -> Result { Ok(DIR_POSITION_FROM_CACHE) } - fn remove(&mut self, _at: &VnodeRef, _name: &str) -> Result<(), Error> { + fn remove(&self, _at: &VnodeRef, _name: &str) -> Result<(), Error> { Ok(()) } - fn size(&mut self, node: &VnodeRef) -> Result { + fn size(&self, node: &VnodeRef) -> Result { Ok(node.children().len() as u64) } - fn metadata(&mut self, _node: &VnodeRef) -> Result { + fn metadata(&self, _node: &VnodeRef) -> Result { Ok(FileAttr { size: 0, mode: FileMode::default_dir(), diff --git a/lib/memfs/src/file.rs b/lib/memfs/src/file.rs index 2ac22277..bc02276b 100644 --- a/lib/memfs/src/file.rs +++ b/lib/memfs/src/file.rs @@ -1,3 +1,5 @@ +use core::cell::RefCell; + use vfs::{VnodeImpl, VnodeRef}; use yggdrasil_abi::{ error::Error, @@ -7,37 +9,37 @@ use yggdrasil_abi::{ use crate::{block::BlockAllocator, bvec::BVec}; pub(crate) struct FileNode { - pub(crate) data: BVec<'static, A>, + pub(crate) data: RefCell>, } impl VnodeImpl for FileNode { - fn open(&mut self, _node: &VnodeRef, opts: OpenOptions, _mode: FileMode) -> Result { + fn open(&self, _node: &VnodeRef, opts: OpenOptions, _mode: FileMode) -> Result { if opts.contains(OpenOptions::APPEND) { - Ok(self.data.size() as u64) + Ok(self.data.borrow().size() as u64) } else { Ok(0) } } - fn close(&mut self, _node: &VnodeRef) -> Result<(), Error> { + fn close(&self, _node: &VnodeRef) -> Result<(), Error> { Ok(()) } - fn read(&mut self, _node: &VnodeRef, pos: u64, data: &mut [u8]) -> Result { - self.data.read(pos, data) + fn read(&self, _node: &VnodeRef, pos: u64, data: &mut [u8]) -> Result { + self.data.borrow().read(pos, data) } - fn write(&mut self, _node: &VnodeRef, pos: u64, data: &[u8]) -> Result { - self.data.write(pos, data) + fn write(&self, _node: &VnodeRef, pos: u64, data: &[u8]) -> Result { + self.data.borrow_mut().write(pos, data) } - fn size(&mut self, _node: &VnodeRef) -> Result { - Ok(self.data.size() as u64) + fn size(&self, _node: &VnodeRef) -> Result { + Ok(self.data.borrow().size() as u64) } - fn metadata(&mut self, _node: &VnodeRef) -> Result { + fn metadata(&self, _node: &VnodeRef) -> Result { Ok(FileAttr { - size: self.data.size() as u64, + size: self.data.borrow().size() as u64, mode: FileMode::default_file(), ty: FileType::File, }) diff --git a/lib/memfs/src/lib.rs b/lib/memfs/src/lib.rs index 853193c4..94d2b5d9 100644 --- a/lib/memfs/src/lib.rs +++ b/lib/memfs/src/lib.rs @@ -162,7 +162,9 @@ impl MemoryFilesystem { let bvec = BVec::::try_from(data)?; assert_eq!(bvec.size(), data.len()); - node.set_data(Box::new(FileNode { data: bvec })); + node.set_data(Box::new(FileNode { + data: RefCell::new(bvec), + })); } } diff --git a/lib/vfs/Cargo.toml b/lib/vfs/Cargo.toml index 42c6b09a..e0b452a7 100644 --- a/lib/vfs/Cargo.toml +++ b/lib/vfs/Cargo.toml @@ -7,4 +7,5 @@ edition = "2021" [dependencies] yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" } +kernel-util = { path = "../kernel-util" } bitflags = "2.3.3" diff --git a/lib/vfs/src/char.rs b/lib/vfs/src/char.rs index 70774490..bb44a524 100644 --- a/lib/vfs/src/char.rs +++ b/lib/vfs/src/char.rs @@ -23,28 +23,23 @@ impl CharDeviceWrapper { } impl VnodeImpl for CharDeviceWrapper { - fn open( - &mut self, - _node: &VnodeRef, - _opts: OpenOptions, - _mode: FileMode, - ) -> Result { + fn open(&self, _node: &VnodeRef, _opts: OpenOptions, _mode: FileMode) -> Result { Ok(0) } - fn close(&mut self, _node: &VnodeRef) -> Result<(), Error> { + fn close(&self, _node: &VnodeRef) -> Result<(), Error> { Ok(()) } - fn read(&mut self, _node: &VnodeRef, _pos: u64, data: &mut [u8]) -> Result { + fn read(&self, _node: &VnodeRef, _pos: u64, data: &mut [u8]) -> Result { self.device.read(true, data) } - fn write(&mut self, _node: &VnodeRef, _pos: u64, data: &[u8]) -> Result { + fn write(&self, _node: &VnodeRef, _pos: u64, data: &[u8]) -> Result { self.device.write(true, data) } - fn metadata(&mut self, _node: &VnodeRef) -> Result { + fn metadata(&self, _node: &VnodeRef) -> Result { Ok(FileAttr { size: 0, ty: FileType::Char, @@ -52,7 +47,7 @@ impl VnodeImpl for CharDeviceWrapper { }) } - fn device_request(&mut self, _node: &VnodeRef, req: &mut DeviceRequest) -> Result<(), Error> { + fn device_request(&self, _node: &VnodeRef, req: &mut DeviceRequest) -> Result<(), Error> { self.device.device_request(req) } } diff --git a/lib/vfs/src/node.rs b/lib/vfs/src/node.rs index 53921e5f..d32735c4 100644 --- a/lib/vfs/src/node.rs +++ b/lib/vfs/src/node.rs @@ -9,6 +9,7 @@ use alloc::{ string::String, vec::Vec, }; +use kernel_util::util::OneTimeInit; use yggdrasil_abi::{ error::Error, io::{DeviceRequest, FileAttr, FileMode, FileType, OpenOptions}, @@ -44,46 +45,46 @@ pub struct Vnode { name: String, tree: RefCell, kind: VnodeKind, - data: RefCell>>, + data: OneTimeInit>, fs: RefCell>>, target: RefCell>, } #[allow(unused_variables)] pub trait VnodeImpl { - fn create(&mut self, at: &VnodeRef, name: &str, kind: VnodeKind) -> Result { + fn create(&self, at: &VnodeRef, name: &str, kind: VnodeKind) -> Result { Err(Error::NotImplemented) } - fn remove(&mut self, at: &VnodeRef, name: &str) -> Result<(), Error> { + fn remove(&self, at: &VnodeRef, name: &str) -> Result<(), Error> { Err(Error::NotImplemented) } - fn open(&mut self, node: &VnodeRef, opts: OpenOptions, mode: FileMode) -> Result { + fn open(&self, node: &VnodeRef, opts: OpenOptions, mode: FileMode) -> Result { Err(Error::NotImplemented) } - fn close(&mut self, node: &VnodeRef) -> Result<(), Error> { + fn close(&self, node: &VnodeRef) -> Result<(), Error> { Err(Error::NotImplemented) } - fn read(&mut self, node: &VnodeRef, pos: u64, data: &mut [u8]) -> Result { + fn read(&self, node: &VnodeRef, pos: u64, data: &mut [u8]) -> Result { Err(Error::NotImplemented) } - fn write(&mut self, node: &VnodeRef, pos: u64, data: &[u8]) -> Result { + fn write(&self, node: &VnodeRef, pos: u64, data: &[u8]) -> Result { Err(Error::NotImplemented) } - fn size(&mut self, node: &VnodeRef) -> Result { + fn size(&self, node: &VnodeRef) -> Result { Err(Error::NotImplemented) } - fn metadata(&mut self, node: &VnodeRef) -> Result { + fn metadata(&self, node: &VnodeRef) -> Result { Err(Error::NotImplemented) } - fn device_request(&mut self, node: &VnodeRef, req: &mut DeviceRequest) -> Result<(), Error> { + fn device_request(&self, node: &VnodeRef, req: &mut DeviceRequest) -> Result<(), Error> { Err(Error::NotImplemented) } } @@ -97,7 +98,7 @@ impl Vnode { children: Vec::new(), }), kind, - data: RefCell::new(None), + data: OneTimeInit::new(), fs: RefCell::new(None), target: RefCell::new(None), }) @@ -119,14 +120,18 @@ impl Vnode { } #[inline] - pub fn data(&self) -> RefMut>> { - match self.data.try_borrow_mut() { - Ok(r) => r, - Err(e) => { - panic!("{:?} on {:?}", e, self.name()) - } - } + pub fn data(&self) -> Option<&dyn VnodeImpl> { + self.data.try_get().map(Box::as_ref) } + // #[inline] + // pub fn data(&self) -> RefMut>> { + // match self.data.try_borrow_mut() { + // Ok(r) => r, + // Err(e) => { + // panic!("{:?} on {:?}", e, self.name()) + // } + // } + // } #[inline] pub fn fs(&self) -> Option> { @@ -151,7 +156,8 @@ impl Vnode { } pub fn set_data(&self, data: Box) { - self.data.borrow_mut().replace(data); + self.data.init(data); + // self.data.borrow_mut().replace(data); } pub fn set_fs(&self, data: Rc) { @@ -273,7 +279,7 @@ impl Vnode { return Err(Error::IsADirectory); } - if let Some(ref mut data) = *self.data() { + if let Some(data) = self.data() { let pos = data.open(self, flags, mode)?; Ok(File::normal(self.clone(), pos, open_flags)) } else { @@ -286,7 +292,7 @@ impl Vnode { return Err(Error::IsADirectory); } - if let Some(ref mut data) = *self.data() { + if let Some(data) = self.data() { let pos = data.open(self, OpenOptions::READ, FileMode::empty())?; Ok(File::directory(self.clone(), pos)) } else { @@ -296,7 +302,7 @@ impl Vnode { } pub fn close(self: &VnodeRef) -> Result<(), Error> { - if let Some(ref mut data) = *self.data() { + if let Some(data) = self.data() { data.close(self) } else { Ok(()) @@ -317,7 +323,7 @@ impl Vnode { e => return e, }; - if let Some(ref mut data) = *self.data() { + if let Some(data) = self.data() { let vnode = data.create(self, name, kind)?; if let Some(fs) = self.fs() { vnode.set_fs(fs); @@ -350,7 +356,7 @@ impl Vnode { } } - if let Some(ref mut data) = *self.data() { + if let Some(data) = self.data() { data.remove(self, name)?; } @@ -365,7 +371,7 @@ impl Vnode { todo!(); } - if let Some(ref mut data) = *self.data() { + if let Some(data) = self.data() { data.write(self, pos, buf) } else { todo!() @@ -377,7 +383,7 @@ impl Vnode { todo!(); } - if let Some(ref mut data) = *self.data() { + if let Some(data) = self.data() { data.read(self, pos, buf) } else { todo!() @@ -385,7 +391,7 @@ impl Vnode { } pub fn size(self: &VnodeRef) -> Result { - if let Some(ref mut data) = *self.data() { + if let Some(data) = self.data() { data.size(self) } else { todo!(); @@ -435,7 +441,7 @@ impl Vnode { } pub fn metadata(self: &VnodeRef) -> Result { - if let Some(ref mut data) = *self.data() { + if let Some(data) = self.data() { data.metadata(self) } else { todo!() @@ -443,7 +449,7 @@ impl Vnode { } pub fn device_request(self: &VnodeRef, req: &mut DeviceRequest) -> Result<(), Error> { - if let Some(ref mut data) = *self.data() { + if let Some(data) = self.data() { data.device_request(self, req) } else { todo!() diff --git a/src/arch/aarch64/boot/mod.rs b/src/arch/aarch64/boot/mod.rs index ac1ec5e3..c209e74b 100644 --- a/src/arch/aarch64/boot/mod.rs +++ b/src/arch/aarch64/boot/mod.rs @@ -100,7 +100,6 @@ unsafe extern "C" fn __aarch64_bsp_upper_entry(dtb_phys: usize) -> ! { Cpu::init_local(); kernel_main() - // kernel_main(dtb_phys) } extern "C" fn __aarch64_ap_upper_entry(_x0: usize) -> ! { diff --git a/src/arch/aarch64/cpu.rs b/src/arch/aarch64/cpu.rs index 3a3f8cec..8ab4463a 100644 --- a/src/arch/aarch64/cpu.rs +++ b/src/arch/aarch64/cpu.rs @@ -3,11 +3,10 @@ use core::sync::atomic::Ordering; use aarch64_cpu::registers::{MPIDR_EL1, TPIDR_EL1}; use alloc::{boxed::Box, vec::Vec}; +use kernel_util::util::OneTimeInit; use tock_registers::interfaces::{Readable, Writeable}; -use crate::{ - arch::CpuMessage, panic, sync::IrqSafeSpinlock, task::sched::CpuQueue, util::OneTimeInit, -}; +use crate::{arch::CpuMessage, panic, sync::IrqSafeSpinlock, task::sched::CpuQueue}; use super::smp::CPU_COUNT; diff --git a/src/arch/aarch64/devtree.rs b/src/arch/aarch64/devtree.rs index 19cf91b4..e69de29b 100644 --- a/src/arch/aarch64/devtree.rs +++ b/src/arch/aarch64/devtree.rs @@ -1,299 +0,0 @@ -//! ARM device tree utlities -use fdt_rs::{ - base::DevTree, - index::{iters::DevTreeIndexNodeSiblingIter, DevTreeIndex, DevTreeIndexNode, DevTreeIndexProp}, - prelude::PropReader, -}; - -use crate::{debug::LogLevel, mem::phys::PhysicalMemoryRegion}; - -const INDEX_BUFFER_SIZE: usize = 65536; - -#[repr(C, align(0x10))] -struct FdtIndexBuffer([u8; INDEX_BUFFER_SIZE]); - -static mut FDT_INDEX_BUFFER: FdtIndexBuffer = FdtIndexBuffer::zeroed(); - -impl FdtIndexBuffer { - const fn zeroed() -> Self { - Self([0; INDEX_BUFFER_SIZE]) - } -} - -/// Device tree node -pub type TNode<'a> = DevTreeIndexNode<'a, 'a, 'a>; -/// Device tree property -pub type TProp<'a> = DevTreeIndexProp<'a, 'a, 'a>; - -/// Helper trait to provide extra functionality for [DevTreeIndexProp] -pub trait DevTreeIndexPropExt { - /// Reads a cell value from single-type cell array at given cell index - fn cell1_array_item(&self, index: usize, cells: usize) -> Option; - /// Reads a cell pair from cell pair array at given pair index - fn cell2_array_item(&self, index: usize, cells0: usize, cells1: usize) -> Option<(u64, u64)>; - - /// Reads a cell value from the property at given offset - fn read_cell(&self, u32_offset: usize, cell_size: usize) -> Option; -} - -/// Helper trait to provide extra functionality for [DevTreeIndexNode] -pub trait DevTreeIndexNodeExt { - /// Returns the root node's `#address-cells` property, or the default value defined by the - /// specification if it's absent - fn address_cells(&self) -> usize { - self.get_address_cells().unwrap_or(2) - } - /// Returns the root node's `#size-cells` property, or the default value defined by the - /// specification if it's absent - fn size_cells(&self) -> usize { - self.get_size_cells().unwrap_or(1) - } - - /// Returns the #address-cells property of the node, if there is one - fn get_address_cells(&self) -> Option; - /// Returns the #size-cells property of the node, if there is one - fn get_size_cells(&self) -> Option; - - /// Prints the node contents to debug output - fn dump(&self, level: LogLevel, depth: usize); -} - -/// Extension trait for [DevTreeIndexNode] to obtain typed property values -pub trait DevTreeIndexNodePropGet { - /// Returns a property value of given type, if it exists - fn prop(&self, name: &str) -> Option; -} - -impl<'a, 'i, 'dt> DevTreeIndexNodePropGet for DevTreeIndexNode<'a, 'i, 'dt> { - fn prop(&self, name: &str) -> Option { - self.props().find_map(|prop| { - if prop.name().ok()? == name { - prop.u32(0).ok() - } else { - None - } - }) - } -} - -impl<'a, 'i, 'dt> DevTreeIndexNodePropGet<&'a str> for DevTreeIndexNode<'a, 'i, 'dt> { - fn prop(&self, name: &str) -> Option<&'a str> { - self.props().find_map(|prop| { - if prop.name().ok()? == name { - prop.str().ok() - } else { - None - } - }) - } -} - -/// Iterator for physical memory regions present in the device tree -#[derive(Clone)] -pub struct FdtMemoryRegionIter<'a> { - inner: DevTreeIndexNodeSiblingIter<'a, 'a, 'a>, - address_cells: usize, - size_cells: usize, -} - -/// Device tree wrapper struct -pub struct DeviceTree<'a> { - tree: DevTree<'a>, - index: DevTreeIndex<'a, 'a>, -} - -impl<'a> DeviceTree<'a> { - /// Constructs a device tree wrapper from the DTB virtual address. - /// - /// # Safety - /// - /// The caller must ensure the validity of the address. - pub unsafe fn from_addr(virt: usize) -> Self { - let tree = DevTree::from_raw_pointer(virt as _).unwrap(); - let index = DevTreeIndex::new(tree, &mut FDT_INDEX_BUFFER.0).unwrap(); - Self { tree, index } - } - - /// Looks up a node for a given path - pub fn node_by_path(&self, path: &str) -> Option { - find_node(self.index.root(), path.trim_start_matches('/')) - } - - /// Prints the device tree to log output - pub fn dump(&self, level: LogLevel) { - self.index.root().dump(level, 0) - } - - /// Returns the total size of the device tree in memory - pub fn size(&self) -> usize { - self.tree.totalsize() - } - - /// Returns the root node's `#address-cells` property, or the default value defined by the - /// specification if it's absent - pub fn address_cells(&self) -> usize { - self.index.root().address_cells() - } - - /// Returns the root node's `#size-cells` property, or the default value defined by the - /// specification if it's absent - pub fn size_cells(&self) -> usize { - self.index.root().size_cells() - } - - /// Returns the root node of the device tree - pub fn root(&self) -> DevTreeIndexNode { - self.index.root() - } -} - -impl<'a, 'i, 'dt> DevTreeIndexNodeExt for DevTreeIndexNode<'a, 'i, 'dt> { - fn get_address_cells(&self) -> Option { - self.props() - .find(|p| p.name().unwrap_or("") == "#address-cells") - .map(|p| p.u32(0).unwrap() as usize) - } - - fn get_size_cells(&self) -> Option { - self.props() - .find(|p| p.name().unwrap_or("") == "#size-cells") - .map(|p| p.u32(0).unwrap() as usize) - } - - fn dump(&self, level: LogLevel, depth: usize) { - fn indent(level: LogLevel, depth: usize) { - for _ in 0..depth { - log_print_raw!(level, " "); - } - } - - let node_name = self.name().unwrap(); - - // Don't dump these - if node_name.starts_with("virtio_mmio@") { - return; - } - - indent(level, depth); - log_print_raw!(level, "{:?} {{\n", node_name); - for prop in self.props() { - indent(level, depth + 1); - let name = prop.name().unwrap_or(""); - log_print_raw!(level, "{name:?} = "); - - match name { - "compatible" | "stdout-path" => { - log_print_raw!(level, "{:?}", prop.str().unwrap_or("")) - } - _ => log_print_raw!(level, "{:x?}", prop.raw()), - } - - log_print_raw!(level, "\n"); - } - - for child in self.children() { - child.dump(level, depth + 1); - } - - indent(level, depth); - log_print_raw!(level, "}}\n"); - } -} - -impl<'a, 'i, 'dt> DevTreeIndexPropExt for DevTreeIndexProp<'a, 'i, 'dt> { - fn read_cell(&self, u32_offset: usize, cell_size: usize) -> Option { - match cell_size { - 1 => self.u32(u32_offset).map(|x| x as u64).ok(), - 2 => { - let high = self.u32(u32_offset).ok()? as u64; - let low = self.u32(u32_offset + 1).ok()? as u64; - - Some((high << 32) | low) - } - _ => unimplemented!(), - } - } - - fn cell1_array_item(&self, index: usize, cells: usize) -> Option { - self.read_cell(index * cells, cells) - } - - fn cell2_array_item(&self, index: usize, cells0: usize, cells1: usize) -> Option<(u64, u64)> { - let u32_index = index * (cells0 + cells1); - let cell0 = self.read_cell(u32_index, cells0)?; - let cell1 = self.read_cell(u32_index + cells0, cells1)?; - Some((cell0, cell1)) - } -} - -impl<'a> FdtMemoryRegionIter<'a> { - /// Constructs a memory region iterator for given device tree - pub fn new(dt: &'a DeviceTree) -> Self { - let inner = dt.index.root().children(); - let address_cells = dt.address_cells(); - let size_cells = dt.size_cells(); - Self { - inner, - address_cells, - size_cells, - } - } -} - -impl Iterator for FdtMemoryRegionIter<'_> { - type Item = PhysicalMemoryRegion; - - fn next(&mut self) -> Option { - loop { - let Some(item) = self.inner.next() else { - break None; - }; - - let name = item.name().unwrap_or(""); - - if name.starts_with("memory@") || name == "memory" { - let reg = item - .props() - .find(|p| p.name().unwrap_or("") == "reg") - .unwrap(); - - let (base, size) = reg - .cell2_array_item(0, self.address_cells, self.size_cells) - .unwrap(); - - break Some(PhysicalMemoryRegion { - base: base as usize, - size: size as usize, - }); - } - } - } -} - -/// Looks up a property with given name in the node -pub fn find_prop<'a>(node: &TNode<'a>, name: &str) -> Option> { - node.props().find(|p| p.name().unwrap_or("") == name) -} - -fn path_component_left(path: &str) -> (&str, &str) { - if let Some((left, right)) = path.split_once('/') { - (left, right.trim_start_matches('/')) - } else { - (path, "") - } -} - -fn find_node<'a>(at: TNode<'a>, path: &str) -> Option> { - let (item, path) = path_component_left(path); - if item.is_empty() { - assert_eq!(path, ""); - Some(at) - } else { - let child = at.children().find(|c| c.name().unwrap() == item)?; - if path.is_empty() { - Some(child) - } else { - find_node(child, path) - } - } -} diff --git a/src/arch/aarch64/gic/mod.rs b/src/arch/aarch64/gic/mod.rs index 19bf8837..0bb60616 100644 --- a/src/arch/aarch64/gic/mod.rs +++ b/src/arch/aarch64/gic/mod.rs @@ -8,20 +8,18 @@ use alloc::boxed::Box; use device_api::{ interrupt::{ ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable, - IpiDeliveryTarget, IrqNumber, IrqOptions, LocalInterruptController, + IpiDeliveryTarget, IrqOptions, LocalInterruptController, }, Device, }; +use kernel_util::util::OneTimeInit; use crate::{ - arch::{ - aarch64::devtree::{self, DevTreeIndexPropExt}, - Architecture, CpuMessage, - }, + arch::{aarch64::IrqNumber, Architecture, CpuMessage}, + device::devtree::{self, DevTreeIndexPropExt}, device_tree_driver, mem::device::{DeviceMemory, DeviceMemoryIo}, sync::IrqSafeSpinlock, - util::OneTimeInit, }; use self::{gicc::Gicc, gicd::Gicd}; @@ -71,6 +69,8 @@ impl Device for Gic { } impl ExternalInterruptController for Gic { + type IrqNumber = IrqNumber; + fn register_irq( &self, irq: IrqNumber, diff --git a/src/arch/aarch64/mod.rs b/src/arch/aarch64/mod.rs index b2834c36..407924fe 100644 --- a/src/arch/aarch64/mod.rs +++ b/src/arch/aarch64/mod.rs @@ -11,30 +11,34 @@ use aarch64_cpu::{ }; use abi::error::Error; use device_api::{ - interrupt::{ - ExternalInterruptController, IpiDeliveryTarget, IrqNumber, LocalInterruptController, - }, + interrupt::{ExternalInterruptController, IpiDeliveryTarget, LocalInterruptController}, timer::MonotonicTimestampProviderDevice, ResetDevice, }; use fdt_rs::prelude::PropReader; use git_version::git_version; +use kernel_util::util::OneTimeInit; use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use crate::{ - arch::{aarch64::devtree::FdtMemoryRegionIter, Architecture}, - debug::{self}, - device::{self, power::arm_psci::Psci, DevTreeNodeInfo}, + arch::Architecture, + debug, + device::{ + self, + devtree::{ + self, DevTreeIndexNodePropGet, DevTreeIndexPropExt, DevTreeNodeInfo, DeviceTree, + FdtMemoryRegionIter, + }, + power::arm_psci::Psci, + }, fs::{Initrd, INITRD_DATA}, mem::{ phys::{self, reserved::reserve_region, PhysicalMemoryRegion}, ConvertAddress, }, - util::OneTimeInit, }; use self::{ - devtree::{DevTreeIndexPropExt, DeviceTree}, smp::CPU_COUNT, table::{init_fixed_tables, KERNEL_TABLES}, }; @@ -44,7 +48,6 @@ use super::CpuMessage; pub mod boot; pub mod context; pub mod cpu; -pub mod devtree; pub mod exception; pub mod gic; pub mod smp; @@ -59,10 +62,16 @@ struct KernelStack { data: [u8; BOOT_STACK_SIZE], } +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum IrqNumber { + Private(u32), + Shared(u32), +} + /// AArch64 platform interface pub struct AArch64 { dt: OneTimeInit>, - ext_intc: OneTimeInit<&'static dyn ExternalInterruptController>, + ext_intc: OneTimeInit<&'static dyn ExternalInterruptController>, local_intc: OneTimeInit<&'static dyn LocalInterruptController>, mtimer: OneTimeInit<&'static dyn MonotonicTimestampProviderDevice>, reset: OneTimeInit<&'static dyn ResetDevice>, @@ -85,6 +94,8 @@ pub static ARCHITECTURE: AArch64 = AArch64 { }; impl Architecture for AArch64 { + type IrqNumber = IrqNumber; + const KERNEL_VIRT_OFFSET: usize = 0xFFFFFF8000000000; unsafe fn init_mmu(&self, bsp: bool) { @@ -165,7 +176,7 @@ impl Architecture for AArch64 { fn register_external_interrupt_controller( &self, - intc: &'static dyn ExternalInterruptController, + intc: &'static dyn ExternalInterruptController, ) -> Result<(), Error> { self.ext_intc.init(intc); Ok(()) @@ -192,7 +203,9 @@ impl Architecture for AArch64 { Ok(()) } - fn external_interrupt_controller(&self) -> &'static dyn ExternalInterruptController { + fn external_interrupt_controller( + &self, + ) -> &'static dyn ExternalInterruptController { *self.ext_intc.get() } @@ -272,8 +285,7 @@ impl AArch64 { fn chosen_stdout_path<'a>(dt: &'a DeviceTree) -> Option<&'a str> { let chosen = dt.node_by_path("/chosen")?; - let prop = devtree::find_prop(&chosen, "stdout-path")?; - prop.str().ok() + chosen.prop("stdout-path") } fn init_platform(&self, bsp: bool) { @@ -294,7 +306,7 @@ impl AArch64 { node, }; - if let Some((device, _)) = device::probe_dt_node(&probe) { + if let Some((device, _)) = devtree::probe_dt_node(&probe) { unsafe { device.init().unwrap(); } @@ -313,7 +325,7 @@ impl AArch64 { // Probe and initialize the rest of devices let nodes = dt.root().children(); - if let Err(error) = device::enumerate_dt( + if let Err(error) = devtree::enumerate_dt( address_cells, size_cells, nodes, @@ -323,7 +335,7 @@ impl AArch64 { return Ok(()); } - if let Some((device, _)) = device::probe_dt_node(&probe) { + if let Some((device, _)) = devtree::probe_dt_node(&probe) { unsafe { device.init()?; } diff --git a/src/arch/aarch64/smp.rs b/src/arch/aarch64/smp.rs index 36366aed..424a8cf7 100644 --- a/src/arch/aarch64/smp.rs +++ b/src/arch/aarch64/smp.rs @@ -14,7 +14,7 @@ use crate::{ }, }; -use super::devtree::{self, DevTreeIndexNodePropGet, DeviceTree}; +use crate::device::devtree::{self, DevTreeIndexNodePropGet, DeviceTree}; #[derive(Debug)] enum CpuEnableMethod { diff --git a/src/arch/aarch64/timer.rs b/src/arch/aarch64/timer.rs index fc00a0c7..e4cc7c8f 100644 --- a/src/arch/aarch64/timer.rs +++ b/src/arch/aarch64/timer.rs @@ -5,15 +5,11 @@ use core::time::Duration; use aarch64_cpu::registers::{CNTFRQ_EL0, CNTPCT_EL0, CNTP_CTL_EL0, CNTP_TVAL_EL0}; use abi::error::Error; use alloc::boxed::Box; -use device_api::{ - interrupt::{InterruptHandler, IrqNumber}, - timer::MonotonicTimestampProviderDevice, - Device, -}; +use device_api::{interrupt::InterruptHandler, timer::MonotonicTimestampProviderDevice, Device}; use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use crate::{ - arch::{Architecture, ARCHITECTURE}, + arch::{aarch64::IrqNumber, Architecture, ARCHITECTURE}, device_tree_driver, proc::wait, task::tasklet, diff --git a/src/arch/mod.rs b/src/arch/mod.rs index 34d4b2bc..ae4f3b11 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -19,31 +19,27 @@ macro_rules! absolute_address { }}; } -pub mod aarch64; - -pub use aarch64::{AArch64 as ArchitectureImpl, ARCHITECTURE}; +use cfg_if::cfg_if; +// pub use aarch64::{AArch64 as ArchitectureImpl, ARCHITECTURE}; use device_api::{ interrupt::{ExternalInterruptController, IpiDeliveryTarget, LocalInterruptController}, timer::MonotonicTimestampProviderDevice, ResetDevice, }; -// cfg_if! { -// if #[cfg(target_arch = "aarch64")] { -// -// pub use aarch64::{AArch64 as ArchitectureImpl, ARCHITECTURE, PlatformImpl, PLATFORM}; -// } else if #[cfg(target_arch = "x86_64")] { -// pub mod x86_64; -// -// pub use x86_64::{ -// X86_64 as ArchitectureImpl, -// X86_64 as PlatformImpl, -// ARCHITECTURE, -// ARCHITECTURE as PLATFORM -// }; -// } else { -// compile_error!("Architecture is not supported"); -// } -// } + +cfg_if! { + if #[cfg(target_arch = "aarch64")] { + pub mod aarch64; + + pub use aarch64::{AArch64 as ArchitectureImpl, ARCHITECTURE}; + } else if #[cfg(target_arch = "x86_64")] { + pub mod x86_64; + + pub use x86_64::{X86_64 as ArchitectureImpl, ARCHITECTURE}; + } else { + compile_error!("Architecture is not supported"); + } +} /// Describes messages sent from some CPU to others #[derive(Clone, Copy, PartialEq, Debug)] @@ -58,6 +54,9 @@ pub trait Architecture { /// Address, to which "zero" address is mapped in the virtual address space const KERNEL_VIRT_OFFSET: usize; + /// IRQ number type associated with the architecture + type IrqNumber; + /// Initializes the memory management unit and sets up virtual memory management. /// `bsp` flag is provided to make sure mapping tables are only initialized once in a SMP /// system. @@ -99,7 +98,7 @@ pub trait Architecture { /// Adds an external interrupt controller to the system fn register_external_interrupt_controller( &self, - intc: &'static dyn ExternalInterruptController, + intc: &'static dyn ExternalInterruptController, ) -> Result<(), Error>; /// Adds a local interrupt controller to the system @@ -119,15 +118,17 @@ pub trait Architecture { // TODO only supports 1 extintc per system /// Returns the primary external interrupt controller - fn external_interrupt_controller(&self) -> &'static dyn ExternalInterruptController; + fn external_interrupt_controller( + &'static self, + ) -> &'static dyn ExternalInterruptController; /// Returns the local interrupt controller fn local_interrupt_controller( - &self, + &'static self, ) -> &'static dyn LocalInterruptController; /// Returns the monotonic timer - fn monotonic_timer(&self) -> &'static dyn MonotonicTimestampProviderDevice; + fn monotonic_timer(&'static self) -> &'static dyn MonotonicTimestampProviderDevice; /// Sends a message to the requested set of CPUs through an interprocessor interrupt. /// diff --git a/src/arch/x86_64/apic/ioapic.rs b/src/arch/x86_64/apic/ioapic.rs index 4cc5c33a..ff5b50eb 100644 --- a/src/arch/x86_64/apic/ioapic.rs +++ b/src/arch/x86_64/apic/ioapic.rs @@ -1,19 +1,21 @@ //! x86-64 I/O APIC driver implementation use abi::error::Error; use acpi::platform::interrupt::{Apic as AcpiApic, Polarity, TriggerMode}; - -use crate::{ - arch::x86_64::apic::local::BSP_APIC_ID, - device::{ - interrupt::{ExternalInterruptController, InterruptSource}, - Device, InitializableDevice, +use device_api::{ + interrupt::{ + ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable, + IrqLevel, IrqOptions, IrqTrigger, }, - mem::ConvertAddress, - sync::IrqSafeSpinlock, - util::OneTimeInit, + Device, }; -use super::{IrqNumber, Table, APIC_EXTERNAL_OFFSET}; +use crate::{ + arch::x86_64::{apic::local::BSP_APIC_ID, IrqNumber}, + mem::ConvertAddress, + sync::IrqSafeSpinlock, +}; + +use super::{APIC_EXTERNAL_OFFSET, MAX_EXTERNAL_VECTORS}; // IRQ 0 is timer, IRQ 1 reserved (for now?), +32 offset for exception entries const IO_APIC_VECTOR_OFFSET: u32 = 32 + APIC_EXTERNAL_OFFSET; @@ -54,10 +56,10 @@ struct Inner { /// I/O APIC interface. Provides a way to route and control how interrupts from external devices /// are handled. pub struct IoApic { - inner: OneTimeInit>, - isa_redirections: OneTimeInit<[Option; 16]>, + inner: IrqSafeSpinlock, + isa_redirections: [Option; 16], - table: IrqSafeSpinlock, + table: IrqSafeSpinlock>, } impl Regs { @@ -108,21 +110,29 @@ impl Inner { Ok(()) } - fn configure_gsi(&mut self, gsi: u32, active_low: bool, level_triggered: bool) { + fn configure_gsi(&mut self, gsi: u32, options: IrqOptions) { assert!(gsi < self.max_gsi); let mut low = self.regs.read(REG_REDIRECTION_BASE + gsi * 2); - if active_low { - low |= ENTRY_LOW_POLARITY_LOW; - } else { - low &= !ENTRY_LOW_POLARITY_LOW; + match options.level { + IrqLevel::Default => (), + IrqLevel::ActiveLow => { + low |= ENTRY_LOW_POLARITY_LOW; + } + IrqLevel::ActiveHigh => { + low &= !ENTRY_LOW_POLARITY_LOW; + } } - if level_triggered { - low |= ENTRY_LOW_TRIGGER_LEVEL; - } else { - low &= !ENTRY_LOW_TRIGGER_LEVEL; + match options.trigger { + IrqTrigger::Default => (), + IrqTrigger::Level => { + low |= ENTRY_LOW_TRIGGER_LEVEL; + } + IrqTrigger::Edge => { + low &= !ENTRY_LOW_TRIGGER_LEVEL; + } } self.regs.write(REG_REDIRECTION_BASE + gsi * 2, low); @@ -144,71 +154,69 @@ impl Inner { } impl Device for IoApic { - fn name(&self) -> &'static str { + fn display_name(&self) -> &'static str { "I/O APIC" } + + unsafe fn init(&'static self) -> Result<(), Error> { + todo!() + } } impl ExternalInterruptController for IoApic { type IrqNumber = IrqNumber; - fn enable_irq(&self, irq: Self::IrqNumber) -> Result<(), Error> { - let mut inner = self.inner.get().lock(); - let gsi = self.translate_irq(irq); - inner.set_gsi_enabled(gsi, true); - Ok(()) - } - - fn register_handler( + fn register_irq( &self, irq: Self::IrqNumber, - handler: &'static (dyn InterruptSource + Sync), + options: IrqOptions, + handler: &'static dyn InterruptHandler, ) -> Result<(), Error> { - let mut inner = self.inner.get().lock(); - let mut table = self.table.lock(); + let mut inner = self.inner.lock(); + let table_vector = self.table.lock().insert_least_loaded(handler)?; - // Allocate a vector - let table_vector = table.least_loaded(); let gsi_target_vector = (table_vector as u32) + IO_APIC_VECTOR_OFFSET; let bsp_apic = *BSP_APIC_ID.get(); infoln!( "Binding {:?} ({}) to {}:{}", irq, - handler.name(), + handler.display_name(), bsp_apic, table_vector ); - table.add_handler(table_vector, handler)?; let gsi = self.translate_irq(irq); - inner.map_gsi(gsi, gsi_target_vector, *BSP_APIC_ID.get())?; + inner.configure_gsi(gsi, options); + inner.map_gsi(gsi, gsi_target_vector, bsp_apic)?; Ok(()) } - fn configure_irq( - &self, - irq: Self::IrqNumber, - active_low: bool, - level_triggered: bool, - ) -> Result<(), Error> { - let mut inner = self.inner.get().lock(); + fn enable_irq(&self, irq: Self::IrqNumber) -> Result<(), Error> { + let mut inner = self.inner.lock(); let gsi = self.translate_irq(irq); - - inner.configure_gsi(gsi, active_low, level_triggered); - + inner.set_gsi_enabled(gsi, true); Ok(()) } + + fn handle_specific_irq(&self, gsi: usize) { + let table = self.table.lock(); + + if let Some(handler) = table.handler(gsi) { + handler.handle_irq(); + } else { + warnln!("No handler set for GSI #{}", gsi); + } + } } -impl InitializableDevice for IoApic { - type Options = AcpiApic; +impl IoApic { + /// Creates an I/O APIC instance from its ACPI definition + pub fn from_acpi(info: &AcpiApic) -> Result { + let ioapic = info.io_apics.first().unwrap(); - unsafe fn init(&self, info: Self::Options) -> Result<(), Error> { - let io_apic = info.io_apics.first().unwrap(); - - infoln!("I/O APIC at {:#x}", io_apic.address); + infoln!("I/O APIC at {:#x}", ioapic.address); let mut isa_redirections = [None; 16]; @@ -236,8 +244,9 @@ impl InitializableDevice for IoApic { }); } + // TODO properly map this using DeviceMemory let regs = Regs { - base: unsafe { (io_apic.address as usize).virtualize() }, + base: unsafe { (ioapic.address as usize).virtualize() }, }; let max_gsi = (regs.read(REG_IOAPIC_VERSION) >> 16) & 0xFF; @@ -251,36 +260,15 @@ impl InitializableDevice for IoApic { inner.set_gsi_enabled(gsi, false); } - self.inner.init(IrqSafeSpinlock::new(inner)); - self.isa_redirections.init(isa_redirections); - - Ok(()) - } -} - -impl IoApic { - /// Constructs an uninitialized I/O APIC interface - pub const fn new() -> Self { - Self { - inner: OneTimeInit::new(), - isa_redirections: OneTimeInit::new(), - table: IrqSafeSpinlock::new(Table::new()), - } - } - - /// Handles an interrupt with given GSI number - pub fn handle_irq(&self, gsi: u32) { - let table = self.table.lock(); - - if let Some(handler) = table.vectors[gsi as usize] { - handler.handle_irq().expect("IRQ handler failed"); - } else { - warnln!("No handler set for GSI #{}", gsi); - } + Ok(Self { + isa_redirections, + inner: IrqSafeSpinlock::new(inner), + table: IrqSafeSpinlock::new(FixedInterruptTable::new()), + }) } fn translate_irq(&self, irq: IrqNumber) -> u32 { - let redir = self.isa_redirections.get(); + let redir = &self.isa_redirections; match irq { IrqNumber::Isa(isa) => redir[isa as usize] .map(|t| t.gsi_index) diff --git a/src/arch/x86_64/apic/local.rs b/src/arch/x86_64/apic/local.rs index df5d9b6f..20f0bebb 100644 --- a/src/arch/x86_64/apic/local.rs +++ b/src/arch/x86_64/apic/local.rs @@ -1,4 +1,6 @@ //! x86-64 Local APIC driver implementation +use device_api::{interrupt::LocalInterruptController, Device}; +use kernel_util::util::OneTimeInit; use tock_registers::{ interfaces::{ReadWriteable, Readable, Writeable}, register_bitfields, register_structs, @@ -6,13 +8,11 @@ use tock_registers::{ }; use crate::{ - arch::x86_64::registers::MSR_IA32_APIC_BASE, device::interrupt::IpiDeliveryTarget, - mem::ConvertAddress, util::OneTimeInit, + arch::{x86_64::registers::MSR_IA32_APIC_BASE, CpuMessage}, + mem::ConvertAddress, }; -use super::{ - APIC_IPI_VECTOR, APIC_LINT0_VECTOR, APIC_LINT1_VECTOR, APIC_SPURIOUS_VECTOR, APIC_TIMER_VECTOR, -}; +use super::{APIC_LINT0_VECTOR, APIC_LINT1_VECTOR, APIC_SPURIOUS_VECTOR, APIC_TIMER_VECTOR}; const TIMER_INTERVAL: u32 = 150000; @@ -122,6 +122,31 @@ pub struct LocalApic { regs: &'static Regs, } +unsafe impl Send for LocalApic {} +unsafe impl Sync for LocalApic {} + +impl Device for LocalApic { + fn display_name(&self) -> &'static str { + "Local APIC" + } +} + +impl LocalInterruptController for LocalApic { + type IpiMessage = CpuMessage; + + fn send_ipi( + &self, + _target: device_api::interrupt::IpiDeliveryTarget, + _msg: Self::IpiMessage, + ) -> Result<(), abi::error::Error> { + todo!() + } + + unsafe fn init_ap(&self) -> Result<(), abi::error::Error> { + todo!() + } +} + impl LocalApic { /// Constructs a new instance of Local APIC. /// @@ -232,28 +257,28 @@ impl LocalApic { } } - /// Issues an interprocessor interrupt for the target. - /// - /// # Safety - /// - /// Unsafe: this function may break the control flow on the target processors. - pub unsafe fn send_ipi(&self, target: IpiDeliveryTarget) { - while self.regs.ICR0.matches_all(ICR0::DeliveryStatus::SET) { - core::hint::spin_loop(); - } + // /// Issues an interprocessor interrupt for the target. + // /// + // /// # Safety + // /// + // /// Unsafe: this function may break the control flow on the target processors. + // pub unsafe fn send_ipi(&self, target: IpiDeliveryTarget) { + // while self.regs.ICR0.matches_all(ICR0::DeliveryStatus::SET) { + // core::hint::spin_loop(); + // } - match target { - IpiDeliveryTarget::AllExceptLocal => { - self.regs.ICR1.write(ICR1::PhysicalDestination.val(0)); - self.regs.ICR0.write( - ICR0::Vector.val(APIC_IPI_VECTOR + 32) - + ICR0::Destination::NMI - + ICR0::DestinationType::AllExceptThis, - ); - } - IpiDeliveryTarget::Specified(_) => todo!(), - } - } + // match target { + // IpiDeliveryTarget::AllExceptLocal => { + // self.regs.ICR1.write(ICR1::PhysicalDestination.val(0)); + // self.regs.ICR0.write( + // ICR0::Vector.val(APIC_IPI_VECTOR + 32) + // + ICR0::Destination::NMI + // + ICR0::DestinationType::AllExceptThis, + // ); + // } + // IpiDeliveryTarget::Specified(_) => todo!(), + // } + // } #[inline] fn base() -> usize { diff --git a/src/arch/x86_64/apic/mod.rs b/src/arch/x86_64/apic/mod.rs index 5f5fdea5..a363b572 100644 --- a/src/arch/x86_64/apic/mod.rs +++ b/src/arch/x86_64/apic/mod.rs @@ -2,15 +2,15 @@ use core::arch::global_asm; -use abi::error::Error; - use crate::{ - arch::{x86_64::cpu::Cpu, PLATFORM}, - device::interrupt::IrqHandler, + arch::{x86_64::cpu::Cpu, Architecture}, task::process::Process, }; -use super::exception::{self, IrqFrame}; +use super::{ + exception::{self, IrqFrame}, + ARCHITECTURE, +}; pub mod ioapic; pub mod local; @@ -32,37 +32,6 @@ pub const APIC_EXTERNAL_OFFSET: u32 = 4; /// Maximum number of APIC vectors allocated for handling IRQs from I/O APIC pub const MAX_EXTERNAL_VECTORS: u32 = 16; -/// x86-64 interrupt number wrapper -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum IrqNumber { - /// Legacy (ISA) interrupt. Can have value in range 0..16. - Isa(u8), - /// Global system interrupt. Means an external interrupt for I/O APIC. - Gsi(u8), -} - -struct Table { - vectors: [Option; MAX_EXTERNAL_VECTORS as usize], -} - -impl Table { - pub const fn new() -> Self { - Self { - vectors: [None; MAX_EXTERNAL_VECTORS as usize], - } - } - - pub fn add_handler(&mut self, vector: usize, handler: IrqHandler) -> Result<(), Error> { - let old = self.vectors[vector].replace(handler); - assert!(old.is_none()); - Ok(()) - } - - pub fn least_loaded(&self) -> usize { - self.vectors.iter().position(|p| p.is_none()).unwrap() - } -} - /// Fills the IDT with interrupt vectors for this APIC pub fn setup_vectors(idt: &mut [exception::Entry]) { extern "C" { @@ -83,7 +52,9 @@ unsafe extern "C" fn irq_handler(vector: usize, frame: *mut IrqFrame) { let cpu = Cpu::local(); let frame = &mut *frame; - PLATFORM.ioapic.handle_irq(vector as u32); + ARCHITECTURE + .external_interrupt_controller() + .handle_specific_irq(vector); cpu.local_apic().clear_interrupt(); if let Some(process) = Process::get_current() { diff --git a/src/arch/x86_64/boot/mod.rs b/src/arch/x86_64/boot/mod.rs index f28ce514..5dd6e45c 100644 --- a/src/arch/x86_64/boot/mod.rs +++ b/src/arch/x86_64/boot/mod.rs @@ -9,18 +9,21 @@ use yboot_proto::{ use crate::{ arch::{ - x86_64::{ - apic::local::LocalApic, cpu::Cpu, cpuid, exception, kernel_main, - registers::MSR_IA32_KERNEL_GS_BASE, syscall, CPU_INIT_FENCE, - }, - Architecture, ArchitectureImpl, + x86_64::{cpuid, exception, registers::MSR_IA32_KERNEL_GS_BASE, BootData}, + Architecture, ArchitectureImpl, ARCHITECTURE, + }, + fs::devfs, + kernel_main, kernel_secondary_main, + mem::{ + heap, + phys::{self, PageUsage}, + ConvertAddress, KERNEL_VIRT_OFFSET, }, - device::platform::Platform, - mem::KERNEL_VIRT_OFFSET, - task, }; -use super::ARCHITECTURE; +use super::smp::CPU_COUNT; + +// use super::ARCHITECTURE; const BOOT_STACK_SIZE: usize = 65536; @@ -76,45 +79,58 @@ unsafe extern "C" fn __x86_64_upper_entry() -> ! { ARCHITECTURE.init_mmu(true); core::arch::asm!("wbinvd"); - kernel_main(&YBOOT_DATA) + ARCHITECTURE.set_boot_data(BootData::YBoot(&YBOOT_DATA)); + ARCHITECTURE + .init_physical_memory() + .expect("Failed to initialize the physical memory manager"); + + let heap_base = phys::alloc_pages_contiguous(16, PageUsage::Used) + .expect("Couldn't allocate memory for heap"); + heap::init_heap(heap_base.virtualize(), 16 * 0x1000); + + exception::init_exceptions(0); + + devfs::init(); + + // Initializes: local CPU, platform devices (timers/serials/etc), debug output + ARCHITECTURE.init_platform(0); + + cpuid::feature_gate(); + + kernel_main() } /// Application processor entry point pub extern "C" fn __x86_64_ap_entry() -> ! { - loop {} - // let cpu_id = CPU_COUNT.load(Ordering::Acquire); + let cpu_id = CPU_COUNT.load(Ordering::Acquire); - // MSR_IA32_KERNEL_GS_BASE.set(&UNINIT_CPU as *const _ as u64); - // unsafe { - // core::arch::asm!("swapgs"); - // } - // MSR_IA32_KERNEL_GS_BASE.set(&UNINIT_CPU as *const _ as u64); - // unsafe { - // core::arch::asm!("swapgs"); - // } + MSR_IA32_KERNEL_GS_BASE.set(&UNINIT_CPU as *const _ as u64); + unsafe { + core::arch::asm!("swapgs"); + } + MSR_IA32_KERNEL_GS_BASE.set(&UNINIT_CPU as *const _ as u64); + unsafe { + core::arch::asm!("swapgs"); + } - // // Still not initialized: GDT, IDT, CPU features, syscall, kernel_gs_base - // cpuid::feature_gate(); + // Still not initialized: GDT, IDT, CPU features, syscall, kernel_gs_base + cpuid::feature_gate(); - // infoln!("cpu{} initializing", cpu_id); - // unsafe { - // ARCHITECTURE.init_mmu(false); - // core::arch::asm!("wbinvd"); + infoln!("cpu{} initializing", cpu_id); + unsafe { + ARCHITECTURE.init_mmu(false); + core::arch::asm!("wbinvd"); - // Cpu::init_local(LocalApic::new(), cpu_id as u32); - // syscall::init_syscall(); - // exception::init_exceptions(cpu_id); + // Cpu::init_local(LocalApic::new(), cpu_id as u32); + // syscall::init_syscall(); + exception::init_exceptions(cpu_id); - // ARCHITECTURE.init(false).unwrap(); - // } + ARCHITECTURE.init_platform(cpu_id); + } - // CPU_COUNT.fetch_add(1, Ordering::Release); + CPU_COUNT.fetch_add(1, Ordering::Release); - // CPU_INIT_FENCE.wait_one(); - - // infoln!("cpu{} initialized", cpu_id); - - // unsafe { task::enter() } + kernel_secondary_main() } global_asm!( diff --git a/src/arch/x86_64/cpu.rs b/src/arch/x86_64/cpu.rs index 9737049a..122fa42e 100644 --- a/src/arch/x86_64/cpu.rs +++ b/src/arch/x86_64/cpu.rs @@ -2,12 +2,12 @@ use core::ptr::null_mut; use alloc::boxed::Box; +use kernel_util::util::OneTimeInit; use tock_registers::interfaces::Writeable; use crate::{ - arch::x86_64::{gdt, registers::MSR_IA32_KERNEL_GS_BASE}, + arch::x86_64::{gdt, registers::MSR_IA32_KERNEL_GS_BASE, syscall}, task::sched::CpuQueue, - util::OneTimeInit, }; use super::apic::local::LocalApic; @@ -53,6 +53,8 @@ impl Cpu { MSR_IA32_KERNEL_GS_BASE.set(this as u64); core::arch::asm!("wbinvd; swapgs"); + + syscall::init_syscall(); } /// Returns this CPU's local data structure diff --git a/src/arch/x86_64/mod.rs b/src/arch/x86_64/mod.rs index ac4136f3..a867a5ae 100644 --- a/src/arch/x86_64/mod.rs +++ b/src/arch/x86_64/mod.rs @@ -2,50 +2,50 @@ use core::{ptr::NonNull, sync::atomic::Ordering}; use abi::error::Error; -use acpi::{ - platform::ProcessorInfo, AcpiHandler, AcpiTables, HpetInfo, InterruptModel, PhysicalMapping, -}; +use acpi::{AcpiHandler, AcpiTables, HpetInfo, InterruptModel, PhysicalMapping}; +use alloc::boxed::Box; use cpu::Cpu; +use device_api::{ + input::KeyboardProducer, + interrupt::{ExternalInterruptController, IpiDeliveryTarget, LocalInterruptController}, + timer::MonotonicTimestampProviderDevice, + Device, +}; use git_version::git_version; -use yboot_proto::{v1::FramebufferOption, AvailableRegion, IterableMemoryMap, LoadProtocolV1}; +use kernel_util::util::OneTimeInit; +use yboot_proto::{AvailableRegion, IterableMemoryMap}; use crate::{ arch::x86_64::{ apic::local::LocalApic, + peripherals::serial::ComPort, table::{init_fixed_tables, KERNEL_TABLES}, }, debug::{self, LogLevel}, device::{ + self, display::{ - console::{add_console_autoflush, ConsoleBuffer, ConsoleRow, DisplayConsole}, + console::{self, ConsoleBuffer}, fb_console::FramebufferConsole, linear_fb::LinearFramebuffer, }, - input::KeyboardDevice, - interrupt::{ExternalInterruptController, InterruptSource, IpiDeliveryTarget}, - platform::Platform, - timer::TimestampSource, - tty::CombinedTerminal, - InitializableDevice, + tty::combined::CombinedTerminal, }, fs::{ devfs::{self, CharDeviceType}, Initrd, INITRD_DATA, }, mem::{ - heap, - phys::{self, reserved::reserve_region, PageUsage, PhysicalMemoryRegion}, + phys::{self, reserved::reserve_region, PhysicalMemoryRegion}, ConvertAddress, }, - sync::SpinFence, - task, - util::OneTimeInit, + CPU_INIT_FENCE, }; use self::{ - apic::{ioapic::IoApic, IrqNumber}, + apic::ioapic::IoApic, intrinsics::IoPort, - peripherals::{hpet::Hpet, ps2::PS2Controller, serial::ComPort}, + peripherals::{hpet::Hpet, ps2::PS2Controller}, smp::CPU_COUNT, }; @@ -67,7 +67,14 @@ pub mod smp; pub mod syscall; pub mod table; -static CPU_INIT_FENCE: SpinFence = SpinFence::new(); +/// x86-64 interrupt number wrapper +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum IrqNumber { + /// Legacy (ISA) interrupt. Can have value in range 0..16. + Isa(u8), + /// Global system interrupt. Means an external interrupt for I/O APIC. + Gsi(u8), +} /// Helper trait to provide abstract access to available memory regions pub trait AbstractAvailableRegion { @@ -119,6 +126,27 @@ impl<'a, T: IterableMemoryMap<'a> + 'a> AbstractMemoryMap<'a> for T { #[derive(Clone, Copy)] struct AcpiHandlerImpl; +struct SimpleLogger; + +impl log::Log for SimpleLogger { + fn enabled(&self, _metadata: &log::Metadata) -> bool { + true + } + + fn log(&self, record: &log::Record) { + match record.level() { + log::Level::Warn => warnln!("{}", record.args()), + log::Level::Info => infoln!("{}", record.args()), + log::Level::Trace | log::Level::Debug => infoln!("{}", record.args()), + log::Level::Error => errorln!("{}", record.args()), + } + } + + fn flush(&self) {} +} + +static LOGGER: SimpleLogger = SimpleLogger; + impl AcpiHandler for AcpiHandlerImpl { // No actual address space modification is performed unsafe fn map_physical_region( @@ -143,28 +171,44 @@ impl AcpiHandler for AcpiHandlerImpl { fn unmap_physical_region(_region: &PhysicalMapping) {} } +/// Describes which kind of bootloader data was provided to the kernel +pub enum BootData { + /// [yboot_proto::LoadProtocolV1] + YBoot(&'static yboot_proto::LoadProtocolV1), +} + /// x86-64 architecture + platform implementation pub struct X86_64 { - yboot_framebuffer: OneTimeInit, - rsdp_phys_base: OneTimeInit, + boot_data: OneTimeInit, + acpi: OneTimeInit>, - acpi_processor_info: OneTimeInit, - - // TODO make this fully dynamic? + framebuffer: OneTimeInit, + fb_console: OneTimeInit, combined_terminal: OneTimeInit, - // Static devices with dynamic addresses - ioapic: IoApic, - timer: Hpet, - - // Static devices - ps2: PS2Controller, - com1_3: ComPort, + ioapic: OneTimeInit, + timer: OneTimeInit, } +/// x86-64 architecture implementation +pub static ARCHITECTURE: X86_64 = X86_64 { + boot_data: OneTimeInit::new(), + acpi: OneTimeInit::new(), + + framebuffer: OneTimeInit::new(), + fb_console: OneTimeInit::new(), + combined_terminal: OneTimeInit::new(), + + // Devices + ioapic: OneTimeInit::new(), + timer: OneTimeInit::new(), +}; + impl Architecture for X86_64 { const KERNEL_VIRT_OFFSET: usize = 0xFFFFFF8000000000; + type IrqNumber = IrqNumber; + unsafe fn init_mmu(&self, bsp: bool) { if bsp { init_fixed_tables(); @@ -174,16 +218,6 @@ impl Architecture for X86_64 { core::arch::asm!("wbinvd; mov {0}, %cr3", in(reg) cr3, options(att_syntax)); } - fn map_device_pages(&self, phys: usize, count: usize) -> Result { - unsafe { KERNEL_TABLES.map_device_pages(phys, count) } - } - - fn wait_for_interrupt() { - unsafe { - core::arch::asm!("hlt"); - } - } - unsafe fn set_interrupt_mask(mask: bool) { if mask { core::arch::asm!("cli"); @@ -201,98 +235,18 @@ impl Architecture for X86_64 { flags & (1 << 9) == 0 } - fn cpu_count() -> usize { - CPU_COUNT.load(Ordering::Acquire) - } -} - -impl Platform for X86_64 { - type IrqNumber = apic::IrqNumber; - - const KERNEL_PHYS_BASE: usize = 0x400000; - - unsafe fn init(&'static self, is_bsp: bool) -> Result<(), Error> { - if is_bsp { - Self::disable_8259(); - - // Initialize I/O APIC from ACPI - let rsdp = *self.rsdp_phys_base.get(); - let acpi_tables = AcpiTables::from_rsdp(AcpiHandlerImpl, rsdp).unwrap(); - - let hpet = HpetInfo::new(&acpi_tables).unwrap(); - let platform_info = acpi_tables.platform_info().unwrap(); - - if let Some(processor_info) = platform_info.processor_info { - self.acpi_processor_info.init(processor_info); - } - - let InterruptModel::Apic(apic_info) = platform_info.interrupt_model else { - panic!("Processor does not have APIC"); - }; - - self.ioapic.init(apic_info)?; - self.timer.init(hpet).unwrap(); - - // Enable IRQs for the devices - self.ps2.init_irq()?; - self.timer.init_irq()?; - - // Add a terminal to the devfs - // TODO this is ugly - let combined_terminal = CombinedTerminal::new(&self.ps2, FB_CONSOLE.get()); - self.combined_terminal.init(combined_terminal); - self.ps2.attach(self.combined_terminal.get()); - - devfs::add_char_device(self.combined_terminal.get(), CharDeviceType::TtyRegular)?; + fn wait_for_interrupt() { + unsafe { + core::arch::asm!("hlt"); } - - Ok(()) } - unsafe fn init_debug(&'static self) { - // Serial output - self.com1_3.init(()).ok(); - debug::add_sink(self.com1_3.port_a(), LogLevel::Debug); - - // Graphical output - if let Some(fb) = self.yboot_framebuffer.try_get() { - LINEAR_FB.init( - LinearFramebuffer::from_physical_bits( - fb.res_address as _, - fb.res_size as _, - fb.res_stride as _, - fb.res_width, - fb.res_height, - ) - .unwrap(), - ); - FB_CONSOLE.init(FramebufferConsole::from_framebuffer( - LINEAR_FB.get(), - &bitmap_font::tamzen::FONT_6x12, - ConsoleBuffer::fixed(&mut EARLY_CONSOLE_BUFFER, 32), - )); - - debug::add_sink(FB_CONSOLE.get(), LogLevel::Info); - } - - debug::reset(); + // CPU management + unsafe fn reset(&self) -> ! { + todo!() } - fn name(&self) -> &'static str { - "x86-64" - } - - fn interrupt_controller( - &self, - ) -> &dyn ExternalInterruptController { - &self.ioapic - } - - fn timestamp_source(&self) -> &dyn TimestampSource { - &self.timer - } - - unsafe fn send_ipi(&self, target: IpiDeliveryTarget, _msg: CpuMessage) -> Result<(), Error> { + unsafe fn send_ipi(&self, target: IpiDeliveryTarget, msg: CpuMessage) -> Result<(), Error> { if !CPU_INIT_FENCE.try_wait_all(1) { // Don't send an IPI: SMP not initialized yet return Ok(()); @@ -302,14 +256,132 @@ impl Platform for X86_64 { panic!("Local APIC has not been initialized yet"); }; - local_apic.send_ipi(target); + local_apic.send_ipi(target, msg) + } - Ok(()) + unsafe fn start_application_processors(&self) { + if let Some(acpi) = self.acpi.try_get() { + let Some(pinfo) = acpi.platform_info().ok().and_then(|p| p.processor_info) else { + return; + }; + + smp::start_ap_cores(&pinfo); + } + } + + fn cpu_count() -> usize { + CPU_COUNT.load(Ordering::Acquire) + } + + // Memory + fn map_device_pages(&self, phys: usize, count: usize) -> Result { + unsafe { KERNEL_TABLES.map_device_pages(phys, count) } + } + + // Devices + fn register_monotonic_timer( + &self, + _timer: &'static dyn device_api::timer::MonotonicTimestampProviderDevice, + ) -> Result<(), Error> { + todo!() + } + + fn register_local_interrupt_controller( + &self, + _intc: &'static dyn device_api::interrupt::LocalInterruptController< + IpiMessage = CpuMessage, + >, + ) -> Result<(), Error> { + todo!() + } + + fn register_external_interrupt_controller( + &self, + _intc: &'static dyn device_api::interrupt::ExternalInterruptController< + IrqNumber = Self::IrqNumber, + >, + ) -> Result<(), Error> { + todo!() + } + + fn register_reset_device( + &self, + _reset: &'static dyn device_api::ResetDevice, + ) -> Result<(), Error> { + todo!() + } + + fn monotonic_timer(&'static self) -> &'static dyn MonotonicTimestampProviderDevice { + self.timer.get() + } + + fn local_interrupt_controller( + &'static self, + ) -> &'static dyn LocalInterruptController { + todo!() + } + + fn external_interrupt_controller( + &'static self, + ) -> &'static dyn ExternalInterruptController { + self.ioapic.get() } } impl X86_64 { - unsafe fn init_physical_memory<'a, M: AbstractMemoryMap<'a>>(memory_map: &M) { + fn set_boot_data(&self, boot_data: BootData) { + match &boot_data { + BootData::YBoot(data) => { + // Setup initrd + Self::init_initrd( + data.initrd_address as usize, + (data.initrd_address + data.initrd_size) as usize, + ); + } + } + + self.boot_data.init(boot_data); + } + + fn init_initrd(initrd_start: usize, initrd_end: usize) { + if initrd_start == 0 || initrd_end <= initrd_start { + infoln!("No initrd loaded"); + return; + } + + let start_aligned = initrd_start & !0xFFF; + let end_aligned = initrd_end & !0xFFF; + + let data = unsafe { + core::slice::from_raw_parts( + initrd_start.virtualize() as *const _, + initrd_end - initrd_start, + ) + }; + + let initrd = Initrd { + phys_page_start: start_aligned, + phys_page_len: end_aligned - start_aligned, + data, + }; + + INITRD_DATA.init(initrd); + } + + fn init_acpi_from_boot_data(&self) { + match *self.boot_data.get() { + BootData::YBoot(data) => { + self.init_acpi_from_rsdp(data.rsdp_address as usize); + } + } + } + + fn init_acpi_from_rsdp(&self, address: usize) { + let acpi_tables = unsafe { AcpiTables::from_rsdp(AcpiHandlerImpl, address).unwrap() }; + self.acpi.init(acpi_tables); + } + + unsafe fn init_physical_memory(&self) -> Result<(), Error> { // Reserve the lower 8MiB of memory reserve_region( "lower-memory", @@ -318,9 +390,8 @@ impl X86_64 { size: 8 << 21, }, ); - // Reserve memory map - reserve_region("memory-map", memory_map.reserved_range()); + // Reserve initrd if let Some(initrd) = INITRD_DATA.try_get() { reserve_region( "initrd", @@ -331,21 +402,125 @@ impl X86_64 { ); } - phys::init_from_iter(memory_map.iter().map(|r| PhysicalMemoryRegion { - base: r.start_address(), - size: r.page_count() * 0x1000, - })) - .expect("Failed to initialize the physical memory manager"); + match *self.boot_data.get() { + BootData::YBoot(data) => { + let memory_map = &data.memory_map; - // Reallocate the console buffer - FB_CONSOLE - .get() - .reallocate_buffer() - .expect("Failed to reallocate console buffer"); + reserve_region("memory-map", memory_map.reserved_range()); + + phys::init_from_iter(IterableMemoryMap::iter(memory_map).map(|r| { + PhysicalMemoryRegion { + base: AbstractAvailableRegion::start_address(r), + size: AbstractAvailableRegion::page_count(r) * 0x1000, + } + })) + .expect("Failed to initialize the physical memory manager"); + } + } + + Ok(()) } - fn init_rsdp(&self, phys: usize) { - self.rsdp_phys_base.init(phys); + unsafe fn init_platform_from_acpi(&self, acpi: &AcpiTables) { + let platform_info = acpi.platform_info().unwrap(); + + let InterruptModel::Apic(apic_info) = platform_info.interrupt_model else { + panic!("Processor does not have an APIC"); + }; + + self.ioapic.init(IoApic::from_acpi(&apic_info).unwrap()); + + let hpet = HpetInfo::new(acpi).unwrap(); + self.timer.init(Hpet::from_acpi(&hpet).unwrap()); + } + + unsafe fn init_framebuffer(&'static self) { + match *self.boot_data.get() { + BootData::YBoot(data) => { + let fb = &data.opt_framebuffer; + self.framebuffer.init( + LinearFramebuffer::from_physical_bits( + fb.res_address as _, + fb.res_size as _, + fb.res_stride as _, + fb.res_width as _, + fb.res_height as _, + ) + .unwrap(), + ); + } + } + + self.fb_console.init(FramebufferConsole::from_framebuffer( + self.framebuffer.get(), + None, + ConsoleBuffer::new(32).unwrap(), + )); + + debug::add_sink(self.fb_console.get(), LogLevel::Info); + + // Add a terminal to the devfs + // TODO this is ugly + let combined_terminal = CombinedTerminal::new(self.fb_console.get()); + self.combined_terminal.init(combined_terminal); + + devfs::add_char_device(self.combined_terminal.get(), CharDeviceType::TtyRegular).unwrap(); + console::add_console_autoflush(self.fb_console.get()); + } + + unsafe fn init_platform(&'static self, cpu_id: usize) { + Cpu::init_local(LocalApic::new(), cpu_id as _); + + if cpu_id == 0 { + self.init_acpi_from_boot_data(); + Self::disable_8259(); + + // Initialize debug output as soon as possible + let com1_3 = Box::leak(Box::new(ComPort::new(0x3F8, 0x3E8, IrqNumber::Isa(4)))); + debug::add_sink(com1_3.port_a(), LogLevel::Debug); + // devfs::add_char_device(com1_3.port_a(), CharDeviceType::TtySerial).unwrap(); + + self.init_framebuffer(); + debug::reset(); + + log::set_logger(&LOGGER) + .map(|_| log::set_max_level(log::LevelFilter::Trace)) + .ok(); + + let ps2 = Box::leak(Box::new(PS2Controller::new( + IrqNumber::Isa(1), + IrqNumber::Isa(12), + 0x64, + 0x60, + ))); + ps2.init().unwrap(); + + // Print some stuff now that the output is initialized + infoln!( + "Yggdrasil v{} ({})", + env!("CARGO_PKG_VERSION"), + git_version!() + ); + infoln!("Initializing x86_64 platform"); + + if let Some(acpi) = self.acpi.try_get() { + self.init_platform_from_acpi(acpi); + } + + // Enable IRQs for the devices + self.timer.get().init_irq().unwrap(); + ps2.connect(self.combined_terminal.get()); + ps2.init_irq().unwrap(); + + device::register_device(self.ioapic.get()); + device::register_device(self.timer.get()); + device::register_device(ps2); + + infoln!("Device list:"); + for device in device::manager_lock().devices() { + infoln!("* {}", device.display_name()); + } + } } unsafe fn disable_8259() { @@ -370,121 +545,3 @@ impl X86_64 { pic_slave_cmd.write(0x20); } } - -/// x86-64 architecture + platform implementation -pub static ARCHITECTURE: X86_64 = X86_64 { - rsdp_phys_base: OneTimeInit::new(), - yboot_framebuffer: OneTimeInit::new(), - - acpi_processor_info: OneTimeInit::new(), - - combined_terminal: OneTimeInit::new(), - - ioapic: IoApic::new(), - timer: Hpet::uninit(), - - ps2: PS2Controller::new(IrqNumber::Isa(1), IrqNumber::Isa(12), 0x64, 0x60), - com1_3: ComPort::new(0x3F8, 0x3E8, IrqNumber::Isa(4)), -}; - -static LINEAR_FB: OneTimeInit = OneTimeInit::new(); -static FB_CONSOLE: OneTimeInit = OneTimeInit::new(); - -const EARLY_BUFFER_LINES: usize = 32; -static mut EARLY_CONSOLE_BUFFER: [ConsoleRow; EARLY_BUFFER_LINES] = - [ConsoleRow::zeroed(); EARLY_BUFFER_LINES]; - -fn setup_initrd(initrd_start: usize, initrd_end: usize) { - if initrd_start == 0 || initrd_end <= initrd_start { - infoln!("No initrd loaded"); - return; - } - - let start_aligned = initrd_start & !0xFFF; - let end_aligned = initrd_end & !0xFFF; - - let data = unsafe { - core::slice::from_raw_parts( - initrd_start.virtualize() as *const _, - initrd_end - initrd_start, - ) - }; - - let initrd = Initrd { - phys_page_start: start_aligned, - phys_page_len: end_aligned - start_aligned, - data, - }; - - INITRD_DATA.init(initrd); -} - -fn kernel_main(boot_data: &LoadProtocolV1) -> ! { - ARCHITECTURE - .yboot_framebuffer - .init(boot_data.opt_framebuffer); - - unsafe { - ARCHITECTURE.init_debug(); - } - - infoln!("Yggdrasil kernel git {} starting", git_version!()); - - cpuid::feature_gate(); - - if boot_data.memory_map.address > 0xFFFFFFFF { - errorln!("Unhandled case: memory map is above 4GiB"); - loop { - X86_64::wait_for_interrupt(); - } - } - - if boot_data.rsdp_address != 0 { - infoln!("ACPI RSDP at {:#x}", boot_data.rsdp_address); - ARCHITECTURE.init_rsdp(boot_data.rsdp_address as _); - } - - // Reserve initrd - let initrd_start = boot_data.initrd_address as usize; - let initrd_end = initrd_start + boot_data.initrd_size as usize; - - setup_initrd(initrd_start, initrd_end); - - // Setup physical memory allocation - unsafe { - X86_64::init_physical_memory(&boot_data.memory_map); - } - - // Allocate memory for the kernel heap - let heap_base = phys::alloc_pages_contiguous(16, PageUsage::Used) - .expect("Could not allocate a block for heap"); - unsafe { - heap::init_heap(heap_base.virtualize(), 16 * 0x1000); - } - - // Add console to flush list - add_console_autoflush(FB_CONSOLE.get()); - - // Also initializes local APIC - unsafe { - Cpu::init_local(LocalApic::new(), 0); - - syscall::init_syscall(); - - // TODO per-CPU IDTs? - exception::init_exceptions(0); - - devfs::init(); - - ARCHITECTURE.init(true).unwrap(); - - if let Some(info) = ARCHITECTURE.acpi_processor_info.try_get() { - smp::start_ap_cores(info); - } - - CPU_INIT_FENCE.signal(); - - task::init().expect("Failed to initialize the scheduler"); - task::enter() - } -} diff --git a/src/arch/x86_64/peripherals/hpet.rs b/src/arch/x86_64/peripherals/hpet.rs index 30766e6f..71fbc63a 100644 --- a/src/arch/x86_64/peripherals/hpet.rs +++ b/src/arch/x86_64/peripherals/hpet.rs @@ -3,6 +3,11 @@ use core::time::Duration; use abi::error::Error; use acpi::hpet::HpetInfo as AcpiHpet; +use device_api::{ + interrupt::{InterruptHandler, IrqLevel, IrqOptions, IrqTrigger}, + timer::MonotonicTimestampProviderDevice, + Device, +}; use tock_registers::{ interfaces::{ReadWriteable, Readable, Writeable}, register_bitfields, register_structs, @@ -10,16 +15,11 @@ use tock_registers::{ }; use crate::{ - arch::{x86_64::apic::IrqNumber, PLATFORM}, - device::{ - interrupt::InterruptSource, platform::Platform, timer::TimestampSource, Device, - InitializableDevice, - }, + arch::{x86_64::IrqNumber, Architecture, ARCHITECTURE}, mem::device::DeviceMemoryIo, proc::wait, sync::IrqSafeSpinlock, task::tasklet, - util::OneTimeInit, }; register_bitfields! { @@ -102,7 +102,7 @@ struct Inner { /// HPET timer group interface pub struct Hpet { - inner: OneTimeInit>, + inner: IrqSafeSpinlock, } impl Inner { @@ -135,17 +135,17 @@ impl Inner { } } -impl TimestampSource for Hpet { - fn timestamp(&self) -> Result { - let inner = self.inner.get().lock(); +impl MonotonicTimestampProviderDevice for Hpet { + fn monotonic_timestamp(&self) -> Result { + let inner = self.inner.lock(); Ok(Duration::from_millis(inner.tim0_counter)) } } -impl InterruptSource for Hpet { - fn handle_irq(&self) -> Result { +impl InterruptHandler for Hpet { + fn handle_irq(&self) -> bool { let now = { - let mut inner = self.inner.get().lock(); + let mut inner = self.inner.lock(); inner.regs.GeneralInterruptStatus.set(1); inner.tim0_counter = inner.tim0_counter.wrapping_add(1); @@ -155,7 +155,13 @@ impl InterruptSource for Hpet { wait::tick(now); tasklet::tick(now); - Ok(true) + true + } +} + +impl Device for Hpet { + fn display_name(&self) -> &'static str { + "HPET" } unsafe fn init_irq(&'static self) -> Result<(), Error> { @@ -163,8 +169,8 @@ impl InterruptSource for Hpet { const FS_IN_MS: u64 = 1000000000000; // Configure timer 0 - let intc = PLATFORM.interrupt_controller(); - let mut inner = self.inner.get().lock(); + let intc = ARCHITECTURE.external_interrupt_controller(); + let mut inner = self.inner.lock(); // Calculate the interrupt period let clk_period = inner @@ -206,9 +212,15 @@ impl InterruptSource for Hpet { let tim0_irq = tim0_irq.expect("Could not pick an IRQ for HPET TIM0"); // Bind and enable the IRQ - let irq = IrqNumber::Gsi(tim0_irq as _); - intc.register_handler(irq, self)?; - intc.configure_irq(irq, true, true)?; + let irq = IrqNumber::Gsi(tim0_irq); + intc.register_irq( + irq, + IrqOptions { + level: IrqLevel::ActiveLow, + trigger: IrqTrigger::Level, + }, + self, + )?; intc.enable_irq(irq)?; // Disable FSB interrupt route and 32 bit mode @@ -245,32 +257,16 @@ impl InterruptSource for Hpet { } } -impl InitializableDevice for Hpet { - type Options = AcpiHpet; - - unsafe fn init(&self, info: Self::Options) -> Result<(), Error> { +impl Hpet { + /// Creates a HPET instance from its ACPI definition + pub fn from_acpi(info: &AcpiHpet) -> Result { infoln!("Initializing HPET:"); infoln!("Address: {:#x}", info.base_address); let inner = unsafe { Inner::new(info.base_address) }?; - self.inner.init(IrqSafeSpinlock::new(inner)); - - Ok(()) - } -} - -impl Device for Hpet { - fn name(&self) -> &'static str { - "HPET" - } -} - -impl Hpet { - /// Constructs an uninitialized HPET instance - pub const fn uninit() -> Self { - Self { - inner: OneTimeInit::new(), - } + Ok(Self { + inner: IrqSafeSpinlock::new(inner), + }) } } diff --git a/src/arch/x86_64/peripherals/ps2/mod.rs b/src/arch/x86_64/peripherals/ps2/mod.rs index ec1b8820..0db12770 100644 --- a/src/arch/x86_64/peripherals/ps2/mod.rs +++ b/src/arch/x86_64/peripherals/ps2/mod.rs @@ -1,14 +1,15 @@ //! Intel 8042 PS/2 controller driver implemenation use abi::error::Error; +use device_api::{ + input::{KeyboardConsumer, KeyboardProducer}, + interrupt::InterruptHandler, + Device, +}; use crate::{ arch::{ - x86_64::{apic::IrqNumber, intrinsics::IoPort}, - PLATFORM, - }, - device::{ - input::KeyboardDevice, interrupt::InterruptSource, platform::Platform, tty::TtyDevice, - Device, + x86_64::{intrinsics::IoPort, IrqNumber}, + Architecture, ARCHITECTURE, }, sync::IrqSafeSpinlock, }; @@ -24,7 +25,7 @@ struct Inner { shift: bool, ctrl: bool, - tty: Option<&'static dyn TtyDevice<16>>, + consumer: Option<&'static dyn KeyboardConsumer>, // tty: Option<&'static dyn TtyDevice<16>>, } /// PS/2 controller driver @@ -80,37 +81,14 @@ impl Inner { } } -impl KeyboardDevice for PS2Controller { - fn attach(&self, terminal: &'static dyn TtyDevice<16>) { - self.inner.lock().tty.replace(terminal); +impl KeyboardProducer for PS2Controller { + fn connect(&self, to: &'static dyn KeyboardConsumer) { + self.inner.lock().consumer.replace(to); } } -impl InterruptSource for PS2Controller { - unsafe fn init_irq(&'static self) -> Result<(), Error> { - let mut inner = self.inner.lock(); - let intc = PLATFORM.interrupt_controller(); - - intc.register_handler(self.primary_irq, self)?; - - // Disable PS/2 devices from sending any further data - inner.send_command(0xAD); - inner.send_command(0xA7); - - // Flush the buffer - while inner.command.read() & Inner::STATUS_OUTPUT_FULL != 0 { - inner.data.read(); - } - - // Enable primary port - inner.send_command(0xAE); - - intc.enable_irq(self.primary_irq)?; - - Ok(()) - } - - fn handle_irq(&self) -> Result { +impl InterruptHandler for PS2Controller { + fn handle_irq(&self) -> bool { let mut count = 0; let mut inner = self.inner.lock(); loop { @@ -177,21 +155,49 @@ impl InterruptSource for PS2Controller { continue; } - if let Some(tty) = inner.tty { - tty.recv_byte(key); + if let Some(consumer) = inner.consumer { + consumer.handle_character(key).ok(); } count += 1; } - Ok(count != 0) + count != 0 } } impl Device for PS2Controller { - fn name(&self) -> &'static str { + fn display_name(&self) -> &'static str { "PS/2 Controller" } + + unsafe fn init(&'static self) -> Result<(), Error> { + Ok(()) + } + + unsafe fn init_irq(&'static self) -> Result<(), Error> { + let mut inner = self.inner.lock(); + // let intc = PLATFORM.interrupt_controller(); + let intc = ARCHITECTURE.external_interrupt_controller(); + + intc.register_irq(self.primary_irq, Default::default(), self)?; + + // Disable PS/2 devices from sending any further data + inner.send_command(0xAD); + inner.send_command(0xA7); + + // Flush the buffer + while inner.command.read() & Inner::STATUS_OUTPUT_FULL != 0 { + inner.data.read(); + } + + // Enable primary port + inner.send_command(0xAE); + + intc.enable_irq(self.primary_irq)?; + + Ok(()) + } } impl PS2Controller { @@ -207,7 +213,7 @@ impl PS2Controller { data: IoPort::new(data_port), shift: false, ctrl: false, - tty: None, + consumer: None, }; Self { diff --git a/src/arch/x86_64/peripherals/serial.rs b/src/arch/x86_64/peripherals/serial.rs index 8aa50788..c76c4802 100644 --- a/src/arch/x86_64/peripherals/serial.rs +++ b/src/arch/x86_64/peripherals/serial.rs @@ -1,9 +1,10 @@ +//! Driver for x86 COM ports use abi::error::Error; +use device_api::{serial::SerialDevice, Device}; use crate::{ - arch::x86_64::{apic::IrqNumber, intrinsics::IoPort}, + arch::x86_64::{intrinsics::IoPort, IrqNumber}, debug::DebugSink, - device::{serial::SerialDevice, Device, InitializableDevice}, sync::IrqSafeSpinlock, }; @@ -13,11 +14,13 @@ struct Inner { lsr: IoPort, } +/// Single port of the COM port pair pub struct Port { inner: IrqSafeSpinlock, } -// Port pair +/// COM port pair +#[allow(unused)] pub struct ComPort { port_a: Port, port_b: Port, @@ -41,22 +44,22 @@ impl SerialDevice for Port { inner.dr.write(byte); Ok(()) } - - fn receive(&self, blocking: bool) -> Result { - todo!() - } } impl Device for Port { - fn name(&self) -> &'static str { - "com-port" + fn display_name(&self) -> &'static str { + "COM port" + } + + unsafe fn init(&'static self) -> Result<(), Error> { + Ok(()) } } impl Port { const LSR_THRE: u8 = 1 << 5; - pub const fn new(base: u16) -> Self { + const fn new(base: u16) -> Self { Self { inner: IrqSafeSpinlock::new(Inner { dr: IoPort::new(base), @@ -66,16 +69,8 @@ impl Port { } } -impl InitializableDevice for ComPort { - type Options = (); - - unsafe fn init(&self, _opts: Self::Options) -> Result<(), Error> { - // TODO proper init - Ok(()) - } -} - impl ComPort { + /// Constructs a COM port pair pub const fn new(port_a: u16, port_b: u16, irq: IrqNumber) -> Self { Self { port_a: Port::new(port_a), @@ -84,6 +79,7 @@ impl ComPort { } } + /// Returns a reference to the A port of this COM pair pub fn port_a(&self) -> &Port { &self.port_a } diff --git a/src/debug.rs b/src/debug.rs index 4940ccc9..22ed097b 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -2,8 +2,9 @@ use core::fmt::{self, Arguments}; use abi::error::Error; +use kernel_util::util::StaticVector; -use crate::{sync::IrqSafeSpinlock, util::StaticVector}; +use crate::sync::IrqSafeSpinlock; const MAX_DEBUG_SINKS: usize = 4; diff --git a/src/device/bus/mod.rs b/src/device/bus/mod.rs index 00a9c9d6..42007f9c 100644 --- a/src/device/bus/mod.rs +++ b/src/device/bus/mod.rs @@ -1,3 +1,4 @@ //! Bus devices +#[cfg(feature = "device-tree")] pub mod simple_bus; diff --git a/src/device/bus/simple_bus.rs b/src/device/bus/simple_bus.rs index a5782d31..9fb1574c 100644 --- a/src/device/bus/simple_bus.rs +++ b/src/device/bus/simple_bus.rs @@ -1,6 +1,6 @@ //! Simple "passthrough" bus device -use crate::{arch::aarch64::devtree::DevTreeIndexNodeExt, device, device_tree_driver}; +use crate::{device::devtree, device_tree_driver}; device_tree_driver! { compatible: ["simple-bus"], @@ -11,8 +11,8 @@ device_tree_driver! { let nodes = dt.node.children(); // Iterate devices on the bus - device::enumerate_dt(address_cells, size_cells, nodes, |_, probe| { - if let Some((device, _)) = device::probe_dt_node(&probe) { + devtree::enumerate_dt(address_cells, size_cells, nodes, |_, probe| { + if let Some((device, _)) = devtree::probe_dt_node(&probe) { unsafe { device.init()?; } diff --git a/src/device/devtree.rs b/src/device/devtree.rs new file mode 100644 index 00000000..8882fb1e --- /dev/null +++ b/src/device/devtree.rs @@ -0,0 +1,454 @@ +//! ARM device tree utlities +use core::mem::size_of; + +use abi::error::Error; +use alloc::boxed::Box; +use device_api::{Device, DeviceId}; +use fdt_rs::{ + base::DevTree, + index::{iters::DevTreeIndexNodeSiblingIter, DevTreeIndex, DevTreeIndexNode, DevTreeIndexProp}, + prelude::PropReader, +}; + +use crate::{debug::LogLevel, mem::phys::PhysicalMemoryRegion}; + +use super::register_device; + +/// Helper macro to return the count of expressions supplied to it +#[macro_export] +macro_rules! count { + () => (0usize); + ($x:tt $($xs:tt)*) => (1usize + $crate::count!($($xs)*)); +} + +/// Registers a device driver for compatible device tree nodes +/// +/// # Usage example +/// +/// ``` +/// device_tree_driver! { +/// compatible: ["arm,pl011"], +/// probe(of) => { +/// let my_device = ...; // ... extract some info about the device ... +/// Some(Box::new(my_device)) +/// } +/// } +/// ``` +#[macro_export] +macro_rules! device_tree_driver { + ( + compatible: [$($compatible:literal),+], + probe ($node:ident) => $probe_body:block $(,)? + ) => { + const __COMPATIBLE_LEN: usize = $crate::count!($($compatible )+); + static __COMPATIBLE: [&str; __COMPATIBLE_LEN] = [$($compatible),+]; + + fn __probe($node: &$crate::device::devtree::DevTreeNodeInfo) -> + Option> $probe_body + + core::arch::global_asm!(r#" + .pushsection .dt_probes, "a" + .quad {compatible} + .quad {compatible_len} + .quad {probe_func} + .popsection + "#, + compatible = sym __COMPATIBLE, + compatible_len = const __COMPATIBLE_LEN, + probe_func = sym __probe + ); + }; +} + +const INDEX_BUFFER_SIZE: usize = 65536; + +#[repr(C, align(0x10))] +struct FdtIndexBuffer([u8; INDEX_BUFFER_SIZE]); + +static mut FDT_INDEX_BUFFER: FdtIndexBuffer = FdtIndexBuffer::zeroed(); + +impl FdtIndexBuffer { + const fn zeroed() -> Self { + Self([0; INDEX_BUFFER_SIZE]) + } +} + +/// Device tree node +pub type TNode<'a> = DevTreeIndexNode<'a, 'a, 'a>; +/// Device tree property +pub type TProp<'a> = DevTreeIndexProp<'a, 'a, 'a>; + +/// Helper trait to provide extra functionality for [DevTreeIndexProp] +pub trait DevTreeIndexPropExt { + /// Reads a cell value from single-type cell array at given cell index + fn cell1_array_item(&self, index: usize, cells: usize) -> Option; + /// Reads a cell pair from cell pair array at given pair index + fn cell2_array_item(&self, index: usize, cells0: usize, cells1: usize) -> Option<(u64, u64)>; + + /// Reads a cell value from the property at given offset + fn read_cell(&self, u32_offset: usize, cell_size: usize) -> Option; +} + +/// Helper trait to provide extra functionality for [DevTreeIndexNode] +pub trait DevTreeIndexNodeExt { + /// Returns the root node's `#address-cells` property, or the default value defined by the + /// specification if it's absent + fn address_cells(&self) -> usize { + self.get_address_cells().unwrap_or(2) + } + /// Returns the root node's `#size-cells` property, or the default value defined by the + /// specification if it's absent + fn size_cells(&self) -> usize { + self.get_size_cells().unwrap_or(1) + } + + /// Returns the #address-cells property of the node, if there is one + fn get_address_cells(&self) -> Option; + /// Returns the #size-cells property of the node, if there is one + fn get_size_cells(&self) -> Option; + + /// Prints the node contents to debug output + fn dump(&self, level: LogLevel, depth: usize); +} + +/// Extension trait for [DevTreeIndexNode] to obtain typed property values +pub trait DevTreeIndexNodePropGet { + /// Returns a property value of given type, if it exists + fn prop(&self, name: &str) -> Option; +} + +impl<'a, 'i, 'dt> DevTreeIndexNodePropGet for DevTreeIndexNode<'a, 'i, 'dt> { + fn prop(&self, name: &str) -> Option { + self.props().find_map(|prop| { + if prop.name().ok()? == name { + prop.u32(0).ok() + } else { + None + } + }) + } +} + +impl<'a, 'i, 'dt> DevTreeIndexNodePropGet<&'a str> for DevTreeIndexNode<'a, 'i, 'dt> { + fn prop(&self, name: &str) -> Option<&'a str> { + self.props().find_map(|prop| { + if prop.name().ok()? == name { + prop.str().ok() + } else { + None + } + }) + } +} + +/// Iterator for physical memory regions present in the device tree +#[derive(Clone)] +pub struct FdtMemoryRegionIter<'a> { + inner: DevTreeIndexNodeSiblingIter<'a, 'a, 'a>, + address_cells: usize, + size_cells: usize, +} + +/// Device tree wrapper struct +pub struct DeviceTree<'a> { + tree: DevTree<'a>, + index: DevTreeIndex<'a, 'a>, +} + +impl<'a> DeviceTree<'a> { + /// Constructs a device tree wrapper from the DTB virtual address. + /// + /// # Safety + /// + /// The caller must ensure the validity of the address. + pub unsafe fn from_addr(virt: usize) -> Self { + let tree = DevTree::from_raw_pointer(virt as _).unwrap(); + let index = DevTreeIndex::new(tree, &mut FDT_INDEX_BUFFER.0).unwrap(); + Self { tree, index } + } + + /// Looks up a node for a given path + pub fn node_by_path(&self, path: &str) -> Option { + find_node(self.index.root(), path.trim_start_matches('/')) + } + + /// Prints the device tree to log output + pub fn dump(&self, level: LogLevel) { + self.index.root().dump(level, 0) + } + + /// Returns the total size of the device tree in memory + pub fn size(&self) -> usize { + self.tree.totalsize() + } + + /// Returns the root node's `#address-cells` property, or the default value defined by the + /// specification if it's absent + pub fn address_cells(&self) -> usize { + self.index.root().address_cells() + } + + /// Returns the root node's `#size-cells` property, or the default value defined by the + /// specification if it's absent + pub fn size_cells(&self) -> usize { + self.index.root().size_cells() + } + + /// Returns the root node of the device tree + pub fn root(&self) -> DevTreeIndexNode { + self.index.root() + } +} + +impl<'a, 'i, 'dt> DevTreeIndexNodeExt for DevTreeIndexNode<'a, 'i, 'dt> { + fn get_address_cells(&self) -> Option { + self.props() + .find(|p| p.name().unwrap_or("") == "#address-cells") + .map(|p| p.u32(0).unwrap() as usize) + } + + fn get_size_cells(&self) -> Option { + self.props() + .find(|p| p.name().unwrap_or("") == "#size-cells") + .map(|p| p.u32(0).unwrap() as usize) + } + + fn dump(&self, level: LogLevel, depth: usize) { + fn indent(level: LogLevel, depth: usize) { + for _ in 0..depth { + log_print_raw!(level, " "); + } + } + + let node_name = self.name().unwrap(); + + // Don't dump these + if node_name.starts_with("virtio_mmio@") { + return; + } + + indent(level, depth); + log_print_raw!(level, "{:?} {{\n", node_name); + for prop in self.props() { + indent(level, depth + 1); + let name = prop.name().unwrap_or(""); + log_print_raw!(level, "{name:?} = "); + + match name { + "compatible" | "stdout-path" => { + log_print_raw!(level, "{:?}", prop.str().unwrap_or("")) + } + _ => log_print_raw!(level, "{:x?}", prop.raw()), + } + + log_print_raw!(level, "\n"); + } + + for child in self.children() { + child.dump(level, depth + 1); + } + + indent(level, depth); + log_print_raw!(level, "}}\n"); + } +} + +impl<'a, 'i, 'dt> DevTreeIndexPropExt for DevTreeIndexProp<'a, 'i, 'dt> { + fn read_cell(&self, u32_offset: usize, cell_size: usize) -> Option { + match cell_size { + 1 => self.u32(u32_offset).map(|x| x as u64).ok(), + 2 => { + let high = self.u32(u32_offset).ok()? as u64; + let low = self.u32(u32_offset + 1).ok()? as u64; + + Some((high << 32) | low) + } + _ => unimplemented!(), + } + } + + fn cell1_array_item(&self, index: usize, cells: usize) -> Option { + self.read_cell(index * cells, cells) + } + + fn cell2_array_item(&self, index: usize, cells0: usize, cells1: usize) -> Option<(u64, u64)> { + let u32_index = index * (cells0 + cells1); + let cell0 = self.read_cell(u32_index, cells0)?; + let cell1 = self.read_cell(u32_index + cells0, cells1)?; + Some((cell0, cell1)) + } +} + +impl<'a> FdtMemoryRegionIter<'a> { + /// Constructs a memory region iterator for given device tree + pub fn new(dt: &'a DeviceTree) -> Self { + let inner = dt.index.root().children(); + let address_cells = dt.address_cells(); + let size_cells = dt.size_cells(); + Self { + inner, + address_cells, + size_cells, + } + } +} + +impl Iterator for FdtMemoryRegionIter<'_> { + type Item = PhysicalMemoryRegion; + + fn next(&mut self) -> Option { + loop { + let Some(item) = self.inner.next() else { + break None; + }; + + let name = item.name().unwrap_or(""); + + if name.starts_with("memory@") || name == "memory" { + let reg = item + .props() + .find(|p| p.name().unwrap_or("") == "reg") + .unwrap(); + + let (base, size) = reg + .cell2_array_item(0, self.address_cells, self.size_cells) + .unwrap(); + + break Some(PhysicalMemoryRegion { + base: base as usize, + size: size as usize, + }); + } + } + } +} + +/// Looks up a property with given name in the node +pub fn find_prop<'a>(node: &TNode<'a>, name: &str) -> Option> { + node.props().find(|p| p.name().unwrap_or("") == name) +} + +fn path_component_left(path: &str) -> (&str, &str) { + if let Some((left, right)) = path.split_once('/') { + (left, right.trim_start_matches('/')) + } else { + (path, "") + } +} + +fn find_node<'a>(at: TNode<'a>, path: &str) -> Option> { + let (item, path) = path_component_left(path); + if item.is_empty() { + assert_eq!(path, ""); + Some(at) + } else { + let child = at.children().find(|c| c.name().unwrap() == item)?; + if path.is_empty() { + Some(child) + } else { + find_node(child, path) + } + } +} + +struct DevTreeProbe<'a> { + compatible: &'static [&'static str], + probe_func: fn(&'a DevTreeNodeInfo<'a, 'a, 'a>) -> Option>, +} + +/// Provides information about a device tree node to device driver's "probe" function +pub struct DevTreeNodeInfo<'a, 'i, 'dt> { + /// #address-cells property of the parent bus/system + pub address_cells: usize, + /// #size-cells property of the parent bus/system + pub size_cells: usize, + /// Device tree node being probed + pub node: DevTreeIndexNode<'a, 'i, 'dt>, +} + +fn iter_dt_probes<'a>() -> impl Iterator> { + extern "C" { + static __dt_probes_start: u64; + static __dt_probes_end: u64; + } + + unsafe { + let base = &__dt_probes_start as *const u64; + let end = &__dt_probes_end as *const u64; + let len = (end as usize - base as usize) / (size_of::() * 3); + + (0..len).map(move |i| { + let compatible_ptr = *base.add(i * 3); + let compatible_len = *base.add(i * 3 + 1); + let probe_func_ptr = *base.add(i * 3 + 2); + + let compatible = + core::slice::from_raw_parts(compatible_ptr as *const &str, compatible_len as usize); + let probe_func = core::mem::transmute(probe_func_ptr); + + DevTreeProbe { + compatible, + probe_func, + } + }) + } +} + +fn dt_match_compatible(compatible: &str) -> Option { + iter_dt_probes().find(|probe| probe.compatible.contains(&compatible)) +} + +/// "Probes" a device tree node for any matching device, registering it if a compatible driver is +/// found +pub fn probe_dt_node(dt: &DevTreeNodeInfo) -> Option<(&'static dyn Device, DeviceId)> { + // TODO use list, not just the first item + let Some(compatible) = dt.node.prop("compatible") else { + return None; + }; + + let probe = dt_match_compatible(compatible)?; + let device = Box::leak((probe.probe_func)(dt)?); + let id = register_device(device); + Some((device, id)) +} + +/// Performs shallow walk of a device tree node and executes the visitor function on each node +pub fn enumerate_dt< + 'a, + I: Iterator>, + F: Fn(&str, DevTreeNodeInfo) -> Result<(), Error>, +>( + address_cells: usize, + size_cells: usize, + nodes: I, + f: F, +) -> Result<(), usize> { + let mut failed_count = 0; + + for node in nodes { + // Skip /cpus and /memory* + let probe = DevTreeNodeInfo { + address_cells, + size_cells, + node, + }; + + let Ok(name) = probe.node.name() else { + continue; + }; + let Some(compatible) = probe.node.prop("compatible") else { + continue; + }; + + if let Err(error) = f(compatible, probe) { + warnln!("{}: {:?}", name, error); + failed_count += 1; + } + } + + if failed_count == 0 { + Ok(()) + } else { + Err(failed_count) + } +} diff --git a/src/device/display/console.rs b/src/device/display/console.rs index 1554c4ba..6ed38b46 100644 --- a/src/device/display/console.rs +++ b/src/device/display/console.rs @@ -4,6 +4,7 @@ use core::mem::size_of; use abi::{error::Error, primitive_enum}; use alloc::vec::Vec; use bitflags::bitflags; +use kernel_util::util::StaticVector; use crate::{ debug::DebugSink, @@ -13,7 +14,6 @@ use crate::{ }, sync::IrqSafeSpinlock, task::tasklet::TaskFlow, - util::StaticVector, }; const CONSOLE_ROW_LEN: usize = 128; @@ -23,36 +23,31 @@ const DEFAULT_FG_COLOR: ColorAttribute = ColorAttribute::White; const DEFAULT_BG_COLOR: ColorAttribute = ColorAttribute::Blue; primitive_enum! { + #[allow(missing_docs)] #[doc = "Color attribute of a console character"] pub enum ColorAttribute: u8 { - #[doc = "..."] Black = 0, - #[doc = "..."] Red = 1, - #[doc = "..."] Green = 2, - #[doc = "..."] Yellow = 3, - #[doc = "..."] Blue = 4, - #[doc = "..."] Magenta = 5, - #[doc = "..."] Cyan = 6, - #[doc = "..."] White = 7, } } bitflags! { + #[doc = "Extra attributes of a console character"] #[derive(Clone, Copy)] pub struct Attributes: u8 { + #[allow(missing_docs)] const BOLD = 1 << 0; } } impl ColorAttribute { - pub fn from_vt100(val: u8) -> Self { + fn from_vt100(val: u8) -> Self { match val { 0..=7 => Self::try_from(val).unwrap(), _ => ColorAttribute::Red, @@ -102,7 +97,7 @@ pub struct ConsoleBuffer { height: u32, } -pub enum EscapeState { +enum EscapeState { Normal, Escape, Csi, @@ -116,7 +111,9 @@ pub struct ConsoleState { pub cursor_col: u32, /// Current foreground color pub fg_color: ColorAttribute, + /// Current background color pub bg_color: ColorAttribute, + /// Current set of attributes pub attributes: Attributes, esc_args: StaticVector, @@ -177,6 +174,7 @@ impl ConsoleChar { } } + /// Returns the attributes of the character #[inline] pub fn attributes(self) -> (ColorAttribute, ColorAttribute, Attributes) { let fg = @@ -256,8 +254,20 @@ impl ConsoleRow { impl ConsoleBuffer { /// Constructs a fixed-size console buffer - pub const fn fixed(rows: &'static mut [ConsoleRow], height: u32) -> Self { - Self { rows, height } + pub fn new(height: u32) -> Result { + let size = size_of::() * (height as usize); + let page_count = (size + 0xFFF) / 0x1000; + let pages = phys::alloc_pages_contiguous(page_count, PageUsage::Used)?; + + let rows = unsafe { + core::slice::from_raw_parts_mut(pages.virtualize() as *mut ConsoleRow, height as usize) + }; + + for row in rows.iter_mut() { + row.clear(DEFAULT_BG_COLOR); + } + + Ok(Self { rows, height }) } /// Reallocates the internal buffer with a new size @@ -394,12 +404,12 @@ impl ConsoleState { // Move back one character b'D' => { if self.cursor_col > 0 { - self.cursor_col = self.cursor_col - 1; + self.cursor_col -= 1; } } // Manipulate display attributes b'm' => { - if let Some(arg) = self.esc_args.get(0) { + if let Some(arg) = self.esc_args.first() { match arg { // Reset 0 => { diff --git a/src/device/display/fb_console.rs b/src/device/display/fb_console.rs index a93e8f9d..1a675c52 100644 --- a/src/device/display/fb_console.rs +++ b/src/device/display/fb_console.rs @@ -112,11 +112,12 @@ impl FramebufferConsole { /// Constructs an instance of console from its framebuffer reference pub fn from_framebuffer( framebuffer: &'static LinearFramebuffer, - font: &'static BitmapFont<'static>, + font: Option<&'static BitmapFont<'static>>, buffer: ConsoleBuffer, ) -> Self { - let char_width = 6; - let char_height = 12; + let font = font.unwrap_or(&bitmap_font::tamzen::FONT_7x14); + let char_width = font.width(); + let char_height = font.height(); let dim = framebuffer.dimensions(); let inner = Inner { @@ -151,12 +152,6 @@ impl Inner { text.draw(self).ok(); } - fn fill_row(&mut self, y: u32, h: u32, val: u32) { - let mut fb = unsafe { self.framebuffer.lock() }; - - fb.fill_rows(y, h, val); - } - fn fill_rect(&mut self, x: u32, y: u32, w: u32, h: u32, val: u32) { let mut fb = unsafe { self.framebuffer.lock() }; diff --git a/src/device/display/linear_fb.rs b/src/device/display/linear_fb.rs index 1df8eb75..5749643a 100644 --- a/src/device/display/linear_fb.rs +++ b/src/device/display/linear_fb.rs @@ -3,8 +3,9 @@ use core::ops::{Index, IndexMut}; use abi::error::Error; +use device_api::Device; -use crate::{device::Device, mem::device::DeviceMemory, sync::IrqSafeSpinlock}; +use crate::{mem::device::DeviceMemory, sync::IrqSafeSpinlock}; use super::{DisplayDevice, DisplayDimensions}; @@ -76,7 +77,7 @@ impl LinearFramebuffer { } impl Device for LinearFramebuffer { - fn name(&self) -> &'static str { + fn display_name(&self) -> &'static str { "Linear Framebuffer" } } diff --git a/src/device/display/mod.rs b/src/device/display/mod.rs index 9d6a6fea..e503e3ae 100644 --- a/src/device/display/mod.rs +++ b/src/device/display/mod.rs @@ -3,6 +3,8 @@ use super::Device; pub mod console; + +#[cfg(feature = "fb_console")] pub mod fb_console; pub mod linear_fb; diff --git a/src/device/mod.rs b/src/device/mod.rs index d7722ae7..2baf9495 100644 --- a/src/device/mod.rs +++ b/src/device/mod.rs @@ -1,178 +1,20 @@ //! Device management and interfaces -use core::mem::size_of; - -use abi::error::Error; -use alloc::boxed::Box; use device_api::{manager::DeviceManager, Device, DeviceId}; -use fdt_rs::{index::DevTreeIndexNode, prelude::PropReader}; -use crate::{ - arch::aarch64::devtree, - sync::{IrqSafeSpinlock, IrqSafeSpinlockGuard}, -}; +use crate::sync::{IrqSafeSpinlock, IrqSafeSpinlockGuard}; + +#[cfg(target_arch = "aarch64")] +pub mod devtree; pub mod bus; +pub mod display; pub mod power; pub mod serial; pub mod tty; -// pub mod display; - static DEVICE_MANAGER: IrqSafeSpinlock = IrqSafeSpinlock::new(DeviceManager::new()); -/// Helper macro to return the count of expressions supplied to it -#[macro_export] -macro_rules! count { - () => (0usize); - ($x:tt $($xs:tt)*) => (1usize + $crate::count!($($xs)*)); -} - -/// Registers a device driver for compatible device tree nodes -/// -/// # Usage example -/// -/// ``` -/// device_tree_driver! { -/// compatible: ["arm,pl011"], -/// probe(of) => { -/// let my_device = ...; // ... extract some info about the device ... -/// Some(Box::new(my_device)) -/// } -/// } -/// ``` -#[macro_export] -macro_rules! device_tree_driver { - ( - compatible: [$($compatible:literal),+], - probe ($node:ident) => $probe_body:block $(,)? - ) => { - const __COMPATIBLE_LEN: usize = $crate::count!($($compatible )+); - static __COMPATIBLE: [&str; __COMPATIBLE_LEN] = [$($compatible),+]; - - fn __probe($node: &$crate::device::DevTreeNodeInfo) -> - Option> $probe_body - - core::arch::global_asm!(r#" - .pushsection .dt_probes, "a" - .quad {compatible} - .quad {compatible_len} - .quad {probe_func} - .popsection - "#, - compatible = sym __COMPATIBLE, - compatible_len = const __COMPATIBLE_LEN, - probe_func = sym __probe - ); - }; -} - -struct DevTreeProbe<'a> { - compatible: &'static [&'static str], - probe_func: fn(&'a DevTreeNodeInfo<'a, 'a, 'a>) -> Option>, -} - -/// Provides information about a device tree node to device driver's "probe" function -pub struct DevTreeNodeInfo<'a, 'i, 'dt> { - /// #address-cells property of the parent bus/system - pub address_cells: usize, - /// #size-cells property of the parent bus/system - pub size_cells: usize, - /// Device tree node being probed - pub node: DevTreeIndexNode<'a, 'i, 'dt>, -} - -fn iter_dt_probes<'a>() -> impl Iterator> { - extern "C" { - static __dt_probes_start: u64; - static __dt_probes_end: u64; - } - - unsafe { - let base = &__dt_probes_start as *const u64; - let end = &__dt_probes_end as *const u64; - let len = (end as usize - base as usize) / (size_of::() * 3); - - (0..len).map(move |i| { - let compatible_ptr = *base.add(i * 3); - let compatible_len = *base.add(i * 3 + 1); - let probe_func_ptr = *base.add(i * 3 + 2); - - let compatible = - core::slice::from_raw_parts(compatible_ptr as *const &str, compatible_len as usize); - let probe_func = core::mem::transmute(probe_func_ptr); - - DevTreeProbe { - compatible, - probe_func, - } - }) - } -} - -fn dt_match_compatible(compatible: &str) -> Option { - iter_dt_probes().find(|probe| probe.compatible.contains(&compatible)) -} - -/// "Probes" a device tree node for any matching device, registering it if a compatible driver is -/// found -pub fn probe_dt_node(dt: &DevTreeNodeInfo) -> Option<(&'static dyn Device, DeviceId)> { - // TODO use list, not just the first item - let Some(compatible) = - devtree::find_prop(&dt.node, "compatible").and_then(|prop| prop.str().ok()) - else { - return None; - }; - - let probe = dt_match_compatible(compatible)?; - let device = Box::leak((probe.probe_func)(dt)?); - let id = register_device(device); - Some((device, id)) -} - -/// Performs shallow walk of a device tree node and executes the visitor function on each node -pub fn enumerate_dt< - 'a, - I: Iterator>, - F: Fn(&str, DevTreeNodeInfo) -> Result<(), Error>, ->( - address_cells: usize, - size_cells: usize, - nodes: I, - f: F, -) -> Result<(), usize> { - let mut failed_count = 0; - - for node in nodes { - // Skip /cpus and /memory* - let probe = DevTreeNodeInfo { - address_cells, - size_cells, - node, - }; - - let Ok(name) = probe.node.name() else { - continue; - }; - let Some(compatible) = - devtree::find_prop(&probe.node, "compatible").and_then(|prop| prop.str().ok()) - else { - continue; - }; - - if let Err(error) = f(compatible, probe) { - warnln!("{}: {:?}", name, error); - failed_count += 1; - } - } - - if failed_count == 0 { - Ok(()) - } else { - Err(failed_count) - } -} - /// Adds a device to the kernel's device table and returns the ID assigned to it pub fn register_device(device: &'static dyn Device) -> DeviceId { debugln!("Register {:?}", device.display_name()); diff --git a/src/device/power/arm_psci.rs b/src/device/power/arm_psci.rs index d7397796..4dce90a3 100644 --- a/src/device/power/arm_psci.rs +++ b/src/device/power/arm_psci.rs @@ -6,10 +6,8 @@ use device_api::{CpuBringupDevice, Device, ResetDevice}; use fdt_rs::prelude::PropReader; use crate::{ - arch::{ - aarch64::devtree::{self}, - Architecture, ArchitectureImpl, ARCHITECTURE, - }, + arch::{Architecture, ArchitectureImpl, ARCHITECTURE}, + device::devtree, device_tree_driver, }; diff --git a/src/device/power/mod.rs b/src/device/power/mod.rs index 8b87a8c6..7cf0ce21 100644 --- a/src/device/power/mod.rs +++ b/src/device/power/mod.rs @@ -1,4 +1,10 @@ //! Power-management related device drivers -pub mod arm_psci; -pub mod sunxi_rwdog; +use cfg_if::cfg_if; + +cfg_if! { + if #[cfg(target_arch = "aarch64")] { + pub mod arm_psci; + pub mod sunxi_rwdog; + } +} diff --git a/src/device/power/sunxi_rwdog.rs b/src/device/power/sunxi_rwdog.rs index 7d47e3de..917adf60 100644 --- a/src/device/power/sunxi_rwdog.rs +++ b/src/device/power/sunxi_rwdog.rs @@ -3,19 +3,17 @@ use abi::error::Error; use alloc::boxed::Box; use device_api::{Device, ResetDevice}; +use kernel_util::util::OneTimeInit; use tock_registers::{ interfaces::Writeable, register_bitfields, register_structs, registers::ReadWrite, }; use crate::{ - arch::{ - aarch64::devtree::{self, DevTreeIndexNodePropGet, DevTreeIndexPropExt}, - Architecture, ARCHITECTURE, - }, + arch::{Architecture, ARCHITECTURE}, + device::devtree::{self, DevTreeIndexNodePropGet, DevTreeIndexPropExt}, device_tree_driver, mem::device::DeviceMemoryIo, sync::IrqSafeSpinlock, - util::OneTimeInit, }; register_bitfields! { diff --git a/src/device/serial/mod.rs b/src/device/serial/mod.rs index 757e4894..7f672fe6 100644 --- a/src/device/serial/mod.rs +++ b/src/device/serial/mod.rs @@ -1,4 +1,10 @@ //! Serial device interfaces -pub mod pl011; -pub mod sunxi_uart; +use cfg_if::cfg_if; + +cfg_if! { + if #[cfg(target_arch = "aarch64")] { + pub mod pl011; + pub mod sunxi_uart; + } +} diff --git a/src/device/serial/pl011.rs b/src/device/serial/pl011.rs index 987eb1e2..c8d90f93 100644 --- a/src/device/serial/pl011.rs +++ b/src/device/serial/pl011.rs @@ -1,11 +1,8 @@ //! ARM PL011 driver use abi::{error::Error, io::DeviceRequest}; use alloc::boxed::Box; -use device_api::{ - interrupt::{InterruptHandler, IrqNumber}, - serial::SerialDevice, - Device, -}; +use device_api::{interrupt::InterruptHandler, serial::SerialDevice, Device}; +use kernel_util::util::OneTimeInit; use tock_registers::{ interfaces::{ReadWriteable, Readable, Writeable}, register_bitfields, register_structs, @@ -14,17 +11,16 @@ use tock_registers::{ use vfs::CharDevice; use crate::{ - arch::{ - aarch64::devtree::{self, DevTreeIndexPropExt}, - Architecture, ARCHITECTURE, - }, + arch::{aarch64::IrqNumber, Architecture, ARCHITECTURE}, debug::{self, DebugSink, LogLevel}, - device::tty::{CharRing, TtyDevice}, + device::{ + devtree::{self, DevTreeIndexPropExt}, + tty::{CharRing, TtyDevice}, + }, device_tree_driver, fs::devfs::{self, CharDeviceType}, mem::device::DeviceMemoryIo, sync::IrqSafeSpinlock, - util::OneTimeInit, }; register_bitfields! { diff --git a/src/device/serial/sunxi_uart.rs b/src/device/serial/sunxi_uart.rs index d1d24862..ca0a594f 100644 --- a/src/device/serial/sunxi_uart.rs +++ b/src/device/serial/sunxi_uart.rs @@ -2,11 +2,8 @@ use abi::{error::Error, io::DeviceRequest}; use alloc::boxed::Box; -use device_api::{ - interrupt::{InterruptHandler, IrqNumber}, - serial::SerialDevice, - Device, -}; +use device_api::{interrupt::InterruptHandler, serial::SerialDevice, Device}; +use kernel_util::util::OneTimeInit; use tock_registers::{ interfaces::{ReadWriteable, Readable, Writeable}, register_bitfields, register_structs, @@ -15,17 +12,16 @@ use tock_registers::{ use vfs::CharDevice; use crate::{ - arch::{ - aarch64::devtree::{self, DevTreeIndexPropExt}, - Architecture, ARCHITECTURE, - }, + arch::{aarch64::IrqNumber, Architecture, ARCHITECTURE}, debug::{self, DebugSink, LogLevel}, - device::tty::{CharRing, TtyDevice}, + device::{ + devtree::{self, DevTreeIndexPropExt}, + tty::{CharRing, TtyDevice}, + }, device_tree_driver, fs::devfs::{self, CharDeviceType}, mem::device::DeviceMemoryIo, sync::IrqSafeSpinlock, - util::OneTimeInit, }; register_bitfields! { diff --git a/src/device/timer.rs b/src/device/timer.rs deleted file mode 100644 index bba34bd8..00000000 --- a/src/device/timer.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! Time-providing device interfaces -use core::time::Duration; - -use abi::error::Error; - -use super::Device; - -/// Interface for devices capable of providing some notion of time -pub trait TimestampSource: Device { - /// Returns current time signalled by the device. The time may not be a "real" time and instead - /// is assumed to be monotonically increasing. - fn timestamp(&self) -> Result; -} diff --git a/src/device/tty.rs b/src/device/tty.rs index 44673f98..45b30bee 100644 --- a/src/device/tty.rs +++ b/src/device/tty.rs @@ -14,13 +14,13 @@ use crate::{ #[cfg(feature = "fb_console")] pub mod combined { + //! Combined console + keyboard terminal device use abi::{error::Error, io::DeviceRequest}; + use device_api::{input::KeyboardConsumer, serial::SerialDevice}; use vfs::CharDevice; use crate::device::{ display::{console::DisplayConsole, fb_console::FramebufferConsole}, - input::KeyboardDevice, - serial::SerialDevice, Device, }; @@ -29,21 +29,15 @@ pub mod combined { // TODO rewrite this /// Helper device to combine a display and a keyboard input into a single terminal pub struct CombinedTerminal { - #[allow(dead_code)] - input: &'static dyn KeyboardDevice, - output: &'static dyn DisplayConsole, + output: &'static (dyn DisplayConsole + Sync + 'static), input_ring: CharRing<16>, } impl CombinedTerminal { /// Create a combined terminal device from a keyboard and console output devices - pub fn new( - input: &'static dyn KeyboardDevice, - output: &'static FramebufferConsole, - ) -> Self { + pub fn new(output: &'static FramebufferConsole) -> Self { Self { - input, output, input_ring: CharRing::new(), } @@ -56,11 +50,14 @@ pub mod combined { } } - impl SerialDevice for CombinedTerminal { - fn receive(&self, _blocking: bool) -> Result { - todo!() + impl KeyboardConsumer for CombinedTerminal { + fn handle_character(&self, data: u8) -> Result<(), Error> { + self.recv_byte(data); + Ok(()) } + } + impl SerialDevice for CombinedTerminal { fn send(&self, byte: u8) -> Result<(), Error> { self.output.write_char(byte); Ok(()) @@ -68,7 +65,7 @@ pub mod combined { } impl Device for CombinedTerminal { - fn name(&self) -> &'static str { + fn display_name(&self) -> &'static str { "Combined terminal device" } } diff --git a/src/fs/devfs.rs b/src/fs/devfs.rs index a62c20c6..30441c39 100644 --- a/src/fs/devfs.rs +++ b/src/fs/devfs.rs @@ -6,12 +6,11 @@ use abi::{ io::{FileAttr, FileMode, FileType, OpenOptions}, }; use alloc::{boxed::Box, format, string::String}; +use kernel_util::util::OneTimeInit; use vfs::{ CharDevice, CharDeviceWrapper, Vnode, VnodeImpl, VnodeKind, VnodeRef, DIR_POSITION_FROM_CACHE, }; -use crate::util::OneTimeInit; - /// Describes the kind of a character device #[derive(Debug)] pub enum CharDeviceType { @@ -24,16 +23,11 @@ pub enum CharDeviceType { struct DevfsDirectory; impl VnodeImpl for DevfsDirectory { - fn open( - &mut self, - _node: &VnodeRef, - _opts: OpenOptions, - _mode: FileMode, - ) -> Result { + fn open(&self, _node: &VnodeRef, _opts: OpenOptions, _mode: FileMode) -> Result { Ok(DIR_POSITION_FROM_CACHE) } - fn metadata(&mut self, _node: &VnodeRef) -> Result { + fn metadata(&self, _node: &VnodeRef) -> Result { Ok(FileAttr { size: 0, mode: FileMode::default_dir(), diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 513231a2..0f79554c 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -2,17 +2,15 @@ use core::ptr::NonNull; +use kernel_util::util::OneTimeInit; use memfs::block::{self, BlockAllocator}; use vfs::VnodeRef; use yggdrasil_abi::{error::Error, io::MountOptions}; -use crate::{ - mem::{ - self, - phys::{self, PageUsage}, - ConvertAddress, - }, - util::OneTimeInit, +use crate::mem::{ + self, + phys::{self, PageUsage}, + ConvertAddress, }; pub mod devfs; diff --git a/src/init.rs b/src/init.rs index b61f7456..12b21c95 100644 --- a/src/init.rs +++ b/src/init.rs @@ -30,8 +30,8 @@ pub fn kinit() { { use core::time::Duration; - use device::display::console::task_update_consoles; - use task::tasklet; + use crate::device::display::console::task_update_consoles; + use crate::task::tasklet; tasklet::add_periodic( "update-console", diff --git a/src/main.rs b/src/main.rs index 6e7511fb..66d6a577 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,8 @@ arbitrary_self_types, const_mut_refs, let_chains, - linked_list_cursors + linked_list_cursors, + rustc_private )] #![allow(clippy::new_without_default, clippy::fn_to_numeric_cast)] #![warn(missing_docs)] @@ -17,7 +18,7 @@ #![no_main] use sync::SpinFence; -use task::{context::Cpu, spawn_kernel_closure}; +use task::spawn_kernel_closure; use crate::{ arch::{Architecture, ArchitectureImpl, ARCHITECTURE}, @@ -27,6 +28,7 @@ use crate::{ extern crate yggdrasil_abi as abi; extern crate alloc; +extern crate compiler_builtins; #[macro_use] pub mod debug; @@ -42,7 +44,6 @@ pub mod proc; pub mod sync; pub mod syscall; pub mod task; -pub mod util; static CPU_INIT_FENCE: SpinFence = SpinFence::new(); @@ -75,7 +76,7 @@ pub fn kernel_main() -> ! { ARCHITECTURE.start_application_processors(); } - Cpu::init_ipi_queues(); + // Cpu::init_ipi_queues(); // Wait until all APs initialize CPU_INIT_FENCE.signal(); diff --git a/src/mem/mod.rs b/src/mem/mod.rs index 83e941c9..9eda78af 100644 --- a/src/mem/mod.rs +++ b/src/mem/mod.rs @@ -286,70 +286,20 @@ pub fn validate_user_region( #[no_mangle] unsafe extern "C" fn memcpy(p0: *mut c_void, p1: *const c_void, len: usize) -> *mut c_void { - let mut offset = 0; - while offset < len { - let c = (p1 as *const u8).add(offset).read_volatile(); - (p0 as *mut u8).add(offset).write_volatile(c); - - offset += 1; - } - p0 + compiler_builtins::mem::memcpy(p0 as _, p1 as _, len) as _ } #[no_mangle] unsafe extern "C" fn memcmp(p0: *const c_void, p1: *const c_void, len: usize) -> i32 { - let mut offset = 0; - - if len == 0 { - return 0; - } - - while offset < len { - let c0 = (p0 as *const u8).add(offset).read_volatile(); - let c1 = (p1 as *const u8).add(offset).read_volatile(); - - #[allow(clippy::comparison_chain)] - if c0 > c1 { - return (c0 - c1) as i32; - } else if c0 < c1 { - return -((c1 - c0) as i32); - } - - offset += 1; - } - - 0 + compiler_builtins::mem::memcmp(p0 as _, p1 as _, len) } #[no_mangle] unsafe extern "C" fn memmove(dst: *mut c_void, src: *const c_void, len: usize) -> *mut c_void { - if dst as usize == src as usize { - return dst; - } - - if (src.add(len) as usize) <= (dst as usize) || (dst.add(len) as usize) <= (src as usize) { - return memcpy(dst, src, len); - } - - if (dst as usize) < (src as usize) { - let a = src as usize - dst as usize; - memcpy(dst, src, a); - memcpy(src as *mut _, src.add(a), len - a); - } else { - let a = dst as usize - src as usize; - memcpy(dst.add(a), dst, len - a); - memcpy(dst, src, a); - } - - dst + compiler_builtins::mem::memmove(dst as _, src as _, len) as _ } #[no_mangle] unsafe extern "C" fn memset(dst: *mut c_void, val: i32, len: usize) -> *mut c_void { - let mut offset = 0; - while offset < len { - (dst as *mut u8).add(offset).write_volatile(val as u8); - offset += 1; - } - dst + compiler_builtins::mem::memset(dst as _, val, len) as _ } diff --git a/src/mem/phys/mod.rs b/src/mem/phys/mod.rs index 06b8b77d..54ce445c 100644 --- a/src/mem/phys/mod.rs +++ b/src/mem/phys/mod.rs @@ -2,6 +2,7 @@ use core::{iter::StepBy, mem::size_of, ops::Range}; use abi::error::Error; +use kernel_util::util::OneTimeInit; use crate::{ debug::LogLevel, @@ -10,7 +11,6 @@ use crate::{ ConvertAddress, /*, KERNEL_PHYS_BASE */ }, sync::IrqSafeSpinlock, - util::OneTimeInit, }; use self::manager::PhysicalMemoryManager; diff --git a/src/mem/phys/reserved.rs b/src/mem/phys/reserved.rs index 8110ac90..697c77d5 100644 --- a/src/mem/phys/reserved.rs +++ b/src/mem/phys/reserved.rs @@ -1,6 +1,6 @@ //! Utilities for handling reserved memory regions -use crate::util::StaticVector; +use kernel_util::util::StaticVector; use super::PhysicalMemoryRegion; diff --git a/src/task/process.rs b/src/task/process.rs index 911cf8b8..b392e981 100644 --- a/src/task/process.rs +++ b/src/task/process.rs @@ -11,6 +11,7 @@ use abi::{ }; use alloc::{collections::VecDeque, rc::Rc, string::String}; use atomic_enum::atomic_enum; +use kernel_util::util::OneTimeInit; use vfs::VnodeRef; use crate::{ @@ -22,7 +23,6 @@ use crate::{ }, sync::{IrqGuard, IrqSafeSpinlock}, task::context::TaskContextImpl, - util::OneTimeInit, }; use super::{context::TaskFrame, sched::CpuQueue, Cpu, ProcessId, TaskContext, PROCESSES}; diff --git a/src/task/sched.rs b/src/task/sched.rs index f8040c31..b7df1b6d 100644 --- a/src/task/sched.rs +++ b/src/task/sched.rs @@ -3,12 +3,12 @@ // use aarch64_cpu::registers::CNTPCT_EL0; use alloc::{collections::VecDeque, rc::Rc, vec::Vec}; use cfg_if::cfg_if; +use kernel_util::util::OneTimeInit; use crate::{ // arch::aarch64::{context::TaskContext, cpu::Cpu}, arch::{Architecture, ArchitectureImpl}, sync::{IrqSafeSpinlock, IrqSafeSpinlockGuard}, - util::OneTimeInit, }; use super::{