x86-64: sync x86-64 up with the new kernel structure

This commit is contained in:
Mark Poliakov 2023-08-21 17:26:44 +03:00
parent b0379e398f
commit af5529a84f
57 changed files with 1357 additions and 1315 deletions

View File

@ -11,6 +11,7 @@ yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
vfs = { path = "lib/vfs" } vfs = { path = "lib/vfs" }
memfs = { path = "lib/memfs" } memfs = { path = "lib/memfs" }
device-api = { path = "lib/device-api", features = ["derive"] } device-api = { path = "lib/device-api", features = ["derive"] }
kernel-util = { path = "lib/kernel-util" }
atomic_enum = "0.2.0" atomic_enum = "0.2.0"
bitflags = "2.3.3" bitflags = "2.3.3"
@ -21,12 +22,10 @@ tock-registers = "0.8.1"
cfg-if = "1.0.0" cfg-if = "1.0.0"
git-version = "0.3.5" git-version = "0.3.5"
aarch64-cpu = "9.3.1"
bitmap-font = { version = "0.3.0", optional = true } bitmap-font = { version = "0.3.0", optional = true }
embedded-graphics = { version = "0.8.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] [dependencies.elf]
version = "0.7.2" version = "0.7.2"
@ -35,6 +34,8 @@ default-features = false
features = ["no_std_stream"] features = ["no_std_stream"]
[target.'cfg(target_arch = "aarch64")'.dependencies] [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] [target.'cfg(target_arch = "x86_64")'.dependencies]
yboot-proto = { git = "https://git.alnyan.me/yggdrasil/yboot-proto.git" } yboot-proto = { git = "https://git.alnyan.me/yggdrasil/yboot-proto.git" }

View File

@ -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);
}

View File

@ -27,11 +27,11 @@ pub enum IpiDeliveryTarget {
OtherCpus, OtherCpus,
} }
#[derive(Clone, Copy, PartialEq, Eq, Debug)] // #[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum IrqNumber { // pub enum IrqNumber {
Private(u32), // Private(u32),
Shared(u32), // Shared(u32),
} // }
#[derive(Default, Clone, Copy, Debug)] #[derive(Default, Clone, Copy, Debug)]
pub struct IrqOptions { pub struct IrqOptions {
@ -44,17 +44,19 @@ pub trait InterruptTable {
} }
pub trait ExternalInterruptController { pub trait ExternalInterruptController {
type IrqNumber;
/// Performs IRQ delivery method configuration and registers a handler to execute when it is /// Performs IRQ delivery method configuration and registers a handler to execute when it is
/// fired /// fired
fn register_irq( fn register_irq(
&self, &self,
irq: IrqNumber, irq: Self::IrqNumber,
options: IrqOptions, options: IrqOptions,
handler: &'static dyn InterruptHandler, handler: &'static dyn InterruptHandler,
) -> Result<(), Error>; ) -> Result<(), Error>;
/// Enables the specified IRQ (unmasks it) /// 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. /// Handles a single pending interrupt on this controller.
/// The function is intended for interrupt controllers which have internal registers to track /// The function is intended for interrupt controllers which have internal registers to track
@ -107,6 +109,15 @@ impl<const SIZE: usize> FixedInterruptTable<SIZE> {
self.entries[index] = Some(handler); self.entries[index] = Some(handler);
Ok(()) Ok(())
} }
pub fn insert_least_loaded(
&mut self,
handler: &'static dyn InterruptHandler,
) -> Result<usize, Error> {
let index = self.entries.iter().position(|p| p.is_none()).unwrap();
self.entries[index].replace(handler);
Ok(index)
}
} }
impl<const SIZE: usize> InterruptTable for FixedInterruptTable<SIZE> { impl<const SIZE: usize> InterruptTable for FixedInterruptTable<SIZE> {

View File

@ -4,6 +4,7 @@ extern crate alloc;
pub mod bus; pub mod bus;
pub mod device; pub mod device;
pub mod input;
pub mod interrupt; pub mod interrupt;
pub mod manager; pub mod manager;
pub mod serial; pub mod serial;
@ -24,5 +25,11 @@ pub trait CpuBringupDevice: Device {
} }
pub trait ResetDevice: 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) -> !; unsafe fn reset(&self) -> !;
} }

View File

@ -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]

View File

@ -0,0 +1,5 @@
#![no_std]
#![feature(maybe_uninit_slice)]
pub mod sync;
pub mod util;

View File

View File

@ -1,4 +1,4 @@
use core::marker::PhantomData; use core::{cell::RefCell, marker::PhantomData};
use alloc::boxed::Box; use alloc::boxed::Box;
@ -15,12 +15,12 @@ pub(crate) struct DirectoryNode<A: BlockAllocator> {
} }
impl<A: BlockAllocator> VnodeImpl for DirectoryNode<A> { impl<A: BlockAllocator> VnodeImpl for DirectoryNode<A> {
fn create(&mut self, at: &VnodeRef, name: &str, kind: VnodeKind) -> Result<VnodeRef, Error> { fn create(&self, at: &VnodeRef, name: &str, kind: VnodeKind) -> Result<VnodeRef, Error> {
let child = Vnode::new(name, kind); let child = Vnode::new(name, kind);
match kind { match kind {
VnodeKind::Directory => child.set_data(Box::new(Self { _pd: PhantomData })), VnodeKind::Directory => child.set_data(Box::new(Self { _pd: PhantomData })),
VnodeKind::Regular => child.set_data(Box::new(FileNode { VnodeKind::Regular => child.set_data(Box::new(FileNode {
data: BVec::<A>::new(), data: RefCell::new(BVec::<A>::new()),
})), })),
_ => todo!(), _ => todo!(),
} }
@ -28,24 +28,19 @@ impl<A: BlockAllocator> VnodeImpl for DirectoryNode<A> {
Ok(child) Ok(child)
} }
fn open( fn open(&self, _node: &VnodeRef, _opts: OpenOptions, _mode: FileMode) -> Result<u64, Error> {
&mut self,
_node: &VnodeRef,
_opts: OpenOptions,
_mode: FileMode,
) -> Result<u64, Error> {
Ok(DIR_POSITION_FROM_CACHE) 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(()) Ok(())
} }
fn size(&mut self, node: &VnodeRef) -> Result<u64, Error> { fn size(&self, node: &VnodeRef) -> Result<u64, Error> {
Ok(node.children().len() as u64) Ok(node.children().len() as u64)
} }
fn metadata(&mut self, _node: &VnodeRef) -> Result<FileAttr, Error> { fn metadata(&self, _node: &VnodeRef) -> Result<FileAttr, Error> {
Ok(FileAttr { Ok(FileAttr {
size: 0, size: 0,
mode: FileMode::default_dir(), mode: FileMode::default_dir(),

View File

@ -1,3 +1,5 @@
use core::cell::RefCell;
use vfs::{VnodeImpl, VnodeRef}; use vfs::{VnodeImpl, VnodeRef};
use yggdrasil_abi::{ use yggdrasil_abi::{
error::Error, error::Error,
@ -7,37 +9,37 @@ use yggdrasil_abi::{
use crate::{block::BlockAllocator, bvec::BVec}; use crate::{block::BlockAllocator, bvec::BVec};
pub(crate) struct FileNode<A: BlockAllocator> { pub(crate) struct FileNode<A: BlockAllocator> {
pub(crate) data: BVec<'static, A>, pub(crate) data: RefCell<BVec<'static, A>>,
} }
impl<A: BlockAllocator> VnodeImpl for FileNode<A> { impl<A: BlockAllocator> VnodeImpl for FileNode<A> {
fn open(&mut self, _node: &VnodeRef, opts: OpenOptions, _mode: FileMode) -> Result<u64, Error> { fn open(&self, _node: &VnodeRef, opts: OpenOptions, _mode: FileMode) -> Result<u64, Error> {
if opts.contains(OpenOptions::APPEND) { if opts.contains(OpenOptions::APPEND) {
Ok(self.data.size() as u64) Ok(self.data.borrow().size() as u64)
} else { } else {
Ok(0) Ok(0)
} }
} }
fn close(&mut self, _node: &VnodeRef) -> Result<(), Error> { fn close(&self, _node: &VnodeRef) -> Result<(), Error> {
Ok(()) Ok(())
} }
fn read(&mut self, _node: &VnodeRef, pos: u64, data: &mut [u8]) -> Result<usize, Error> { fn read(&self, _node: &VnodeRef, pos: u64, data: &mut [u8]) -> Result<usize, Error> {
self.data.read(pos, data) self.data.borrow().read(pos, data)
} }
fn write(&mut self, _node: &VnodeRef, pos: u64, data: &[u8]) -> Result<usize, Error> { fn write(&self, _node: &VnodeRef, pos: u64, data: &[u8]) -> Result<usize, Error> {
self.data.write(pos, data) self.data.borrow_mut().write(pos, data)
} }
fn size(&mut self, _node: &VnodeRef) -> Result<u64, Error> { fn size(&self, _node: &VnodeRef) -> Result<u64, Error> {
Ok(self.data.size() as u64) Ok(self.data.borrow().size() as u64)
} }
fn metadata(&mut self, _node: &VnodeRef) -> Result<FileAttr, Error> { fn metadata(&self, _node: &VnodeRef) -> Result<FileAttr, Error> {
Ok(FileAttr { Ok(FileAttr {
size: self.data.size() as u64, size: self.data.borrow().size() as u64,
mode: FileMode::default_file(), mode: FileMode::default_file(),
ty: FileType::File, ty: FileType::File,
}) })

View File

@ -162,7 +162,9 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
let bvec = BVec::<A>::try_from(data)?; let bvec = BVec::<A>::try_from(data)?;
assert_eq!(bvec.size(), data.len()); assert_eq!(bvec.size(), data.len());
node.set_data(Box::new(FileNode { data: bvec })); node.set_data(Box::new(FileNode {
data: RefCell::new(bvec),
}));
} }
} }

View File

@ -7,4 +7,5 @@ edition = "2021"
[dependencies] [dependencies]
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" } yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
kernel-util = { path = "../kernel-util" }
bitflags = "2.3.3" bitflags = "2.3.3"

View File

@ -23,28 +23,23 @@ impl CharDeviceWrapper {
} }
impl VnodeImpl for CharDeviceWrapper { impl VnodeImpl for CharDeviceWrapper {
fn open( fn open(&self, _node: &VnodeRef, _opts: OpenOptions, _mode: FileMode) -> Result<u64, Error> {
&mut self,
_node: &VnodeRef,
_opts: OpenOptions,
_mode: FileMode,
) -> Result<u64, Error> {
Ok(0) Ok(0)
} }
fn close(&mut self, _node: &VnodeRef) -> Result<(), Error> { fn close(&self, _node: &VnodeRef) -> Result<(), Error> {
Ok(()) Ok(())
} }
fn read(&mut self, _node: &VnodeRef, _pos: u64, data: &mut [u8]) -> Result<usize, Error> { fn read(&self, _node: &VnodeRef, _pos: u64, data: &mut [u8]) -> Result<usize, Error> {
self.device.read(true, data) self.device.read(true, data)
} }
fn write(&mut self, _node: &VnodeRef, _pos: u64, data: &[u8]) -> Result<usize, Error> { fn write(&self, _node: &VnodeRef, _pos: u64, data: &[u8]) -> Result<usize, Error> {
self.device.write(true, data) self.device.write(true, data)
} }
fn metadata(&mut self, _node: &VnodeRef) -> Result<FileAttr, Error> { fn metadata(&self, _node: &VnodeRef) -> Result<FileAttr, Error> {
Ok(FileAttr { Ok(FileAttr {
size: 0, size: 0,
ty: FileType::Char, 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) self.device.device_request(req)
} }
} }

View File

@ -9,6 +9,7 @@ use alloc::{
string::String, string::String,
vec::Vec, vec::Vec,
}; };
use kernel_util::util::OneTimeInit;
use yggdrasil_abi::{ use yggdrasil_abi::{
error::Error, error::Error,
io::{DeviceRequest, FileAttr, FileMode, FileType, OpenOptions}, io::{DeviceRequest, FileAttr, FileMode, FileType, OpenOptions},
@ -44,46 +45,46 @@ pub struct Vnode {
name: String, name: String,
tree: RefCell<TreeNode>, tree: RefCell<TreeNode>,
kind: VnodeKind, kind: VnodeKind,
data: RefCell<Option<Box<dyn VnodeImpl>>>, data: OneTimeInit<Box<dyn VnodeImpl>>,
fs: RefCell<Option<Rc<dyn Filesystem>>>, fs: RefCell<Option<Rc<dyn Filesystem>>>,
target: RefCell<Option<VnodeRef>>, target: RefCell<Option<VnodeRef>>,
} }
#[allow(unused_variables)] #[allow(unused_variables)]
pub trait VnodeImpl { pub trait VnodeImpl {
fn create(&mut self, at: &VnodeRef, name: &str, kind: VnodeKind) -> Result<VnodeRef, Error> { fn create(&self, at: &VnodeRef, name: &str, kind: VnodeKind) -> Result<VnodeRef, Error> {
Err(Error::NotImplemented) 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) Err(Error::NotImplemented)
} }
fn open(&mut self, node: &VnodeRef, opts: OpenOptions, mode: FileMode) -> Result<u64, Error> { fn open(&self, node: &VnodeRef, opts: OpenOptions, mode: FileMode) -> Result<u64, Error> {
Err(Error::NotImplemented) Err(Error::NotImplemented)
} }
fn close(&mut self, node: &VnodeRef) -> Result<(), Error> { fn close(&self, node: &VnodeRef) -> Result<(), Error> {
Err(Error::NotImplemented) Err(Error::NotImplemented)
} }
fn read(&mut self, node: &VnodeRef, pos: u64, data: &mut [u8]) -> Result<usize, Error> { fn read(&self, node: &VnodeRef, pos: u64, data: &mut [u8]) -> Result<usize, Error> {
Err(Error::NotImplemented) Err(Error::NotImplemented)
} }
fn write(&mut self, node: &VnodeRef, pos: u64, data: &[u8]) -> Result<usize, Error> { fn write(&self, node: &VnodeRef, pos: u64, data: &[u8]) -> Result<usize, Error> {
Err(Error::NotImplemented) Err(Error::NotImplemented)
} }
fn size(&mut self, node: &VnodeRef) -> Result<u64, Error> { fn size(&self, node: &VnodeRef) -> Result<u64, Error> {
Err(Error::NotImplemented) Err(Error::NotImplemented)
} }
fn metadata(&mut self, node: &VnodeRef) -> Result<FileAttr, Error> { fn metadata(&self, node: &VnodeRef) -> Result<FileAttr, Error> {
Err(Error::NotImplemented) 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) Err(Error::NotImplemented)
} }
} }
@ -97,7 +98,7 @@ impl Vnode {
children: Vec::new(), children: Vec::new(),
}), }),
kind, kind,
data: RefCell::new(None), data: OneTimeInit::new(),
fs: RefCell::new(None), fs: RefCell::new(None),
target: RefCell::new(None), target: RefCell::new(None),
}) })
@ -119,14 +120,18 @@ impl Vnode {
} }
#[inline] #[inline]
pub fn data(&self) -> RefMut<Option<Box<dyn VnodeImpl>>> { pub fn data(&self) -> Option<&dyn VnodeImpl> {
match self.data.try_borrow_mut() { self.data.try_get().map(Box::as_ref)
Ok(r) => r,
Err(e) => {
panic!("{:?} on {:?}", e, self.name())
}
}
} }
// #[inline]
// pub fn data(&self) -> RefMut<Option<Box<dyn VnodeImpl>>> {
// match self.data.try_borrow_mut() {
// Ok(r) => r,
// Err(e) => {
// panic!("{:?} on {:?}", e, self.name())
// }
// }
// }
#[inline] #[inline]
pub fn fs(&self) -> Option<Rc<dyn Filesystem>> { pub fn fs(&self) -> Option<Rc<dyn Filesystem>> {
@ -151,7 +156,8 @@ impl Vnode {
} }
pub fn set_data(&self, data: Box<dyn VnodeImpl>) { pub fn set_data(&self, data: Box<dyn VnodeImpl>) {
self.data.borrow_mut().replace(data); self.data.init(data);
// self.data.borrow_mut().replace(data);
} }
pub fn set_fs(&self, data: Rc<dyn Filesystem>) { pub fn set_fs(&self, data: Rc<dyn Filesystem>) {
@ -273,7 +279,7 @@ impl Vnode {
return Err(Error::IsADirectory); 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)?; let pos = data.open(self, flags, mode)?;
Ok(File::normal(self.clone(), pos, open_flags)) Ok(File::normal(self.clone(), pos, open_flags))
} else { } else {
@ -286,7 +292,7 @@ impl Vnode {
return Err(Error::IsADirectory); 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())?; let pos = data.open(self, OpenOptions::READ, FileMode::empty())?;
Ok(File::directory(self.clone(), pos)) Ok(File::directory(self.clone(), pos))
} else { } else {
@ -296,7 +302,7 @@ impl Vnode {
} }
pub fn close(self: &VnodeRef) -> Result<(), Error> { pub fn close(self: &VnodeRef) -> Result<(), Error> {
if let Some(ref mut data) = *self.data() { if let Some(data) = self.data() {
data.close(self) data.close(self)
} else { } else {
Ok(()) Ok(())
@ -317,7 +323,7 @@ impl Vnode {
e => return e, e => return e,
}; };
if let Some(ref mut data) = *self.data() { if let Some(data) = self.data() {
let vnode = data.create(self, name, kind)?; let vnode = data.create(self, name, kind)?;
if let Some(fs) = self.fs() { if let Some(fs) = self.fs() {
vnode.set_fs(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)?; data.remove(self, name)?;
} }
@ -365,7 +371,7 @@ impl Vnode {
todo!(); todo!();
} }
if let Some(ref mut data) = *self.data() { if let Some(data) = self.data() {
data.write(self, pos, buf) data.write(self, pos, buf)
} else { } else {
todo!() todo!()
@ -377,7 +383,7 @@ impl Vnode {
todo!(); todo!();
} }
if let Some(ref mut data) = *self.data() { if let Some(data) = self.data() {
data.read(self, pos, buf) data.read(self, pos, buf)
} else { } else {
todo!() todo!()
@ -385,7 +391,7 @@ impl Vnode {
} }
pub fn size(self: &VnodeRef) -> Result<u64, Error> { pub fn size(self: &VnodeRef) -> Result<u64, Error> {
if let Some(ref mut data) = *self.data() { if let Some(data) = self.data() {
data.size(self) data.size(self)
} else { } else {
todo!(); todo!();
@ -435,7 +441,7 @@ impl Vnode {
} }
pub fn metadata(self: &VnodeRef) -> Result<FileAttr, Error> { pub fn metadata(self: &VnodeRef) -> Result<FileAttr, Error> {
if let Some(ref mut data) = *self.data() { if let Some(data) = self.data() {
data.metadata(self) data.metadata(self)
} else { } else {
todo!() todo!()
@ -443,7 +449,7 @@ impl Vnode {
} }
pub fn device_request(self: &VnodeRef, req: &mut DeviceRequest) -> Result<(), Error> { 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) data.device_request(self, req)
} else { } else {
todo!() todo!()

View File

@ -100,7 +100,6 @@ unsafe extern "C" fn __aarch64_bsp_upper_entry(dtb_phys: usize) -> ! {
Cpu::init_local(); Cpu::init_local();
kernel_main() kernel_main()
// kernel_main(dtb_phys)
} }
extern "C" fn __aarch64_ap_upper_entry(_x0: usize) -> ! { extern "C" fn __aarch64_ap_upper_entry(_x0: usize) -> ! {

View File

@ -3,11 +3,10 @@ use core::sync::atomic::Ordering;
use aarch64_cpu::registers::{MPIDR_EL1, TPIDR_EL1}; use aarch64_cpu::registers::{MPIDR_EL1, TPIDR_EL1};
use alloc::{boxed::Box, vec::Vec}; use alloc::{boxed::Box, vec::Vec};
use kernel_util::util::OneTimeInit;
use tock_registers::interfaces::{Readable, Writeable}; use tock_registers::interfaces::{Readable, Writeable};
use crate::{ use crate::{arch::CpuMessage, panic, sync::IrqSafeSpinlock, task::sched::CpuQueue};
arch::CpuMessage, panic, sync::IrqSafeSpinlock, task::sched::CpuQueue, util::OneTimeInit,
};
use super::smp::CPU_COUNT; use super::smp::CPU_COUNT;

View File

@ -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<u64>;
/// 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<u64>;
}
/// 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<usize>;
/// Returns the #size-cells property of the node, if there is one
fn get_size_cells(&self) -> Option<usize>;
/// 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<T> {
/// Returns a property value of given type, if it exists
fn prop(&self, name: &str) -> Option<T>;
}
impl<'a, 'i, 'dt> DevTreeIndexNodePropGet<u32> for DevTreeIndexNode<'a, 'i, 'dt> {
fn prop(&self, name: &str) -> Option<u32> {
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<TNode> {
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<usize> {
self.props()
.find(|p| p.name().unwrap_or("") == "#address-cells")
.map(|p| p.u32(0).unwrap() as usize)
}
fn get_size_cells(&self) -> Option<usize> {
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<u64> {
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<u64> {
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<Self::Item> {
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<TProp<'a>> {
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<TNode<'a>> {
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)
}
}
}

View File

@ -8,20 +8,18 @@ use alloc::boxed::Box;
use device_api::{ use device_api::{
interrupt::{ interrupt::{
ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable, ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable,
IpiDeliveryTarget, IrqNumber, IrqOptions, LocalInterruptController, IpiDeliveryTarget, IrqOptions, LocalInterruptController,
}, },
Device, Device,
}; };
use kernel_util::util::OneTimeInit;
use crate::{ use crate::{
arch::{ arch::{aarch64::IrqNumber, Architecture, CpuMessage},
aarch64::devtree::{self, DevTreeIndexPropExt}, device::devtree::{self, DevTreeIndexPropExt},
Architecture, CpuMessage,
},
device_tree_driver, device_tree_driver,
mem::device::{DeviceMemory, DeviceMemoryIo}, mem::device::{DeviceMemory, DeviceMemoryIo},
sync::IrqSafeSpinlock, sync::IrqSafeSpinlock,
util::OneTimeInit,
}; };
use self::{gicc::Gicc, gicd::Gicd}; use self::{gicc::Gicc, gicd::Gicd};
@ -71,6 +69,8 @@ impl Device for Gic {
} }
impl ExternalInterruptController for Gic { impl ExternalInterruptController for Gic {
type IrqNumber = IrqNumber;
fn register_irq( fn register_irq(
&self, &self,
irq: IrqNumber, irq: IrqNumber,

View File

@ -11,30 +11,34 @@ use aarch64_cpu::{
}; };
use abi::error::Error; use abi::error::Error;
use device_api::{ use device_api::{
interrupt::{ interrupt::{ExternalInterruptController, IpiDeliveryTarget, LocalInterruptController},
ExternalInterruptController, IpiDeliveryTarget, IrqNumber, LocalInterruptController,
},
timer::MonotonicTimestampProviderDevice, timer::MonotonicTimestampProviderDevice,
ResetDevice, ResetDevice,
}; };
use fdt_rs::prelude::PropReader; use fdt_rs::prelude::PropReader;
use git_version::git_version; use git_version::git_version;
use kernel_util::util::OneTimeInit;
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use crate::{ use crate::{
arch::{aarch64::devtree::FdtMemoryRegionIter, Architecture}, arch::Architecture,
debug::{self}, debug,
device::{self, power::arm_psci::Psci, DevTreeNodeInfo}, device::{
self,
devtree::{
self, DevTreeIndexNodePropGet, DevTreeIndexPropExt, DevTreeNodeInfo, DeviceTree,
FdtMemoryRegionIter,
},
power::arm_psci::Psci,
},
fs::{Initrd, INITRD_DATA}, fs::{Initrd, INITRD_DATA},
mem::{ mem::{
phys::{self, reserved::reserve_region, PhysicalMemoryRegion}, phys::{self, reserved::reserve_region, PhysicalMemoryRegion},
ConvertAddress, ConvertAddress,
}, },
util::OneTimeInit,
}; };
use self::{ use self::{
devtree::{DevTreeIndexPropExt, DeviceTree},
smp::CPU_COUNT, smp::CPU_COUNT,
table::{init_fixed_tables, KERNEL_TABLES}, table::{init_fixed_tables, KERNEL_TABLES},
}; };
@ -44,7 +48,6 @@ use super::CpuMessage;
pub mod boot; pub mod boot;
pub mod context; pub mod context;
pub mod cpu; pub mod cpu;
pub mod devtree;
pub mod exception; pub mod exception;
pub mod gic; pub mod gic;
pub mod smp; pub mod smp;
@ -59,10 +62,16 @@ struct KernelStack {
data: [u8; BOOT_STACK_SIZE], data: [u8; BOOT_STACK_SIZE],
} }
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum IrqNumber {
Private(u32),
Shared(u32),
}
/// AArch64 platform interface /// AArch64 platform interface
pub struct AArch64 { pub struct AArch64 {
dt: OneTimeInit<DeviceTree<'static>>, dt: OneTimeInit<DeviceTree<'static>>,
ext_intc: OneTimeInit<&'static dyn ExternalInterruptController>, ext_intc: OneTimeInit<&'static dyn ExternalInterruptController<IrqNumber = IrqNumber>>,
local_intc: OneTimeInit<&'static dyn LocalInterruptController<IpiMessage = CpuMessage>>, local_intc: OneTimeInit<&'static dyn LocalInterruptController<IpiMessage = CpuMessage>>,
mtimer: OneTimeInit<&'static dyn MonotonicTimestampProviderDevice>, mtimer: OneTimeInit<&'static dyn MonotonicTimestampProviderDevice>,
reset: OneTimeInit<&'static dyn ResetDevice>, reset: OneTimeInit<&'static dyn ResetDevice>,
@ -85,6 +94,8 @@ pub static ARCHITECTURE: AArch64 = AArch64 {
}; };
impl Architecture for AArch64 { impl Architecture for AArch64 {
type IrqNumber = IrqNumber;
const KERNEL_VIRT_OFFSET: usize = 0xFFFFFF8000000000; const KERNEL_VIRT_OFFSET: usize = 0xFFFFFF8000000000;
unsafe fn init_mmu(&self, bsp: bool) { unsafe fn init_mmu(&self, bsp: bool) {
@ -165,7 +176,7 @@ impl Architecture for AArch64 {
fn register_external_interrupt_controller( fn register_external_interrupt_controller(
&self, &self,
intc: &'static dyn ExternalInterruptController, intc: &'static dyn ExternalInterruptController<IrqNumber = Self::IrqNumber>,
) -> Result<(), Error> { ) -> Result<(), Error> {
self.ext_intc.init(intc); self.ext_intc.init(intc);
Ok(()) Ok(())
@ -192,7 +203,9 @@ impl Architecture for AArch64 {
Ok(()) Ok(())
} }
fn external_interrupt_controller(&self) -> &'static dyn ExternalInterruptController { fn external_interrupt_controller(
&self,
) -> &'static dyn ExternalInterruptController<IrqNumber = Self::IrqNumber> {
*self.ext_intc.get() *self.ext_intc.get()
} }
@ -272,8 +285,7 @@ impl AArch64 {
fn chosen_stdout_path<'a>(dt: &'a DeviceTree) -> Option<&'a str> { fn chosen_stdout_path<'a>(dt: &'a DeviceTree) -> Option<&'a str> {
let chosen = dt.node_by_path("/chosen")?; let chosen = dt.node_by_path("/chosen")?;
let prop = devtree::find_prop(&chosen, "stdout-path")?; chosen.prop("stdout-path")
prop.str().ok()
} }
fn init_platform(&self, bsp: bool) { fn init_platform(&self, bsp: bool) {
@ -294,7 +306,7 @@ impl AArch64 {
node, node,
}; };
if let Some((device, _)) = device::probe_dt_node(&probe) { if let Some((device, _)) = devtree::probe_dt_node(&probe) {
unsafe { unsafe {
device.init().unwrap(); device.init().unwrap();
} }
@ -313,7 +325,7 @@ impl AArch64 {
// Probe and initialize the rest of devices // Probe and initialize the rest of devices
let nodes = dt.root().children(); let nodes = dt.root().children();
if let Err(error) = device::enumerate_dt( if let Err(error) = devtree::enumerate_dt(
address_cells, address_cells,
size_cells, size_cells,
nodes, nodes,
@ -323,7 +335,7 @@ impl AArch64 {
return Ok(()); return Ok(());
} }
if let Some((device, _)) = device::probe_dt_node(&probe) { if let Some((device, _)) = devtree::probe_dt_node(&probe) {
unsafe { unsafe {
device.init()?; device.init()?;
} }

View File

@ -14,7 +14,7 @@ use crate::{
}, },
}; };
use super::devtree::{self, DevTreeIndexNodePropGet, DeviceTree}; use crate::device::devtree::{self, DevTreeIndexNodePropGet, DeviceTree};
#[derive(Debug)] #[derive(Debug)]
enum CpuEnableMethod { enum CpuEnableMethod {

View File

@ -5,15 +5,11 @@ use core::time::Duration;
use aarch64_cpu::registers::{CNTFRQ_EL0, CNTPCT_EL0, CNTP_CTL_EL0, CNTP_TVAL_EL0}; use aarch64_cpu::registers::{CNTFRQ_EL0, CNTPCT_EL0, CNTP_CTL_EL0, CNTP_TVAL_EL0};
use abi::error::Error; use abi::error::Error;
use alloc::boxed::Box; use alloc::boxed::Box;
use device_api::{ use device_api::{interrupt::InterruptHandler, timer::MonotonicTimestampProviderDevice, Device};
interrupt::{InterruptHandler, IrqNumber},
timer::MonotonicTimestampProviderDevice,
Device,
};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use crate::{ use crate::{
arch::{Architecture, ARCHITECTURE}, arch::{aarch64::IrqNumber, Architecture, ARCHITECTURE},
device_tree_driver, device_tree_driver,
proc::wait, proc::wait,
task::tasklet, task::tasklet,

View File

@ -19,31 +19,27 @@ macro_rules! absolute_address {
}}; }};
} }
pub mod aarch64; use cfg_if::cfg_if;
// pub use aarch64::{AArch64 as ArchitectureImpl, ARCHITECTURE};
pub use aarch64::{AArch64 as ArchitectureImpl, ARCHITECTURE};
use device_api::{ use device_api::{
interrupt::{ExternalInterruptController, IpiDeliveryTarget, LocalInterruptController}, interrupt::{ExternalInterruptController, IpiDeliveryTarget, LocalInterruptController},
timer::MonotonicTimestampProviderDevice, timer::MonotonicTimestampProviderDevice,
ResetDevice, ResetDevice,
}; };
// cfg_if! {
// if #[cfg(target_arch = "aarch64")] { cfg_if! {
// if #[cfg(target_arch = "aarch64")] {
// pub use aarch64::{AArch64 as ArchitectureImpl, ARCHITECTURE, PlatformImpl, PLATFORM}; pub mod aarch64;
// } else if #[cfg(target_arch = "x86_64")] {
// pub mod x86_64; pub use aarch64::{AArch64 as ArchitectureImpl, ARCHITECTURE};
// } else if #[cfg(target_arch = "x86_64")] {
// pub use x86_64::{ pub mod x86_64;
// X86_64 as ArchitectureImpl,
// X86_64 as PlatformImpl, pub use x86_64::{X86_64 as ArchitectureImpl, ARCHITECTURE};
// ARCHITECTURE, } else {
// ARCHITECTURE as PLATFORM compile_error!("Architecture is not supported");
// }; }
// } else { }
// compile_error!("Architecture is not supported");
// }
// }
/// Describes messages sent from some CPU to others /// Describes messages sent from some CPU to others
#[derive(Clone, Copy, PartialEq, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]
@ -58,6 +54,9 @@ pub trait Architecture {
/// Address, to which "zero" address is mapped in the virtual address space /// Address, to which "zero" address is mapped in the virtual address space
const KERNEL_VIRT_OFFSET: usize; 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. /// 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 /// `bsp` flag is provided to make sure mapping tables are only initialized once in a SMP
/// system. /// system.
@ -99,7 +98,7 @@ pub trait Architecture {
/// Adds an external interrupt controller to the system /// Adds an external interrupt controller to the system
fn register_external_interrupt_controller( fn register_external_interrupt_controller(
&self, &self,
intc: &'static dyn ExternalInterruptController, intc: &'static dyn ExternalInterruptController<IrqNumber = Self::IrqNumber>,
) -> Result<(), Error>; ) -> Result<(), Error>;
/// Adds a local interrupt controller to the system /// Adds a local interrupt controller to the system
@ -119,15 +118,17 @@ pub trait Architecture {
// TODO only supports 1 extintc per system // TODO only supports 1 extintc per system
/// Returns the primary external interrupt controller /// Returns the primary external interrupt controller
fn external_interrupt_controller(&self) -> &'static dyn ExternalInterruptController; fn external_interrupt_controller(
&'static self,
) -> &'static dyn ExternalInterruptController<IrqNumber = Self::IrqNumber>;
/// Returns the local interrupt controller /// Returns the local interrupt controller
fn local_interrupt_controller( fn local_interrupt_controller(
&self, &'static self,
) -> &'static dyn LocalInterruptController<IpiMessage = CpuMessage>; ) -> &'static dyn LocalInterruptController<IpiMessage = CpuMessage>;
/// Returns the monotonic timer /// 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. /// Sends a message to the requested set of CPUs through an interprocessor interrupt.
/// ///

View File

@ -1,19 +1,21 @@
//! x86-64 I/O APIC driver implementation //! x86-64 I/O APIC driver implementation
use abi::error::Error; use abi::error::Error;
use acpi::platform::interrupt::{Apic as AcpiApic, Polarity, TriggerMode}; use acpi::platform::interrupt::{Apic as AcpiApic, Polarity, TriggerMode};
use device_api::{
use crate::{ interrupt::{
arch::x86_64::apic::local::BSP_APIC_ID, ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable,
device::{ IrqLevel, IrqOptions, IrqTrigger,
interrupt::{ExternalInterruptController, InterruptSource},
Device, InitializableDevice,
}, },
mem::ConvertAddress, Device,
sync::IrqSafeSpinlock,
util::OneTimeInit,
}; };
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 // IRQ 0 is timer, IRQ 1 reserved (for now?), +32 offset for exception entries
const IO_APIC_VECTOR_OFFSET: u32 = 32 + APIC_EXTERNAL_OFFSET; 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 /// I/O APIC interface. Provides a way to route and control how interrupts from external devices
/// are handled. /// are handled.
pub struct IoApic { pub struct IoApic {
inner: OneTimeInit<IrqSafeSpinlock<Inner>>, inner: IrqSafeSpinlock<Inner>,
isa_redirections: OneTimeInit<[Option<IsaRedirection>; 16]>, isa_redirections: [Option<IsaRedirection>; 16],
table: IrqSafeSpinlock<Table>, table: IrqSafeSpinlock<FixedInterruptTable<{ MAX_EXTERNAL_VECTORS as usize }>>,
} }
impl Regs { impl Regs {
@ -108,21 +110,29 @@ impl Inner {
Ok(()) 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); assert!(gsi < self.max_gsi);
let mut low = self.regs.read(REG_REDIRECTION_BASE + gsi * 2); let mut low = self.regs.read(REG_REDIRECTION_BASE + gsi * 2);
if active_low { match options.level {
low |= ENTRY_LOW_POLARITY_LOW; IrqLevel::Default => (),
} else { IrqLevel::ActiveLow => {
low &= !ENTRY_LOW_POLARITY_LOW; low |= ENTRY_LOW_POLARITY_LOW;
}
IrqLevel::ActiveHigh => {
low &= !ENTRY_LOW_POLARITY_LOW;
}
} }
if level_triggered { match options.trigger {
low |= ENTRY_LOW_TRIGGER_LEVEL; IrqTrigger::Default => (),
} else { IrqTrigger::Level => {
low &= !ENTRY_LOW_TRIGGER_LEVEL; low |= ENTRY_LOW_TRIGGER_LEVEL;
}
IrqTrigger::Edge => {
low &= !ENTRY_LOW_TRIGGER_LEVEL;
}
} }
self.regs.write(REG_REDIRECTION_BASE + gsi * 2, low); self.regs.write(REG_REDIRECTION_BASE + gsi * 2, low);
@ -144,71 +154,69 @@ impl Inner {
} }
impl Device for IoApic { impl Device for IoApic {
fn name(&self) -> &'static str { fn display_name(&self) -> &'static str {
"I/O APIC" "I/O APIC"
} }
unsafe fn init(&'static self) -> Result<(), Error> {
todo!()
}
} }
impl ExternalInterruptController for IoApic { impl ExternalInterruptController for IoApic {
type IrqNumber = IrqNumber; type IrqNumber = IrqNumber;
fn enable_irq(&self, irq: Self::IrqNumber) -> Result<(), Error> { fn register_irq(
let mut inner = self.inner.get().lock();
let gsi = self.translate_irq(irq);
inner.set_gsi_enabled(gsi, true);
Ok(())
}
fn register_handler(
&self, &self,
irq: Self::IrqNumber, irq: Self::IrqNumber,
handler: &'static (dyn InterruptSource + Sync), options: IrqOptions,
handler: &'static dyn InterruptHandler,
) -> Result<(), Error> { ) -> Result<(), Error> {
let mut inner = self.inner.get().lock(); let mut inner = self.inner.lock();
let mut table = self.table.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 gsi_target_vector = (table_vector as u32) + IO_APIC_VECTOR_OFFSET;
let bsp_apic = *BSP_APIC_ID.get(); let bsp_apic = *BSP_APIC_ID.get();
infoln!( infoln!(
"Binding {:?} ({}) to {}:{}", "Binding {:?} ({}) to {}:{}",
irq, irq,
handler.name(), handler.display_name(),
bsp_apic, bsp_apic,
table_vector table_vector
); );
table.add_handler(table_vector, handler)?;
let gsi = self.translate_irq(irq); 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(()) Ok(())
} }
fn configure_irq( fn enable_irq(&self, irq: Self::IrqNumber) -> Result<(), Error> {
&self, let mut inner = self.inner.lock();
irq: Self::IrqNumber,
active_low: bool,
level_triggered: bool,
) -> Result<(), Error> {
let mut inner = self.inner.get().lock();
let gsi = self.translate_irq(irq); let gsi = self.translate_irq(irq);
inner.set_gsi_enabled(gsi, true);
inner.configure_gsi(gsi, active_low, level_triggered);
Ok(()) 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 { impl IoApic {
type Options = AcpiApic; /// Creates an I/O APIC instance from its ACPI definition
pub fn from_acpi(info: &AcpiApic) -> Result<Self, Error> {
let ioapic = info.io_apics.first().unwrap();
unsafe fn init(&self, info: Self::Options) -> Result<(), Error> { infoln!("I/O APIC at {:#x}", ioapic.address);
let io_apic = info.io_apics.first().unwrap();
infoln!("I/O APIC at {:#x}", io_apic.address);
let mut isa_redirections = [None; 16]; let mut isa_redirections = [None; 16];
@ -236,8 +244,9 @@ impl InitializableDevice for IoApic {
}); });
} }
// TODO properly map this using DeviceMemory
let regs = Regs { 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; let max_gsi = (regs.read(REG_IOAPIC_VERSION) >> 16) & 0xFF;
@ -251,36 +260,15 @@ impl InitializableDevice for IoApic {
inner.set_gsi_enabled(gsi, false); inner.set_gsi_enabled(gsi, false);
} }
self.inner.init(IrqSafeSpinlock::new(inner)); Ok(Self {
self.isa_redirections.init(isa_redirections); isa_redirections,
inner: IrqSafeSpinlock::new(inner),
Ok(()) table: IrqSafeSpinlock::new(FixedInterruptTable::new()),
} })
}
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);
}
} }
fn translate_irq(&self, irq: IrqNumber) -> u32 { fn translate_irq(&self, irq: IrqNumber) -> u32 {
let redir = self.isa_redirections.get(); let redir = &self.isa_redirections;
match irq { match irq {
IrqNumber::Isa(isa) => redir[isa as usize] IrqNumber::Isa(isa) => redir[isa as usize]
.map(|t| t.gsi_index) .map(|t| t.gsi_index)

View File

@ -1,4 +1,6 @@
//! x86-64 Local APIC driver implementation //! x86-64 Local APIC driver implementation
use device_api::{interrupt::LocalInterruptController, Device};
use kernel_util::util::OneTimeInit;
use tock_registers::{ use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable}, interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs, register_bitfields, register_structs,
@ -6,13 +8,11 @@ use tock_registers::{
}; };
use crate::{ use crate::{
arch::x86_64::registers::MSR_IA32_APIC_BASE, device::interrupt::IpiDeliveryTarget, arch::{x86_64::registers::MSR_IA32_APIC_BASE, CpuMessage},
mem::ConvertAddress, util::OneTimeInit, mem::ConvertAddress,
}; };
use super::{ use super::{APIC_LINT0_VECTOR, APIC_LINT1_VECTOR, APIC_SPURIOUS_VECTOR, APIC_TIMER_VECTOR};
APIC_IPI_VECTOR, APIC_LINT0_VECTOR, APIC_LINT1_VECTOR, APIC_SPURIOUS_VECTOR, APIC_TIMER_VECTOR,
};
const TIMER_INTERVAL: u32 = 150000; const TIMER_INTERVAL: u32 = 150000;
@ -122,6 +122,31 @@ pub struct LocalApic {
regs: &'static Regs, 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 { impl LocalApic {
/// Constructs a new instance of Local APIC. /// Constructs a new instance of Local APIC.
/// ///
@ -232,28 +257,28 @@ impl LocalApic {
} }
} }
/// Issues an interprocessor interrupt for the target. // /// Issues an interprocessor interrupt for the target.
/// // ///
/// # Safety // /// # Safety
/// // ///
/// Unsafe: this function may break the control flow on the target processors. // /// Unsafe: this function may break the control flow on the target processors.
pub unsafe fn send_ipi(&self, target: IpiDeliveryTarget) { // pub unsafe fn send_ipi(&self, target: IpiDeliveryTarget) {
while self.regs.ICR0.matches_all(ICR0::DeliveryStatus::SET) { // while self.regs.ICR0.matches_all(ICR0::DeliveryStatus::SET) {
core::hint::spin_loop(); // core::hint::spin_loop();
} // }
match target { // match target {
IpiDeliveryTarget::AllExceptLocal => { // IpiDeliveryTarget::AllExceptLocal => {
self.regs.ICR1.write(ICR1::PhysicalDestination.val(0)); // self.regs.ICR1.write(ICR1::PhysicalDestination.val(0));
self.regs.ICR0.write( // self.regs.ICR0.write(
ICR0::Vector.val(APIC_IPI_VECTOR + 32) // ICR0::Vector.val(APIC_IPI_VECTOR + 32)
+ ICR0::Destination::NMI // + ICR0::Destination::NMI
+ ICR0::DestinationType::AllExceptThis, // + ICR0::DestinationType::AllExceptThis,
); // );
} // }
IpiDeliveryTarget::Specified(_) => todo!(), // IpiDeliveryTarget::Specified(_) => todo!(),
} // }
} // }
#[inline] #[inline]
fn base() -> usize { fn base() -> usize {

View File

@ -2,15 +2,15 @@
use core::arch::global_asm; use core::arch::global_asm;
use abi::error::Error;
use crate::{ use crate::{
arch::{x86_64::cpu::Cpu, PLATFORM}, arch::{x86_64::cpu::Cpu, Architecture},
device::interrupt::IrqHandler,
task::process::Process, task::process::Process,
}; };
use super::exception::{self, IrqFrame}; use super::{
exception::{self, IrqFrame},
ARCHITECTURE,
};
pub mod ioapic; pub mod ioapic;
pub mod local; 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 /// Maximum number of APIC vectors allocated for handling IRQs from I/O APIC
pub const MAX_EXTERNAL_VECTORS: u32 = 16; 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<IrqHandler>; 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 /// Fills the IDT with interrupt vectors for this APIC
pub fn setup_vectors(idt: &mut [exception::Entry]) { pub fn setup_vectors(idt: &mut [exception::Entry]) {
extern "C" { extern "C" {
@ -83,7 +52,9 @@ unsafe extern "C" fn irq_handler(vector: usize, frame: *mut IrqFrame) {
let cpu = Cpu::local(); let cpu = Cpu::local();
let frame = &mut *frame; let frame = &mut *frame;
PLATFORM.ioapic.handle_irq(vector as u32); ARCHITECTURE
.external_interrupt_controller()
.handle_specific_irq(vector);
cpu.local_apic().clear_interrupt(); cpu.local_apic().clear_interrupt();
if let Some(process) = Process::get_current() { if let Some(process) = Process::get_current() {

View File

@ -9,18 +9,21 @@ use yboot_proto::{
use crate::{ use crate::{
arch::{ arch::{
x86_64::{ x86_64::{cpuid, exception, registers::MSR_IA32_KERNEL_GS_BASE, BootData},
apic::local::LocalApic, cpu::Cpu, cpuid, exception, kernel_main, Architecture, ArchitectureImpl, ARCHITECTURE,
registers::MSR_IA32_KERNEL_GS_BASE, syscall, CPU_INIT_FENCE, },
}, fs::devfs,
Architecture, ArchitectureImpl, 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; const BOOT_STACK_SIZE: usize = 65536;
@ -76,45 +79,58 @@ unsafe extern "C" fn __x86_64_upper_entry() -> ! {
ARCHITECTURE.init_mmu(true); ARCHITECTURE.init_mmu(true);
core::arch::asm!("wbinvd"); 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 /// Application processor entry point
pub extern "C" fn __x86_64_ap_entry() -> ! { 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); MSR_IA32_KERNEL_GS_BASE.set(&UNINIT_CPU as *const _ as u64);
// unsafe { unsafe {
// core::arch::asm!("swapgs"); core::arch::asm!("swapgs");
// } }
// MSR_IA32_KERNEL_GS_BASE.set(&UNINIT_CPU as *const _ as u64); MSR_IA32_KERNEL_GS_BASE.set(&UNINIT_CPU as *const _ as u64);
// unsafe { unsafe {
// core::arch::asm!("swapgs"); core::arch::asm!("swapgs");
// } }
// // Still not initialized: GDT, IDT, CPU features, syscall, kernel_gs_base // Still not initialized: GDT, IDT, CPU features, syscall, kernel_gs_base
// cpuid::feature_gate(); cpuid::feature_gate();
// infoln!("cpu{} initializing", cpu_id); infoln!("cpu{} initializing", cpu_id);
// unsafe { unsafe {
// ARCHITECTURE.init_mmu(false); ARCHITECTURE.init_mmu(false);
// core::arch::asm!("wbinvd"); core::arch::asm!("wbinvd");
// Cpu::init_local(LocalApic::new(), cpu_id as u32); // Cpu::init_local(LocalApic::new(), cpu_id as u32);
// syscall::init_syscall(); // syscall::init_syscall();
// exception::init_exceptions(cpu_id); 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(); kernel_secondary_main()
// infoln!("cpu{} initialized", cpu_id);
// unsafe { task::enter() }
} }
global_asm!( global_asm!(

View File

@ -2,12 +2,12 @@
use core::ptr::null_mut; use core::ptr::null_mut;
use alloc::boxed::Box; use alloc::boxed::Box;
use kernel_util::util::OneTimeInit;
use tock_registers::interfaces::Writeable; use tock_registers::interfaces::Writeable;
use crate::{ 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, task::sched::CpuQueue,
util::OneTimeInit,
}; };
use super::apic::local::LocalApic; use super::apic::local::LocalApic;
@ -53,6 +53,8 @@ impl Cpu {
MSR_IA32_KERNEL_GS_BASE.set(this as u64); MSR_IA32_KERNEL_GS_BASE.set(this as u64);
core::arch::asm!("wbinvd; swapgs"); core::arch::asm!("wbinvd; swapgs");
syscall::init_syscall();
} }
/// Returns this CPU's local data structure /// Returns this CPU's local data structure

View File

@ -2,50 +2,50 @@
use core::{ptr::NonNull, sync::atomic::Ordering}; use core::{ptr::NonNull, sync::atomic::Ordering};
use abi::error::Error; use abi::error::Error;
use acpi::{ use acpi::{AcpiHandler, AcpiTables, HpetInfo, InterruptModel, PhysicalMapping};
platform::ProcessorInfo, AcpiHandler, AcpiTables, HpetInfo, InterruptModel, PhysicalMapping, use alloc::boxed::Box;
};
use cpu::Cpu; use cpu::Cpu;
use device_api::{
input::KeyboardProducer,
interrupt::{ExternalInterruptController, IpiDeliveryTarget, LocalInterruptController},
timer::MonotonicTimestampProviderDevice,
Device,
};
use git_version::git_version; 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::{ use crate::{
arch::x86_64::{ arch::x86_64::{
apic::local::LocalApic, apic::local::LocalApic,
peripherals::serial::ComPort,
table::{init_fixed_tables, KERNEL_TABLES}, table::{init_fixed_tables, KERNEL_TABLES},
}, },
debug::{self, LogLevel}, debug::{self, LogLevel},
device::{ device::{
self,
display::{ display::{
console::{add_console_autoflush, ConsoleBuffer, ConsoleRow, DisplayConsole}, console::{self, ConsoleBuffer},
fb_console::FramebufferConsole, fb_console::FramebufferConsole,
linear_fb::LinearFramebuffer, linear_fb::LinearFramebuffer,
}, },
input::KeyboardDevice, tty::combined::CombinedTerminal,
interrupt::{ExternalInterruptController, InterruptSource, IpiDeliveryTarget},
platform::Platform,
timer::TimestampSource,
tty::CombinedTerminal,
InitializableDevice,
}, },
fs::{ fs::{
devfs::{self, CharDeviceType}, devfs::{self, CharDeviceType},
Initrd, INITRD_DATA, Initrd, INITRD_DATA,
}, },
mem::{ mem::{
heap, phys::{self, reserved::reserve_region, PhysicalMemoryRegion},
phys::{self, reserved::reserve_region, PageUsage, PhysicalMemoryRegion},
ConvertAddress, ConvertAddress,
}, },
sync::SpinFence, CPU_INIT_FENCE,
task,
util::OneTimeInit,
}; };
use self::{ use self::{
apic::{ioapic::IoApic, IrqNumber}, apic::ioapic::IoApic,
intrinsics::IoPort, intrinsics::IoPort,
peripherals::{hpet::Hpet, ps2::PS2Controller, serial::ComPort}, peripherals::{hpet::Hpet, ps2::PS2Controller},
smp::CPU_COUNT, smp::CPU_COUNT,
}; };
@ -67,7 +67,14 @@ pub mod smp;
pub mod syscall; pub mod syscall;
pub mod table; 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 /// Helper trait to provide abstract access to available memory regions
pub trait AbstractAvailableRegion { pub trait AbstractAvailableRegion {
@ -119,6 +126,27 @@ impl<'a, T: IterableMemoryMap<'a> + 'a> AbstractMemoryMap<'a> for T {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
struct AcpiHandlerImpl; 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 { impl AcpiHandler for AcpiHandlerImpl {
// No actual address space modification is performed // No actual address space modification is performed
unsafe fn map_physical_region<T>( unsafe fn map_physical_region<T>(
@ -143,28 +171,44 @@ impl AcpiHandler for AcpiHandlerImpl {
fn unmap_physical_region<T>(_region: &PhysicalMapping<Self, T>) {} fn unmap_physical_region<T>(_region: &PhysicalMapping<Self, T>) {}
} }
/// 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 /// x86-64 architecture + platform implementation
pub struct X86_64 { pub struct X86_64 {
yboot_framebuffer: OneTimeInit<FramebufferOption>, boot_data: OneTimeInit<BootData>,
rsdp_phys_base: OneTimeInit<usize>, acpi: OneTimeInit<AcpiTables<AcpiHandlerImpl>>,
acpi_processor_info: OneTimeInit<ProcessorInfo>, framebuffer: OneTimeInit<LinearFramebuffer>,
fb_console: OneTimeInit<FramebufferConsole>,
// TODO make this fully dynamic?
combined_terminal: OneTimeInit<CombinedTerminal>, combined_terminal: OneTimeInit<CombinedTerminal>,
// Static devices with dynamic addresses ioapic: OneTimeInit<IoApic>,
ioapic: IoApic, timer: OneTimeInit<Hpet>,
timer: Hpet,
// Static devices
ps2: PS2Controller,
com1_3: ComPort,
} }
/// 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 { impl Architecture for X86_64 {
const KERNEL_VIRT_OFFSET: usize = 0xFFFFFF8000000000; const KERNEL_VIRT_OFFSET: usize = 0xFFFFFF8000000000;
type IrqNumber = IrqNumber;
unsafe fn init_mmu(&self, bsp: bool) { unsafe fn init_mmu(&self, bsp: bool) {
if bsp { if bsp {
init_fixed_tables(); 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)); core::arch::asm!("wbinvd; mov {0}, %cr3", in(reg) cr3, options(att_syntax));
} }
fn map_device_pages(&self, phys: usize, count: usize) -> Result<usize, Error> {
unsafe { KERNEL_TABLES.map_device_pages(phys, count) }
}
fn wait_for_interrupt() {
unsafe {
core::arch::asm!("hlt");
}
}
unsafe fn set_interrupt_mask(mask: bool) { unsafe fn set_interrupt_mask(mask: bool) {
if mask { if mask {
core::arch::asm!("cli"); core::arch::asm!("cli");
@ -201,98 +235,18 @@ impl Architecture for X86_64 {
flags & (1 << 9) == 0 flags & (1 << 9) == 0
} }
fn cpu_count() -> usize { fn wait_for_interrupt() {
CPU_COUNT.load(Ordering::Acquire) unsafe {
} core::arch::asm!("hlt");
}
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)?;
} }
Ok(())
} }
unsafe fn init_debug(&'static self) { // CPU management
// Serial output unsafe fn reset(&self) -> ! {
self.com1_3.init(()).ok(); todo!()
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();
} }
fn name(&self) -> &'static str { unsafe fn send_ipi(&self, target: IpiDeliveryTarget, msg: CpuMessage) -> Result<(), Error> {
"x86-64"
}
fn interrupt_controller(
&self,
) -> &dyn ExternalInterruptController<IrqNumber = Self::IrqNumber> {
&self.ioapic
}
fn timestamp_source(&self) -> &dyn TimestampSource {
&self.timer
}
unsafe fn send_ipi(&self, target: IpiDeliveryTarget, _msg: CpuMessage) -> Result<(), Error> {
if !CPU_INIT_FENCE.try_wait_all(1) { if !CPU_INIT_FENCE.try_wait_all(1) {
// Don't send an IPI: SMP not initialized yet // Don't send an IPI: SMP not initialized yet
return Ok(()); return Ok(());
@ -302,14 +256,132 @@ impl Platform for X86_64 {
panic!("Local APIC has not been initialized yet"); 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<usize, Error> {
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<IpiMessage = CpuMessage> {
todo!()
}
fn external_interrupt_controller(
&'static self,
) -> &'static dyn ExternalInterruptController<IrqNumber = Self::IrqNumber> {
self.ioapic.get()
} }
} }
impl X86_64 { 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 the lower 8MiB of memory
reserve_region( reserve_region(
"lower-memory", "lower-memory",
@ -318,9 +390,8 @@ impl X86_64 {
size: 8 << 21, size: 8 << 21,
}, },
); );
// Reserve memory map
reserve_region("memory-map", memory_map.reserved_range());
// Reserve initrd
if let Some(initrd) = INITRD_DATA.try_get() { if let Some(initrd) = INITRD_DATA.try_get() {
reserve_region( reserve_region(
"initrd", "initrd",
@ -331,21 +402,125 @@ impl X86_64 {
); );
} }
phys::init_from_iter(memory_map.iter().map(|r| PhysicalMemoryRegion { match *self.boot_data.get() {
base: r.start_address(), BootData::YBoot(data) => {
size: r.page_count() * 0x1000, let memory_map = &data.memory_map;
}))
.expect("Failed to initialize the physical memory manager");
// Reallocate the console buffer reserve_region("memory-map", memory_map.reserved_range());
FB_CONSOLE
.get() phys::init_from_iter(IterableMemoryMap::iter(memory_map).map(|r| {
.reallocate_buffer() PhysicalMemoryRegion {
.expect("Failed to reallocate console buffer"); 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) { unsafe fn init_platform_from_acpi(&self, acpi: &AcpiTables<AcpiHandlerImpl>) {
self.rsdp_phys_base.init(phys); 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() { unsafe fn disable_8259() {
@ -370,121 +545,3 @@ impl X86_64 {
pic_slave_cmd.write(0x20); 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<LinearFramebuffer> = OneTimeInit::new();
static FB_CONSOLE: OneTimeInit<FramebufferConsole> = 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()
}
}

View File

@ -3,6 +3,11 @@ use core::time::Duration;
use abi::error::Error; use abi::error::Error;
use acpi::hpet::HpetInfo as AcpiHpet; use acpi::hpet::HpetInfo as AcpiHpet;
use device_api::{
interrupt::{InterruptHandler, IrqLevel, IrqOptions, IrqTrigger},
timer::MonotonicTimestampProviderDevice,
Device,
};
use tock_registers::{ use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable}, interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs, register_bitfields, register_structs,
@ -10,16 +15,11 @@ use tock_registers::{
}; };
use crate::{ use crate::{
arch::{x86_64::apic::IrqNumber, PLATFORM}, arch::{x86_64::IrqNumber, Architecture, ARCHITECTURE},
device::{
interrupt::InterruptSource, platform::Platform, timer::TimestampSource, Device,
InitializableDevice,
},
mem::device::DeviceMemoryIo, mem::device::DeviceMemoryIo,
proc::wait, proc::wait,
sync::IrqSafeSpinlock, sync::IrqSafeSpinlock,
task::tasklet, task::tasklet,
util::OneTimeInit,
}; };
register_bitfields! { register_bitfields! {
@ -102,7 +102,7 @@ struct Inner {
/// HPET timer group interface /// HPET timer group interface
pub struct Hpet { pub struct Hpet {
inner: OneTimeInit<IrqSafeSpinlock<Inner>>, inner: IrqSafeSpinlock<Inner>,
} }
impl Inner { impl Inner {
@ -135,17 +135,17 @@ impl Inner {
} }
} }
impl TimestampSource for Hpet { impl MonotonicTimestampProviderDevice for Hpet {
fn timestamp(&self) -> Result<Duration, Error> { fn monotonic_timestamp(&self) -> Result<Duration, Error> {
let inner = self.inner.get().lock(); let inner = self.inner.lock();
Ok(Duration::from_millis(inner.tim0_counter)) Ok(Duration::from_millis(inner.tim0_counter))
} }
} }
impl InterruptSource for Hpet { impl InterruptHandler for Hpet {
fn handle_irq(&self) -> Result<bool, Error> { fn handle_irq(&self) -> bool {
let now = { let now = {
let mut inner = self.inner.get().lock(); let mut inner = self.inner.lock();
inner.regs.GeneralInterruptStatus.set(1); inner.regs.GeneralInterruptStatus.set(1);
inner.tim0_counter = inner.tim0_counter.wrapping_add(1); inner.tim0_counter = inner.tim0_counter.wrapping_add(1);
@ -155,7 +155,13 @@ impl InterruptSource for Hpet {
wait::tick(now); wait::tick(now);
tasklet::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> { unsafe fn init_irq(&'static self) -> Result<(), Error> {
@ -163,8 +169,8 @@ impl InterruptSource for Hpet {
const FS_IN_MS: u64 = 1000000000000; const FS_IN_MS: u64 = 1000000000000;
// Configure timer 0 // Configure timer 0
let intc = PLATFORM.interrupt_controller(); let intc = ARCHITECTURE.external_interrupt_controller();
let mut inner = self.inner.get().lock(); let mut inner = self.inner.lock();
// Calculate the interrupt period // Calculate the interrupt period
let clk_period = inner 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"); let tim0_irq = tim0_irq.expect("Could not pick an IRQ for HPET TIM0");
// Bind and enable the IRQ // Bind and enable the IRQ
let irq = IrqNumber::Gsi(tim0_irq as _); let irq = IrqNumber::Gsi(tim0_irq);
intc.register_handler(irq, self)?; intc.register_irq(
intc.configure_irq(irq, true, true)?; irq,
IrqOptions {
level: IrqLevel::ActiveLow,
trigger: IrqTrigger::Level,
},
self,
)?;
intc.enable_irq(irq)?; intc.enable_irq(irq)?;
// Disable FSB interrupt route and 32 bit mode // Disable FSB interrupt route and 32 bit mode
@ -245,32 +257,16 @@ impl InterruptSource for Hpet {
} }
} }
impl InitializableDevice for Hpet { impl Hpet {
type Options = AcpiHpet; /// Creates a HPET instance from its ACPI definition
pub fn from_acpi(info: &AcpiHpet) -> Result<Self, Error> {
unsafe fn init(&self, info: Self::Options) -> Result<(), Error> {
infoln!("Initializing HPET:"); infoln!("Initializing HPET:");
infoln!("Address: {:#x}", info.base_address); infoln!("Address: {:#x}", info.base_address);
let inner = unsafe { Inner::new(info.base_address) }?; let inner = unsafe { Inner::new(info.base_address) }?;
self.inner.init(IrqSafeSpinlock::new(inner)); Ok(Self {
inner: 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(),
}
} }
} }

View File

@ -1,14 +1,15 @@
//! Intel 8042 PS/2 controller driver implemenation //! Intel 8042 PS/2 controller driver implemenation
use abi::error::Error; use abi::error::Error;
use device_api::{
input::{KeyboardConsumer, KeyboardProducer},
interrupt::InterruptHandler,
Device,
};
use crate::{ use crate::{
arch::{ arch::{
x86_64::{apic::IrqNumber, intrinsics::IoPort}, x86_64::{intrinsics::IoPort, IrqNumber},
PLATFORM, Architecture, ARCHITECTURE,
},
device::{
input::KeyboardDevice, interrupt::InterruptSource, platform::Platform, tty::TtyDevice,
Device,
}, },
sync::IrqSafeSpinlock, sync::IrqSafeSpinlock,
}; };
@ -24,7 +25,7 @@ struct Inner {
shift: bool, shift: bool,
ctrl: bool, ctrl: bool,
tty: Option<&'static dyn TtyDevice<16>>, consumer: Option<&'static dyn KeyboardConsumer>, // tty: Option<&'static dyn TtyDevice<16>>,
} }
/// PS/2 controller driver /// PS/2 controller driver
@ -80,37 +81,14 @@ impl Inner {
} }
} }
impl KeyboardDevice for PS2Controller { impl KeyboardProducer for PS2Controller {
fn attach(&self, terminal: &'static dyn TtyDevice<16>) { fn connect(&self, to: &'static dyn KeyboardConsumer) {
self.inner.lock().tty.replace(terminal); self.inner.lock().consumer.replace(to);
} }
} }
impl InterruptSource for PS2Controller { impl InterruptHandler for PS2Controller {
unsafe fn init_irq(&'static self) -> Result<(), Error> { fn handle_irq(&self) -> bool {
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<bool, Error> {
let mut count = 0; let mut count = 0;
let mut inner = self.inner.lock(); let mut inner = self.inner.lock();
loop { loop {
@ -177,21 +155,49 @@ impl InterruptSource for PS2Controller {
continue; continue;
} }
if let Some(tty) = inner.tty { if let Some(consumer) = inner.consumer {
tty.recv_byte(key); consumer.handle_character(key).ok();
} }
count += 1; count += 1;
} }
Ok(count != 0) count != 0
} }
} }
impl Device for PS2Controller { impl Device for PS2Controller {
fn name(&self) -> &'static str { fn display_name(&self) -> &'static str {
"PS/2 Controller" "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 { impl PS2Controller {
@ -207,7 +213,7 @@ impl PS2Controller {
data: IoPort::new(data_port), data: IoPort::new(data_port),
shift: false, shift: false,
ctrl: false, ctrl: false,
tty: None, consumer: None,
}; };
Self { Self {

View File

@ -1,9 +1,10 @@
//! Driver for x86 COM ports
use abi::error::Error; use abi::error::Error;
use device_api::{serial::SerialDevice, Device};
use crate::{ use crate::{
arch::x86_64::{apic::IrqNumber, intrinsics::IoPort}, arch::x86_64::{intrinsics::IoPort, IrqNumber},
debug::DebugSink, debug::DebugSink,
device::{serial::SerialDevice, Device, InitializableDevice},
sync::IrqSafeSpinlock, sync::IrqSafeSpinlock,
}; };
@ -13,11 +14,13 @@ struct Inner {
lsr: IoPort<u8>, lsr: IoPort<u8>,
} }
/// Single port of the COM port pair
pub struct Port { pub struct Port {
inner: IrqSafeSpinlock<Inner>, inner: IrqSafeSpinlock<Inner>,
} }
// Port pair /// COM port pair
#[allow(unused)]
pub struct ComPort { pub struct ComPort {
port_a: Port, port_a: Port,
port_b: Port, port_b: Port,
@ -41,22 +44,22 @@ impl SerialDevice for Port {
inner.dr.write(byte); inner.dr.write(byte);
Ok(()) Ok(())
} }
fn receive(&self, blocking: bool) -> Result<u8, Error> {
todo!()
}
} }
impl Device for Port { impl Device for Port {
fn name(&self) -> &'static str { fn display_name(&self) -> &'static str {
"com-port" "COM port"
}
unsafe fn init(&'static self) -> Result<(), Error> {
Ok(())
} }
} }
impl Port { impl Port {
const LSR_THRE: u8 = 1 << 5; const LSR_THRE: u8 = 1 << 5;
pub const fn new(base: u16) -> Self { const fn new(base: u16) -> Self {
Self { Self {
inner: IrqSafeSpinlock::new(Inner { inner: IrqSafeSpinlock::new(Inner {
dr: IoPort::new(base), 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 { impl ComPort {
/// Constructs a COM port pair
pub const fn new(port_a: u16, port_b: u16, irq: IrqNumber) -> Self { pub const fn new(port_a: u16, port_b: u16, irq: IrqNumber) -> Self {
Self { Self {
port_a: Port::new(port_a), 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 { pub fn port_a(&self) -> &Port {
&self.port_a &self.port_a
} }

View File

@ -2,8 +2,9 @@
use core::fmt::{self, Arguments}; use core::fmt::{self, Arguments};
use abi::error::Error; 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; const MAX_DEBUG_SINKS: usize = 4;

View File

@ -1,3 +1,4 @@
//! Bus devices //! Bus devices
#[cfg(feature = "device-tree")]
pub mod simple_bus; pub mod simple_bus;

View File

@ -1,6 +1,6 @@
//! Simple "passthrough" bus device //! Simple "passthrough" bus device
use crate::{arch::aarch64::devtree::DevTreeIndexNodeExt, device, device_tree_driver}; use crate::{device::devtree, device_tree_driver};
device_tree_driver! { device_tree_driver! {
compatible: ["simple-bus"], compatible: ["simple-bus"],
@ -11,8 +11,8 @@ device_tree_driver! {
let nodes = dt.node.children(); let nodes = dt.node.children();
// Iterate devices on the bus // Iterate devices on the bus
device::enumerate_dt(address_cells, size_cells, nodes, |_, probe| { devtree::enumerate_dt(address_cells, size_cells, nodes, |_, probe| {
if let Some((device, _)) = device::probe_dt_node(&probe) { if let Some((device, _)) = devtree::probe_dt_node(&probe) {
unsafe { unsafe {
device.init()?; device.init()?;
} }

454
src/device/devtree.rs Normal file
View File

@ -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<alloc::boxed::Box<dyn device_api::Device>> $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<u64>;
/// 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<u64>;
}
/// 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<usize>;
/// Returns the #size-cells property of the node, if there is one
fn get_size_cells(&self) -> Option<usize>;
/// 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<T> {
/// Returns a property value of given type, if it exists
fn prop(&self, name: &str) -> Option<T>;
}
impl<'a, 'i, 'dt> DevTreeIndexNodePropGet<u32> for DevTreeIndexNode<'a, 'i, 'dt> {
fn prop(&self, name: &str) -> Option<u32> {
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<TNode> {
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<usize> {
self.props()
.find(|p| p.name().unwrap_or("") == "#address-cells")
.map(|p| p.u32(0).unwrap() as usize)
}
fn get_size_cells(&self) -> Option<usize> {
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<u64> {
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<u64> {
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<Self::Item> {
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<TProp<'a>> {
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<TNode<'a>> {
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<Box<dyn Device>>,
}
/// 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<Item = DevTreeProbe<'a>> {
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::<u64>() * 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<DevTreeProbe> {
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<Item = DevTreeIndexNode<'a, 'a, 'a>>,
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)
}
}

View File

@ -4,6 +4,7 @@ use core::mem::size_of;
use abi::{error::Error, primitive_enum}; use abi::{error::Error, primitive_enum};
use alloc::vec::Vec; use alloc::vec::Vec;
use bitflags::bitflags; use bitflags::bitflags;
use kernel_util::util::StaticVector;
use crate::{ use crate::{
debug::DebugSink, debug::DebugSink,
@ -13,7 +14,6 @@ use crate::{
}, },
sync::IrqSafeSpinlock, sync::IrqSafeSpinlock,
task::tasklet::TaskFlow, task::tasklet::TaskFlow,
util::StaticVector,
}; };
const CONSOLE_ROW_LEN: usize = 128; const CONSOLE_ROW_LEN: usize = 128;
@ -23,36 +23,31 @@ const DEFAULT_FG_COLOR: ColorAttribute = ColorAttribute::White;
const DEFAULT_BG_COLOR: ColorAttribute = ColorAttribute::Blue; const DEFAULT_BG_COLOR: ColorAttribute = ColorAttribute::Blue;
primitive_enum! { primitive_enum! {
#[allow(missing_docs)]
#[doc = "Color attribute of a console character"] #[doc = "Color attribute of a console character"]
pub enum ColorAttribute: u8 { pub enum ColorAttribute: u8 {
#[doc = "..."]
Black = 0, Black = 0,
#[doc = "..."]
Red = 1, Red = 1,
#[doc = "..."]
Green = 2, Green = 2,
#[doc = "..."]
Yellow = 3, Yellow = 3,
#[doc = "..."]
Blue = 4, Blue = 4,
#[doc = "..."]
Magenta = 5, Magenta = 5,
#[doc = "..."]
Cyan = 6, Cyan = 6,
#[doc = "..."]
White = 7, White = 7,
} }
} }
bitflags! { bitflags! {
#[doc = "Extra attributes of a console character"]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct Attributes: u8 { pub struct Attributes: u8 {
#[allow(missing_docs)]
const BOLD = 1 << 0; const BOLD = 1 << 0;
} }
} }
impl ColorAttribute { impl ColorAttribute {
pub fn from_vt100(val: u8) -> Self { fn from_vt100(val: u8) -> Self {
match val { match val {
0..=7 => Self::try_from(val).unwrap(), 0..=7 => Self::try_from(val).unwrap(),
_ => ColorAttribute::Red, _ => ColorAttribute::Red,
@ -102,7 +97,7 @@ pub struct ConsoleBuffer {
height: u32, height: u32,
} }
pub enum EscapeState { enum EscapeState {
Normal, Normal,
Escape, Escape,
Csi, Csi,
@ -116,7 +111,9 @@ pub struct ConsoleState {
pub cursor_col: u32, pub cursor_col: u32,
/// Current foreground color /// Current foreground color
pub fg_color: ColorAttribute, pub fg_color: ColorAttribute,
/// Current background color
pub bg_color: ColorAttribute, pub bg_color: ColorAttribute,
/// Current set of attributes
pub attributes: Attributes, pub attributes: Attributes,
esc_args: StaticVector<u32, MAX_CSI_ARGS>, esc_args: StaticVector<u32, MAX_CSI_ARGS>,
@ -177,6 +174,7 @@ impl ConsoleChar {
} }
} }
/// Returns the attributes of the character
#[inline] #[inline]
pub fn attributes(self) -> (ColorAttribute, ColorAttribute, Attributes) { pub fn attributes(self) -> (ColorAttribute, ColorAttribute, Attributes) {
let fg = let fg =
@ -256,8 +254,20 @@ impl ConsoleRow {
impl ConsoleBuffer { impl ConsoleBuffer {
/// Constructs a fixed-size console buffer /// Constructs a fixed-size console buffer
pub const fn fixed(rows: &'static mut [ConsoleRow], height: u32) -> Self { pub fn new(height: u32) -> Result<Self, Error> {
Self { rows, height } let size = size_of::<ConsoleRow>() * (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 /// Reallocates the internal buffer with a new size
@ -394,12 +404,12 @@ impl ConsoleState {
// Move back one character // Move back one character
b'D' => { b'D' => {
if self.cursor_col > 0 { if self.cursor_col > 0 {
self.cursor_col = self.cursor_col - 1; self.cursor_col -= 1;
} }
} }
// Manipulate display attributes // Manipulate display attributes
b'm' => { b'm' => {
if let Some(arg) = self.esc_args.get(0) { if let Some(arg) = self.esc_args.first() {
match arg { match arg {
// Reset // Reset
0 => { 0 => {

View File

@ -112,11 +112,12 @@ impl FramebufferConsole {
/// Constructs an instance of console from its framebuffer reference /// Constructs an instance of console from its framebuffer reference
pub fn from_framebuffer( pub fn from_framebuffer(
framebuffer: &'static LinearFramebuffer, framebuffer: &'static LinearFramebuffer,
font: &'static BitmapFont<'static>, font: Option<&'static BitmapFont<'static>>,
buffer: ConsoleBuffer, buffer: ConsoleBuffer,
) -> Self { ) -> Self {
let char_width = 6; let font = font.unwrap_or(&bitmap_font::tamzen::FONT_7x14);
let char_height = 12; let char_width = font.width();
let char_height = font.height();
let dim = framebuffer.dimensions(); let dim = framebuffer.dimensions();
let inner = Inner { let inner = Inner {
@ -151,12 +152,6 @@ impl Inner {
text.draw(self).ok(); 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) { fn fill_rect(&mut self, x: u32, y: u32, w: u32, h: u32, val: u32) {
let mut fb = unsafe { self.framebuffer.lock() }; let mut fb = unsafe { self.framebuffer.lock() };

View File

@ -3,8 +3,9 @@
use core::ops::{Index, IndexMut}; use core::ops::{Index, IndexMut};
use abi::error::Error; 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}; use super::{DisplayDevice, DisplayDimensions};
@ -76,7 +77,7 @@ impl LinearFramebuffer {
} }
impl Device for LinearFramebuffer { impl Device for LinearFramebuffer {
fn name(&self) -> &'static str { fn display_name(&self) -> &'static str {
"Linear Framebuffer" "Linear Framebuffer"
} }
} }

View File

@ -3,6 +3,8 @@
use super::Device; use super::Device;
pub mod console; pub mod console;
#[cfg(feature = "fb_console")]
pub mod fb_console; pub mod fb_console;
pub mod linear_fb; pub mod linear_fb;

View File

@ -1,178 +1,20 @@
//! Device management and interfaces //! 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 device_api::{manager::DeviceManager, Device, DeviceId};
use fdt_rs::{index::DevTreeIndexNode, prelude::PropReader};
use crate::{ use crate::sync::{IrqSafeSpinlock, IrqSafeSpinlockGuard};
arch::aarch64::devtree,
sync::{IrqSafeSpinlock, IrqSafeSpinlockGuard}, #[cfg(target_arch = "aarch64")]
}; pub mod devtree;
pub mod bus; pub mod bus;
pub mod display;
pub mod power; pub mod power;
pub mod serial; pub mod serial;
pub mod tty; pub mod tty;
// pub mod display;
static DEVICE_MANAGER: IrqSafeSpinlock<DeviceManager> = IrqSafeSpinlock::new(DeviceManager::new()); static DEVICE_MANAGER: IrqSafeSpinlock<DeviceManager> = 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<alloc::boxed::Box<dyn device_api::Device>> $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<Box<dyn Device>>,
}
/// 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<Item = DevTreeProbe<'a>> {
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::<u64>() * 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<DevTreeProbe> {
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<Item = DevTreeIndexNode<'a, 'a, 'a>>,
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 /// 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 { pub fn register_device(device: &'static dyn Device) -> DeviceId {
debugln!("Register {:?}", device.display_name()); debugln!("Register {:?}", device.display_name());

View File

@ -6,10 +6,8 @@ use device_api::{CpuBringupDevice, Device, ResetDevice};
use fdt_rs::prelude::PropReader; use fdt_rs::prelude::PropReader;
use crate::{ use crate::{
arch::{ arch::{Architecture, ArchitectureImpl, ARCHITECTURE},
aarch64::devtree::{self}, device::devtree,
Architecture, ArchitectureImpl, ARCHITECTURE,
},
device_tree_driver, device_tree_driver,
}; };

View File

@ -1,4 +1,10 @@
//! Power-management related device drivers //! Power-management related device drivers
pub mod arm_psci; use cfg_if::cfg_if;
pub mod sunxi_rwdog;
cfg_if! {
if #[cfg(target_arch = "aarch64")] {
pub mod arm_psci;
pub mod sunxi_rwdog;
}
}

View File

@ -3,19 +3,17 @@
use abi::error::Error; use abi::error::Error;
use alloc::boxed::Box; use alloc::boxed::Box;
use device_api::{Device, ResetDevice}; use device_api::{Device, ResetDevice};
use kernel_util::util::OneTimeInit;
use tock_registers::{ use tock_registers::{
interfaces::Writeable, register_bitfields, register_structs, registers::ReadWrite, interfaces::Writeable, register_bitfields, register_structs, registers::ReadWrite,
}; };
use crate::{ use crate::{
arch::{ arch::{Architecture, ARCHITECTURE},
aarch64::devtree::{self, DevTreeIndexNodePropGet, DevTreeIndexPropExt}, device::devtree::{self, DevTreeIndexNodePropGet, DevTreeIndexPropExt},
Architecture, ARCHITECTURE,
},
device_tree_driver, device_tree_driver,
mem::device::DeviceMemoryIo, mem::device::DeviceMemoryIo,
sync::IrqSafeSpinlock, sync::IrqSafeSpinlock,
util::OneTimeInit,
}; };
register_bitfields! { register_bitfields! {

View File

@ -1,4 +1,10 @@
//! Serial device interfaces //! Serial device interfaces
pub mod pl011; use cfg_if::cfg_if;
pub mod sunxi_uart;
cfg_if! {
if #[cfg(target_arch = "aarch64")] {
pub mod pl011;
pub mod sunxi_uart;
}
}

View File

@ -1,11 +1,8 @@
//! ARM PL011 driver //! ARM PL011 driver
use abi::{error::Error, io::DeviceRequest}; use abi::{error::Error, io::DeviceRequest};
use alloc::boxed::Box; use alloc::boxed::Box;
use device_api::{ use device_api::{interrupt::InterruptHandler, serial::SerialDevice, Device};
interrupt::{InterruptHandler, IrqNumber}, use kernel_util::util::OneTimeInit;
serial::SerialDevice,
Device,
};
use tock_registers::{ use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable}, interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs, register_bitfields, register_structs,
@ -14,17 +11,16 @@ use tock_registers::{
use vfs::CharDevice; use vfs::CharDevice;
use crate::{ use crate::{
arch::{ arch::{aarch64::IrqNumber, Architecture, ARCHITECTURE},
aarch64::devtree::{self, DevTreeIndexPropExt},
Architecture, ARCHITECTURE,
},
debug::{self, DebugSink, LogLevel}, debug::{self, DebugSink, LogLevel},
device::tty::{CharRing, TtyDevice}, device::{
devtree::{self, DevTreeIndexPropExt},
tty::{CharRing, TtyDevice},
},
device_tree_driver, device_tree_driver,
fs::devfs::{self, CharDeviceType}, fs::devfs::{self, CharDeviceType},
mem::device::DeviceMemoryIo, mem::device::DeviceMemoryIo,
sync::IrqSafeSpinlock, sync::IrqSafeSpinlock,
util::OneTimeInit,
}; };
register_bitfields! { register_bitfields! {

View File

@ -2,11 +2,8 @@
use abi::{error::Error, io::DeviceRequest}; use abi::{error::Error, io::DeviceRequest};
use alloc::boxed::Box; use alloc::boxed::Box;
use device_api::{ use device_api::{interrupt::InterruptHandler, serial::SerialDevice, Device};
interrupt::{InterruptHandler, IrqNumber}, use kernel_util::util::OneTimeInit;
serial::SerialDevice,
Device,
};
use tock_registers::{ use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable}, interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs, register_bitfields, register_structs,
@ -15,17 +12,16 @@ use tock_registers::{
use vfs::CharDevice; use vfs::CharDevice;
use crate::{ use crate::{
arch::{ arch::{aarch64::IrqNumber, Architecture, ARCHITECTURE},
aarch64::devtree::{self, DevTreeIndexPropExt},
Architecture, ARCHITECTURE,
},
debug::{self, DebugSink, LogLevel}, debug::{self, DebugSink, LogLevel},
device::tty::{CharRing, TtyDevice}, device::{
devtree::{self, DevTreeIndexPropExt},
tty::{CharRing, TtyDevice},
},
device_tree_driver, device_tree_driver,
fs::devfs::{self, CharDeviceType}, fs::devfs::{self, CharDeviceType},
mem::device::DeviceMemoryIo, mem::device::DeviceMemoryIo,
sync::IrqSafeSpinlock, sync::IrqSafeSpinlock,
util::OneTimeInit,
}; };
register_bitfields! { register_bitfields! {

View File

@ -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<Duration, Error>;
}

View File

@ -14,13 +14,13 @@ use crate::{
#[cfg(feature = "fb_console")] #[cfg(feature = "fb_console")]
pub mod combined { pub mod combined {
//! Combined console + keyboard terminal device
use abi::{error::Error, io::DeviceRequest}; use abi::{error::Error, io::DeviceRequest};
use device_api::{input::KeyboardConsumer, serial::SerialDevice};
use vfs::CharDevice; use vfs::CharDevice;
use crate::device::{ use crate::device::{
display::{console::DisplayConsole, fb_console::FramebufferConsole}, display::{console::DisplayConsole, fb_console::FramebufferConsole},
input::KeyboardDevice,
serial::SerialDevice,
Device, Device,
}; };
@ -29,21 +29,15 @@ pub mod combined {
// TODO rewrite this // TODO rewrite this
/// Helper device to combine a display and a keyboard input into a single terminal /// Helper device to combine a display and a keyboard input into a single terminal
pub struct CombinedTerminal { pub struct CombinedTerminal {
#[allow(dead_code)] output: &'static (dyn DisplayConsole + Sync + 'static),
input: &'static dyn KeyboardDevice,
output: &'static dyn DisplayConsole,
input_ring: CharRing<16>, input_ring: CharRing<16>,
} }
impl CombinedTerminal { impl CombinedTerminal {
/// Create a combined terminal device from a keyboard and console output devices /// Create a combined terminal device from a keyboard and console output devices
pub fn new( pub fn new(output: &'static FramebufferConsole) -> Self {
input: &'static dyn KeyboardDevice,
output: &'static FramebufferConsole,
) -> Self {
Self { Self {
input,
output, output,
input_ring: CharRing::new(), input_ring: CharRing::new(),
} }
@ -56,11 +50,14 @@ pub mod combined {
} }
} }
impl SerialDevice for CombinedTerminal { impl KeyboardConsumer for CombinedTerminal {
fn receive(&self, _blocking: bool) -> Result<u8, Error> { fn handle_character(&self, data: u8) -> Result<(), Error> {
todo!() self.recv_byte(data);
Ok(())
} }
}
impl SerialDevice for CombinedTerminal {
fn send(&self, byte: u8) -> Result<(), Error> { fn send(&self, byte: u8) -> Result<(), Error> {
self.output.write_char(byte); self.output.write_char(byte);
Ok(()) Ok(())
@ -68,7 +65,7 @@ pub mod combined {
} }
impl Device for CombinedTerminal { impl Device for CombinedTerminal {
fn name(&self) -> &'static str { fn display_name(&self) -> &'static str {
"Combined terminal device" "Combined terminal device"
} }
} }

View File

@ -6,12 +6,11 @@ use abi::{
io::{FileAttr, FileMode, FileType, OpenOptions}, io::{FileAttr, FileMode, FileType, OpenOptions},
}; };
use alloc::{boxed::Box, format, string::String}; use alloc::{boxed::Box, format, string::String};
use kernel_util::util::OneTimeInit;
use vfs::{ use vfs::{
CharDevice, CharDeviceWrapper, Vnode, VnodeImpl, VnodeKind, VnodeRef, DIR_POSITION_FROM_CACHE, CharDevice, CharDeviceWrapper, Vnode, VnodeImpl, VnodeKind, VnodeRef, DIR_POSITION_FROM_CACHE,
}; };
use crate::util::OneTimeInit;
/// Describes the kind of a character device /// Describes the kind of a character device
#[derive(Debug)] #[derive(Debug)]
pub enum CharDeviceType { pub enum CharDeviceType {
@ -24,16 +23,11 @@ pub enum CharDeviceType {
struct DevfsDirectory; struct DevfsDirectory;
impl VnodeImpl for DevfsDirectory { impl VnodeImpl for DevfsDirectory {
fn open( fn open(&self, _node: &VnodeRef, _opts: OpenOptions, _mode: FileMode) -> Result<u64, Error> {
&mut self,
_node: &VnodeRef,
_opts: OpenOptions,
_mode: FileMode,
) -> Result<u64, Error> {
Ok(DIR_POSITION_FROM_CACHE) Ok(DIR_POSITION_FROM_CACHE)
} }
fn metadata(&mut self, _node: &VnodeRef) -> Result<FileAttr, Error> { fn metadata(&self, _node: &VnodeRef) -> Result<FileAttr, Error> {
Ok(FileAttr { Ok(FileAttr {
size: 0, size: 0,
mode: FileMode::default_dir(), mode: FileMode::default_dir(),

View File

@ -2,17 +2,15 @@
use core::ptr::NonNull; use core::ptr::NonNull;
use kernel_util::util::OneTimeInit;
use memfs::block::{self, BlockAllocator}; use memfs::block::{self, BlockAllocator};
use vfs::VnodeRef; use vfs::VnodeRef;
use yggdrasil_abi::{error::Error, io::MountOptions}; use yggdrasil_abi::{error::Error, io::MountOptions};
use crate::{ use crate::mem::{
mem::{ self,
self, phys::{self, PageUsage},
phys::{self, PageUsage}, ConvertAddress,
ConvertAddress,
},
util::OneTimeInit,
}; };
pub mod devfs; pub mod devfs;

View File

@ -30,8 +30,8 @@ pub fn kinit() {
{ {
use core::time::Duration; use core::time::Duration;
use device::display::console::task_update_consoles; use crate::device::display::console::task_update_consoles;
use task::tasklet; use crate::task::tasklet;
tasklet::add_periodic( tasklet::add_periodic(
"update-console", "update-console",

View File

@ -9,7 +9,8 @@
arbitrary_self_types, arbitrary_self_types,
const_mut_refs, const_mut_refs,
let_chains, let_chains,
linked_list_cursors linked_list_cursors,
rustc_private
)] )]
#![allow(clippy::new_without_default, clippy::fn_to_numeric_cast)] #![allow(clippy::new_without_default, clippy::fn_to_numeric_cast)]
#![warn(missing_docs)] #![warn(missing_docs)]
@ -17,7 +18,7 @@
#![no_main] #![no_main]
use sync::SpinFence; use sync::SpinFence;
use task::{context::Cpu, spawn_kernel_closure}; use task::spawn_kernel_closure;
use crate::{ use crate::{
arch::{Architecture, ArchitectureImpl, ARCHITECTURE}, arch::{Architecture, ArchitectureImpl, ARCHITECTURE},
@ -27,6 +28,7 @@ use crate::{
extern crate yggdrasil_abi as abi; extern crate yggdrasil_abi as abi;
extern crate alloc; extern crate alloc;
extern crate compiler_builtins;
#[macro_use] #[macro_use]
pub mod debug; pub mod debug;
@ -42,7 +44,6 @@ pub mod proc;
pub mod sync; pub mod sync;
pub mod syscall; pub mod syscall;
pub mod task; pub mod task;
pub mod util;
static CPU_INIT_FENCE: SpinFence = SpinFence::new(); static CPU_INIT_FENCE: SpinFence = SpinFence::new();
@ -75,7 +76,7 @@ pub fn kernel_main() -> ! {
ARCHITECTURE.start_application_processors(); ARCHITECTURE.start_application_processors();
} }
Cpu::init_ipi_queues(); // Cpu::init_ipi_queues();
// Wait until all APs initialize // Wait until all APs initialize
CPU_INIT_FENCE.signal(); CPU_INIT_FENCE.signal();

View File

@ -286,70 +286,20 @@ pub fn validate_user_region(
#[no_mangle] #[no_mangle]
unsafe extern "C" fn memcpy(p0: *mut c_void, p1: *const c_void, len: usize) -> *mut c_void { unsafe extern "C" fn memcpy(p0: *mut c_void, p1: *const c_void, len: usize) -> *mut c_void {
let mut offset = 0; compiler_builtins::mem::memcpy(p0 as _, p1 as _, len) as _
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
} }
#[no_mangle] #[no_mangle]
unsafe extern "C" fn memcmp(p0: *const c_void, p1: *const c_void, len: usize) -> i32 { unsafe extern "C" fn memcmp(p0: *const c_void, p1: *const c_void, len: usize) -> i32 {
let mut offset = 0; compiler_builtins::mem::memcmp(p0 as _, p1 as _, len)
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
} }
#[no_mangle] #[no_mangle]
unsafe extern "C" fn memmove(dst: *mut c_void, src: *const c_void, len: usize) -> *mut c_void { 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 { compiler_builtins::mem::memmove(dst as _, src as _, len) as _
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
} }
#[no_mangle] #[no_mangle]
unsafe extern "C" fn memset(dst: *mut c_void, val: i32, len: usize) -> *mut c_void { unsafe extern "C" fn memset(dst: *mut c_void, val: i32, len: usize) -> *mut c_void {
let mut offset = 0; compiler_builtins::mem::memset(dst as _, val, len) as _
while offset < len {
(dst as *mut u8).add(offset).write_volatile(val as u8);
offset += 1;
}
dst
} }

View File

@ -2,6 +2,7 @@
use core::{iter::StepBy, mem::size_of, ops::Range}; use core::{iter::StepBy, mem::size_of, ops::Range};
use abi::error::Error; use abi::error::Error;
use kernel_util::util::OneTimeInit;
use crate::{ use crate::{
debug::LogLevel, debug::LogLevel,
@ -10,7 +11,6 @@ use crate::{
ConvertAddress, /*, KERNEL_PHYS_BASE */ ConvertAddress, /*, KERNEL_PHYS_BASE */
}, },
sync::IrqSafeSpinlock, sync::IrqSafeSpinlock,
util::OneTimeInit,
}; };
use self::manager::PhysicalMemoryManager; use self::manager::PhysicalMemoryManager;

View File

@ -1,6 +1,6 @@
//! Utilities for handling reserved memory regions //! Utilities for handling reserved memory regions
use crate::util::StaticVector; use kernel_util::util::StaticVector;
use super::PhysicalMemoryRegion; use super::PhysicalMemoryRegion;

View File

@ -11,6 +11,7 @@ use abi::{
}; };
use alloc::{collections::VecDeque, rc::Rc, string::String}; use alloc::{collections::VecDeque, rc::Rc, string::String};
use atomic_enum::atomic_enum; use atomic_enum::atomic_enum;
use kernel_util::util::OneTimeInit;
use vfs::VnodeRef; use vfs::VnodeRef;
use crate::{ use crate::{
@ -22,7 +23,6 @@ use crate::{
}, },
sync::{IrqGuard, IrqSafeSpinlock}, sync::{IrqGuard, IrqSafeSpinlock},
task::context::TaskContextImpl, task::context::TaskContextImpl,
util::OneTimeInit,
}; };
use super::{context::TaskFrame, sched::CpuQueue, Cpu, ProcessId, TaskContext, PROCESSES}; use super::{context::TaskFrame, sched::CpuQueue, Cpu, ProcessId, TaskContext, PROCESSES};

View File

@ -3,12 +3,12 @@
// use aarch64_cpu::registers::CNTPCT_EL0; // use aarch64_cpu::registers::CNTPCT_EL0;
use alloc::{collections::VecDeque, rc::Rc, vec::Vec}; use alloc::{collections::VecDeque, rc::Rc, vec::Vec};
use cfg_if::cfg_if; use cfg_if::cfg_if;
use kernel_util::util::OneTimeInit;
use crate::{ use crate::{
// arch::aarch64::{context::TaskContext, cpu::Cpu}, // arch::aarch64::{context::TaskContext, cpu::Cpu},
arch::{Architecture, ArchitectureImpl}, arch::{Architecture, ArchitectureImpl},
sync::{IrqSafeSpinlock, IrqSafeSpinlockGuard}, sync::{IrqSafeSpinlock, IrqSafeSpinlockGuard},
util::OneTimeInit,
}; };
use super::{ use super::{