x86-64: sync x86-64 up with the new kernel structure
This commit is contained in:
parent
b0379e398f
commit
af5529a84f
@ -11,6 +11,7 @@ yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
|
||||
vfs = { path = "lib/vfs" }
|
||||
memfs = { path = "lib/memfs" }
|
||||
device-api = { path = "lib/device-api", features = ["derive"] }
|
||||
kernel-util = { path = "lib/kernel-util" }
|
||||
|
||||
atomic_enum = "0.2.0"
|
||||
bitflags = "2.3.3"
|
||||
@ -21,12 +22,10 @@ tock-registers = "0.8.1"
|
||||
cfg-if = "1.0.0"
|
||||
git-version = "0.3.5"
|
||||
|
||||
aarch64-cpu = "9.3.1"
|
||||
|
||||
bitmap-font = { version = "0.3.0", optional = true }
|
||||
embedded-graphics = { version = "0.8.0", optional = true }
|
||||
|
||||
fdt-rs = { version = "0.4.3", default-features = false }
|
||||
log = "0.4.20"
|
||||
|
||||
[dependencies.elf]
|
||||
version = "0.7.2"
|
||||
@ -35,6 +34,8 @@ default-features = false
|
||||
features = ["no_std_stream"]
|
||||
|
||||
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
||||
aarch64-cpu = "9.3.1"
|
||||
fdt-rs = { version = "0.4.3", default-features = false }
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||
yboot-proto = { git = "https://git.alnyan.me/yggdrasil/yboot-proto.git" }
|
||||
|
11
lib/device-api/src/input.rs
Normal file
11
lib/device-api/src/input.rs
Normal 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);
|
||||
}
|
@ -27,11 +27,11 @@ pub enum IpiDeliveryTarget {
|
||||
OtherCpus,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum IrqNumber {
|
||||
Private(u32),
|
||||
Shared(u32),
|
||||
}
|
||||
// #[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
// pub enum IrqNumber {
|
||||
// Private(u32),
|
||||
// Shared(u32),
|
||||
// }
|
||||
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
pub struct IrqOptions {
|
||||
@ -44,17 +44,19 @@ pub trait InterruptTable {
|
||||
}
|
||||
|
||||
pub trait ExternalInterruptController {
|
||||
type IrqNumber;
|
||||
|
||||
/// Performs IRQ delivery method configuration and registers a handler to execute when it is
|
||||
/// fired
|
||||
fn register_irq(
|
||||
&self,
|
||||
irq: IrqNumber,
|
||||
irq: Self::IrqNumber,
|
||||
options: IrqOptions,
|
||||
handler: &'static dyn InterruptHandler,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
/// Enables the specified IRQ (unmasks it)
|
||||
fn enable_irq(&self, irq: IrqNumber) -> Result<(), Error>;
|
||||
fn enable_irq(&self, irq: Self::IrqNumber) -> Result<(), Error>;
|
||||
|
||||
/// Handles a single pending interrupt on this controller.
|
||||
/// The function is intended for interrupt controllers which have internal registers to track
|
||||
@ -107,6 +109,15 @@ impl<const SIZE: usize> FixedInterruptTable<SIZE> {
|
||||
self.entries[index] = Some(handler);
|
||||
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> {
|
||||
|
@ -4,6 +4,7 @@ extern crate alloc;
|
||||
|
||||
pub mod bus;
|
||||
pub mod device;
|
||||
pub mod input;
|
||||
pub mod interrupt;
|
||||
pub mod manager;
|
||||
pub mod serial;
|
||||
@ -24,5 +25,11 @@ pub trait CpuBringupDevice: Device {
|
||||
}
|
||||
|
||||
pub trait ResetDevice: Device {
|
||||
/// Performs a system reset.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The kernel must ensure it is actually safe to perform a reset, no critical operations
|
||||
/// are aborted and no data is lost.
|
||||
unsafe fn reset(&self) -> !;
|
||||
}
|
||||
|
8
lib/kernel-util/Cargo.toml
Normal file
8
lib/kernel-util/Cargo.toml
Normal 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]
|
5
lib/kernel-util/src/lib.rs
Normal file
5
lib/kernel-util/src/lib.rs
Normal file
@ -0,0 +1,5 @@
|
||||
#![no_std]
|
||||
#![feature(maybe_uninit_slice)]
|
||||
|
||||
pub mod sync;
|
||||
pub mod util;
|
0
lib/kernel-util/src/sync.rs
Normal file
0
lib/kernel-util/src/sync.rs
Normal file
@ -1,4 +1,4 @@
|
||||
use core::marker::PhantomData;
|
||||
use core::{cell::RefCell, marker::PhantomData};
|
||||
|
||||
use alloc::boxed::Box;
|
||||
|
||||
@ -15,12 +15,12 @@ pub(crate) struct DirectoryNode<A: BlockAllocator> {
|
||||
}
|
||||
|
||||
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);
|
||||
match kind {
|
||||
VnodeKind::Directory => child.set_data(Box::new(Self { _pd: PhantomData })),
|
||||
VnodeKind::Regular => child.set_data(Box::new(FileNode {
|
||||
data: BVec::<A>::new(),
|
||||
data: RefCell::new(BVec::<A>::new()),
|
||||
})),
|
||||
_ => todo!(),
|
||||
}
|
||||
@ -28,24 +28,19 @@ impl<A: BlockAllocator> VnodeImpl for DirectoryNode<A> {
|
||||
Ok(child)
|
||||
}
|
||||
|
||||
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> {
|
||||
Ok(DIR_POSITION_FROM_CACHE)
|
||||
}
|
||||
|
||||
fn remove(&mut self, _at: &VnodeRef, _name: &str) -> Result<(), Error> {
|
||||
fn remove(&self, _at: &VnodeRef, _name: &str) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn size(&mut self, node: &VnodeRef) -> Result<u64, Error> {
|
||||
fn size(&self, node: &VnodeRef) -> Result<u64, Error> {
|
||||
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 {
|
||||
size: 0,
|
||||
mode: FileMode::default_dir(),
|
||||
|
@ -1,3 +1,5 @@
|
||||
use core::cell::RefCell;
|
||||
|
||||
use vfs::{VnodeImpl, VnodeRef};
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
@ -7,37 +9,37 @@ use yggdrasil_abi::{
|
||||
use crate::{block::BlockAllocator, bvec::BVec};
|
||||
|
||||
pub(crate) struct FileNode<A: BlockAllocator> {
|
||||
pub(crate) data: BVec<'static, A>,
|
||||
pub(crate) data: RefCell<BVec<'static, 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) {
|
||||
Ok(self.data.size() as u64)
|
||||
Ok(self.data.borrow().size() as u64)
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
fn close(&mut self, _node: &VnodeRef) -> Result<(), Error> {
|
||||
fn close(&self, _node: &VnodeRef) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(&mut self, _node: &VnodeRef, pos: u64, data: &mut [u8]) -> Result<usize, Error> {
|
||||
self.data.read(pos, data)
|
||||
fn read(&self, _node: &VnodeRef, pos: u64, data: &mut [u8]) -> Result<usize, Error> {
|
||||
self.data.borrow().read(pos, data)
|
||||
}
|
||||
|
||||
fn write(&mut self, _node: &VnodeRef, pos: u64, data: &[u8]) -> Result<usize, Error> {
|
||||
self.data.write(pos, data)
|
||||
fn write(&self, _node: &VnodeRef, pos: u64, data: &[u8]) -> Result<usize, Error> {
|
||||
self.data.borrow_mut().write(pos, data)
|
||||
}
|
||||
|
||||
fn size(&mut self, _node: &VnodeRef) -> Result<u64, Error> {
|
||||
Ok(self.data.size() as u64)
|
||||
fn size(&self, _node: &VnodeRef) -> Result<u64, Error> {
|
||||
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 {
|
||||
size: self.data.size() as u64,
|
||||
size: self.data.borrow().size() as u64,
|
||||
mode: FileMode::default_file(),
|
||||
ty: FileType::File,
|
||||
})
|
||||
|
@ -162,7 +162,9 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
|
||||
|
||||
let bvec = BVec::<A>::try_from(data)?;
|
||||
assert_eq!(bvec.size(), data.len());
|
||||
node.set_data(Box::new(FileNode { data: bvec }));
|
||||
node.set_data(Box::new(FileNode {
|
||||
data: RefCell::new(bvec),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,4 +7,5 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
|
||||
kernel-util = { path = "../kernel-util" }
|
||||
bitflags = "2.3.3"
|
||||
|
@ -23,28 +23,23 @@ impl CharDeviceWrapper {
|
||||
}
|
||||
|
||||
impl VnodeImpl for CharDeviceWrapper {
|
||||
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> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&mut self, _node: &VnodeRef) -> Result<(), Error> {
|
||||
fn close(&self, _node: &VnodeRef) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read(&mut self, _node: &VnodeRef, _pos: u64, data: &mut [u8]) -> Result<usize, Error> {
|
||||
fn read(&self, _node: &VnodeRef, _pos: u64, data: &mut [u8]) -> Result<usize, Error> {
|
||||
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)
|
||||
}
|
||||
|
||||
fn metadata(&mut self, _node: &VnodeRef) -> Result<FileAttr, Error> {
|
||||
fn metadata(&self, _node: &VnodeRef) -> Result<FileAttr, Error> {
|
||||
Ok(FileAttr {
|
||||
size: 0,
|
||||
ty: FileType::Char,
|
||||
@ -52,7 +47,7 @@ impl VnodeImpl for CharDeviceWrapper {
|
||||
})
|
||||
}
|
||||
|
||||
fn device_request(&mut self, _node: &VnodeRef, req: &mut DeviceRequest) -> Result<(), Error> {
|
||||
fn device_request(&self, _node: &VnodeRef, req: &mut DeviceRequest) -> Result<(), Error> {
|
||||
self.device.device_request(req)
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ use alloc::{
|
||||
string::String,
|
||||
vec::Vec,
|
||||
};
|
||||
use kernel_util::util::OneTimeInit;
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{DeviceRequest, FileAttr, FileMode, FileType, OpenOptions},
|
||||
@ -44,46 +45,46 @@ pub struct Vnode {
|
||||
name: String,
|
||||
tree: RefCell<TreeNode>,
|
||||
kind: VnodeKind,
|
||||
data: RefCell<Option<Box<dyn VnodeImpl>>>,
|
||||
data: OneTimeInit<Box<dyn VnodeImpl>>,
|
||||
fs: RefCell<Option<Rc<dyn Filesystem>>>,
|
||||
target: RefCell<Option<VnodeRef>>,
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
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)
|
||||
}
|
||||
|
||||
fn remove(&mut self, at: &VnodeRef, name: &str) -> Result<(), Error> {
|
||||
fn remove(&self, at: &VnodeRef, name: &str) -> Result<(), Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn open(&mut self, node: &VnodeRef, opts: OpenOptions, mode: FileMode) -> Result<u64, Error> {
|
||||
fn open(&self, node: &VnodeRef, opts: OpenOptions, mode: FileMode) -> Result<u64, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn close(&mut self, node: &VnodeRef) -> Result<(), Error> {
|
||||
fn close(&self, node: &VnodeRef) -> Result<(), Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn read(&mut self, node: &VnodeRef, pos: u64, data: &mut [u8]) -> Result<usize, Error> {
|
||||
fn read(&self, node: &VnodeRef, pos: u64, data: &mut [u8]) -> Result<usize, Error> {
|
||||
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)
|
||||
}
|
||||
|
||||
fn size(&mut self, node: &VnodeRef) -> Result<u64, Error> {
|
||||
fn size(&self, node: &VnodeRef) -> Result<u64, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn metadata(&mut self, node: &VnodeRef) -> Result<FileAttr, Error> {
|
||||
fn metadata(&self, node: &VnodeRef) -> Result<FileAttr, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn device_request(&mut self, node: &VnodeRef, req: &mut DeviceRequest) -> Result<(), Error> {
|
||||
fn device_request(&self, node: &VnodeRef, req: &mut DeviceRequest) -> Result<(), Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
}
|
||||
@ -97,7 +98,7 @@ impl Vnode {
|
||||
children: Vec::new(),
|
||||
}),
|
||||
kind,
|
||||
data: RefCell::new(None),
|
||||
data: OneTimeInit::new(),
|
||||
fs: RefCell::new(None),
|
||||
target: RefCell::new(None),
|
||||
})
|
||||
@ -119,14 +120,18 @@ impl Vnode {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn data(&self) -> RefMut<Option<Box<dyn VnodeImpl>>> {
|
||||
match self.data.try_borrow_mut() {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
panic!("{:?} on {:?}", e, self.name())
|
||||
}
|
||||
}
|
||||
pub fn data(&self) -> Option<&dyn VnodeImpl> {
|
||||
self.data.try_get().map(Box::as_ref)
|
||||
}
|
||||
// #[inline]
|
||||
// pub fn data(&self) -> RefMut<Option<Box<dyn VnodeImpl>>> {
|
||||
// match self.data.try_borrow_mut() {
|
||||
// Ok(r) => r,
|
||||
// Err(e) => {
|
||||
// panic!("{:?} on {:?}", e, self.name())
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
#[inline]
|
||||
pub fn fs(&self) -> Option<Rc<dyn Filesystem>> {
|
||||
@ -151,7 +156,8 @@ impl Vnode {
|
||||
}
|
||||
|
||||
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>) {
|
||||
@ -273,7 +279,7 @@ impl Vnode {
|
||||
return Err(Error::IsADirectory);
|
||||
}
|
||||
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
if let Some(data) = self.data() {
|
||||
let pos = data.open(self, flags, mode)?;
|
||||
Ok(File::normal(self.clone(), pos, open_flags))
|
||||
} else {
|
||||
@ -286,7 +292,7 @@ impl Vnode {
|
||||
return Err(Error::IsADirectory);
|
||||
}
|
||||
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
if let Some(data) = self.data() {
|
||||
let pos = data.open(self, OpenOptions::READ, FileMode::empty())?;
|
||||
Ok(File::directory(self.clone(), pos))
|
||||
} else {
|
||||
@ -296,7 +302,7 @@ impl Vnode {
|
||||
}
|
||||
|
||||
pub fn close(self: &VnodeRef) -> Result<(), Error> {
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
if let Some(data) = self.data() {
|
||||
data.close(self)
|
||||
} else {
|
||||
Ok(())
|
||||
@ -317,7 +323,7 @@ impl Vnode {
|
||||
e => return e,
|
||||
};
|
||||
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
if let Some(data) = self.data() {
|
||||
let vnode = data.create(self, name, kind)?;
|
||||
if let Some(fs) = self.fs() {
|
||||
vnode.set_fs(fs);
|
||||
@ -350,7 +356,7 @@ impl Vnode {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
if let Some(data) = self.data() {
|
||||
data.remove(self, name)?;
|
||||
}
|
||||
|
||||
@ -365,7 +371,7 @@ impl Vnode {
|
||||
todo!();
|
||||
}
|
||||
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
if let Some(data) = self.data() {
|
||||
data.write(self, pos, buf)
|
||||
} else {
|
||||
todo!()
|
||||
@ -377,7 +383,7 @@ impl Vnode {
|
||||
todo!();
|
||||
}
|
||||
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
if let Some(data) = self.data() {
|
||||
data.read(self, pos, buf)
|
||||
} else {
|
||||
todo!()
|
||||
@ -385,7 +391,7 @@ impl Vnode {
|
||||
}
|
||||
|
||||
pub fn size(self: &VnodeRef) -> Result<u64, Error> {
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
if let Some(data) = self.data() {
|
||||
data.size(self)
|
||||
} else {
|
||||
todo!();
|
||||
@ -435,7 +441,7 @@ impl Vnode {
|
||||
}
|
||||
|
||||
pub fn metadata(self: &VnodeRef) -> Result<FileAttr, Error> {
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
if let Some(data) = self.data() {
|
||||
data.metadata(self)
|
||||
} else {
|
||||
todo!()
|
||||
@ -443,7 +449,7 @@ impl Vnode {
|
||||
}
|
||||
|
||||
pub fn device_request(self: &VnodeRef, req: &mut DeviceRequest) -> Result<(), Error> {
|
||||
if let Some(ref mut data) = *self.data() {
|
||||
if let Some(data) = self.data() {
|
||||
data.device_request(self, req)
|
||||
} else {
|
||||
todo!()
|
||||
|
@ -100,7 +100,6 @@ unsafe extern "C" fn __aarch64_bsp_upper_entry(dtb_phys: usize) -> ! {
|
||||
Cpu::init_local();
|
||||
|
||||
kernel_main()
|
||||
// kernel_main(dtb_phys)
|
||||
}
|
||||
|
||||
extern "C" fn __aarch64_ap_upper_entry(_x0: usize) -> ! {
|
||||
|
@ -3,11 +3,10 @@ use core::sync::atomic::Ordering;
|
||||
|
||||
use aarch64_cpu::registers::{MPIDR_EL1, TPIDR_EL1};
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use kernel_util::util::OneTimeInit;
|
||||
use tock_registers::interfaces::{Readable, Writeable};
|
||||
|
||||
use crate::{
|
||||
arch::CpuMessage, panic, sync::IrqSafeSpinlock, task::sched::CpuQueue, util::OneTimeInit,
|
||||
};
|
||||
use crate::{arch::CpuMessage, panic, sync::IrqSafeSpinlock, task::sched::CpuQueue};
|
||||
|
||||
use super::smp::CPU_COUNT;
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -8,20 +8,18 @@ use alloc::boxed::Box;
|
||||
use device_api::{
|
||||
interrupt::{
|
||||
ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable,
|
||||
IpiDeliveryTarget, IrqNumber, IrqOptions, LocalInterruptController,
|
||||
IpiDeliveryTarget, IrqOptions, LocalInterruptController,
|
||||
},
|
||||
Device,
|
||||
};
|
||||
use kernel_util::util::OneTimeInit;
|
||||
|
||||
use crate::{
|
||||
arch::{
|
||||
aarch64::devtree::{self, DevTreeIndexPropExt},
|
||||
Architecture, CpuMessage,
|
||||
},
|
||||
arch::{aarch64::IrqNumber, Architecture, CpuMessage},
|
||||
device::devtree::{self, DevTreeIndexPropExt},
|
||||
device_tree_driver,
|
||||
mem::device::{DeviceMemory, DeviceMemoryIo},
|
||||
sync::IrqSafeSpinlock,
|
||||
util::OneTimeInit,
|
||||
};
|
||||
|
||||
use self::{gicc::Gicc, gicd::Gicd};
|
||||
@ -71,6 +69,8 @@ impl Device for Gic {
|
||||
}
|
||||
|
||||
impl ExternalInterruptController for Gic {
|
||||
type IrqNumber = IrqNumber;
|
||||
|
||||
fn register_irq(
|
||||
&self,
|
||||
irq: IrqNumber,
|
||||
|
@ -11,30 +11,34 @@ use aarch64_cpu::{
|
||||
};
|
||||
use abi::error::Error;
|
||||
use device_api::{
|
||||
interrupt::{
|
||||
ExternalInterruptController, IpiDeliveryTarget, IrqNumber, LocalInterruptController,
|
||||
},
|
||||
interrupt::{ExternalInterruptController, IpiDeliveryTarget, LocalInterruptController},
|
||||
timer::MonotonicTimestampProviderDevice,
|
||||
ResetDevice,
|
||||
};
|
||||
use fdt_rs::prelude::PropReader;
|
||||
use git_version::git_version;
|
||||
use kernel_util::util::OneTimeInit;
|
||||
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
|
||||
|
||||
use crate::{
|
||||
arch::{aarch64::devtree::FdtMemoryRegionIter, Architecture},
|
||||
debug::{self},
|
||||
device::{self, power::arm_psci::Psci, DevTreeNodeInfo},
|
||||
arch::Architecture,
|
||||
debug,
|
||||
device::{
|
||||
self,
|
||||
devtree::{
|
||||
self, DevTreeIndexNodePropGet, DevTreeIndexPropExt, DevTreeNodeInfo, DeviceTree,
|
||||
FdtMemoryRegionIter,
|
||||
},
|
||||
power::arm_psci::Psci,
|
||||
},
|
||||
fs::{Initrd, INITRD_DATA},
|
||||
mem::{
|
||||
phys::{self, reserved::reserve_region, PhysicalMemoryRegion},
|
||||
ConvertAddress,
|
||||
},
|
||||
util::OneTimeInit,
|
||||
};
|
||||
|
||||
use self::{
|
||||
devtree::{DevTreeIndexPropExt, DeviceTree},
|
||||
smp::CPU_COUNT,
|
||||
table::{init_fixed_tables, KERNEL_TABLES},
|
||||
};
|
||||
@ -44,7 +48,6 @@ use super::CpuMessage;
|
||||
pub mod boot;
|
||||
pub mod context;
|
||||
pub mod cpu;
|
||||
pub mod devtree;
|
||||
pub mod exception;
|
||||
pub mod gic;
|
||||
pub mod smp;
|
||||
@ -59,10 +62,16 @@ struct KernelStack {
|
||||
data: [u8; BOOT_STACK_SIZE],
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum IrqNumber {
|
||||
Private(u32),
|
||||
Shared(u32),
|
||||
}
|
||||
|
||||
/// AArch64 platform interface
|
||||
pub struct AArch64 {
|
||||
dt: OneTimeInit<DeviceTree<'static>>,
|
||||
ext_intc: OneTimeInit<&'static dyn ExternalInterruptController>,
|
||||
ext_intc: OneTimeInit<&'static dyn ExternalInterruptController<IrqNumber = IrqNumber>>,
|
||||
local_intc: OneTimeInit<&'static dyn LocalInterruptController<IpiMessage = CpuMessage>>,
|
||||
mtimer: OneTimeInit<&'static dyn MonotonicTimestampProviderDevice>,
|
||||
reset: OneTimeInit<&'static dyn ResetDevice>,
|
||||
@ -85,6 +94,8 @@ pub static ARCHITECTURE: AArch64 = AArch64 {
|
||||
};
|
||||
|
||||
impl Architecture for AArch64 {
|
||||
type IrqNumber = IrqNumber;
|
||||
|
||||
const KERNEL_VIRT_OFFSET: usize = 0xFFFFFF8000000000;
|
||||
|
||||
unsafe fn init_mmu(&self, bsp: bool) {
|
||||
@ -165,7 +176,7 @@ impl Architecture for AArch64 {
|
||||
|
||||
fn register_external_interrupt_controller(
|
||||
&self,
|
||||
intc: &'static dyn ExternalInterruptController,
|
||||
intc: &'static dyn ExternalInterruptController<IrqNumber = Self::IrqNumber>,
|
||||
) -> Result<(), Error> {
|
||||
self.ext_intc.init(intc);
|
||||
Ok(())
|
||||
@ -192,7 +203,9 @@ impl Architecture for AArch64 {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn external_interrupt_controller(&self) -> &'static dyn ExternalInterruptController {
|
||||
fn external_interrupt_controller(
|
||||
&self,
|
||||
) -> &'static dyn ExternalInterruptController<IrqNumber = Self::IrqNumber> {
|
||||
*self.ext_intc.get()
|
||||
}
|
||||
|
||||
@ -272,8 +285,7 @@ impl AArch64 {
|
||||
|
||||
fn chosen_stdout_path<'a>(dt: &'a DeviceTree) -> Option<&'a str> {
|
||||
let chosen = dt.node_by_path("/chosen")?;
|
||||
let prop = devtree::find_prop(&chosen, "stdout-path")?;
|
||||
prop.str().ok()
|
||||
chosen.prop("stdout-path")
|
||||
}
|
||||
|
||||
fn init_platform(&self, bsp: bool) {
|
||||
@ -294,7 +306,7 @@ impl AArch64 {
|
||||
node,
|
||||
};
|
||||
|
||||
if let Some((device, _)) = device::probe_dt_node(&probe) {
|
||||
if let Some((device, _)) = devtree::probe_dt_node(&probe) {
|
||||
unsafe {
|
||||
device.init().unwrap();
|
||||
}
|
||||
@ -313,7 +325,7 @@ impl AArch64 {
|
||||
|
||||
// Probe and initialize the rest of devices
|
||||
let nodes = dt.root().children();
|
||||
if let Err(error) = device::enumerate_dt(
|
||||
if let Err(error) = devtree::enumerate_dt(
|
||||
address_cells,
|
||||
size_cells,
|
||||
nodes,
|
||||
@ -323,7 +335,7 @@ impl AArch64 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some((device, _)) = device::probe_dt_node(&probe) {
|
||||
if let Some((device, _)) = devtree::probe_dt_node(&probe) {
|
||||
unsafe {
|
||||
device.init()?;
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use super::devtree::{self, DevTreeIndexNodePropGet, DeviceTree};
|
||||
use crate::device::devtree::{self, DevTreeIndexNodePropGet, DeviceTree};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum CpuEnableMethod {
|
||||
|
@ -5,15 +5,11 @@ use core::time::Duration;
|
||||
use aarch64_cpu::registers::{CNTFRQ_EL0, CNTPCT_EL0, CNTP_CTL_EL0, CNTP_TVAL_EL0};
|
||||
use abi::error::Error;
|
||||
use alloc::boxed::Box;
|
||||
use device_api::{
|
||||
interrupt::{InterruptHandler, IrqNumber},
|
||||
timer::MonotonicTimestampProviderDevice,
|
||||
Device,
|
||||
};
|
||||
use device_api::{interrupt::InterruptHandler, timer::MonotonicTimestampProviderDevice, Device};
|
||||
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
|
||||
|
||||
use crate::{
|
||||
arch::{Architecture, ARCHITECTURE},
|
||||
arch::{aarch64::IrqNumber, Architecture, ARCHITECTURE},
|
||||
device_tree_driver,
|
||||
proc::wait,
|
||||
task::tasklet,
|
||||
|
@ -19,31 +19,27 @@ macro_rules! absolute_address {
|
||||
}};
|
||||
}
|
||||
|
||||
pub mod aarch64;
|
||||
|
||||
pub use aarch64::{AArch64 as ArchitectureImpl, ARCHITECTURE};
|
||||
use cfg_if::cfg_if;
|
||||
// pub use aarch64::{AArch64 as ArchitectureImpl, ARCHITECTURE};
|
||||
use device_api::{
|
||||
interrupt::{ExternalInterruptController, IpiDeliveryTarget, LocalInterruptController},
|
||||
timer::MonotonicTimestampProviderDevice,
|
||||
ResetDevice,
|
||||
};
|
||||
// cfg_if! {
|
||||
// if #[cfg(target_arch = "aarch64")] {
|
||||
//
|
||||
// pub use aarch64::{AArch64 as ArchitectureImpl, ARCHITECTURE, PlatformImpl, PLATFORM};
|
||||
// } else if #[cfg(target_arch = "x86_64")] {
|
||||
// pub mod x86_64;
|
||||
//
|
||||
// pub use x86_64::{
|
||||
// X86_64 as ArchitectureImpl,
|
||||
// X86_64 as PlatformImpl,
|
||||
// ARCHITECTURE,
|
||||
// ARCHITECTURE as PLATFORM
|
||||
// };
|
||||
// } else {
|
||||
// compile_error!("Architecture is not supported");
|
||||
// }
|
||||
// }
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(target_arch = "aarch64")] {
|
||||
pub mod aarch64;
|
||||
|
||||
pub use aarch64::{AArch64 as ArchitectureImpl, ARCHITECTURE};
|
||||
} else if #[cfg(target_arch = "x86_64")] {
|
||||
pub mod x86_64;
|
||||
|
||||
pub use x86_64::{X86_64 as ArchitectureImpl, ARCHITECTURE};
|
||||
} else {
|
||||
compile_error!("Architecture is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes messages sent from some CPU to others
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
@ -58,6 +54,9 @@ pub trait Architecture {
|
||||
/// Address, to which "zero" address is mapped in the virtual address space
|
||||
const KERNEL_VIRT_OFFSET: usize;
|
||||
|
||||
/// IRQ number type associated with the architecture
|
||||
type IrqNumber;
|
||||
|
||||
/// Initializes the memory management unit and sets up virtual memory management.
|
||||
/// `bsp` flag is provided to make sure mapping tables are only initialized once in a SMP
|
||||
/// system.
|
||||
@ -99,7 +98,7 @@ pub trait Architecture {
|
||||
/// Adds an external interrupt controller to the system
|
||||
fn register_external_interrupt_controller(
|
||||
&self,
|
||||
intc: &'static dyn ExternalInterruptController,
|
||||
intc: &'static dyn ExternalInterruptController<IrqNumber = Self::IrqNumber>,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
/// Adds a local interrupt controller to the system
|
||||
@ -119,15 +118,17 @@ pub trait Architecture {
|
||||
|
||||
// TODO only supports 1 extintc per system
|
||||
/// Returns the primary external interrupt controller
|
||||
fn external_interrupt_controller(&self) -> &'static dyn ExternalInterruptController;
|
||||
fn external_interrupt_controller(
|
||||
&'static self,
|
||||
) -> &'static dyn ExternalInterruptController<IrqNumber = Self::IrqNumber>;
|
||||
|
||||
/// Returns the local interrupt controller
|
||||
fn local_interrupt_controller(
|
||||
&self,
|
||||
&'static self,
|
||||
) -> &'static dyn LocalInterruptController<IpiMessage = CpuMessage>;
|
||||
|
||||
/// 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.
|
||||
///
|
||||
|
@ -1,19 +1,21 @@
|
||||
//! x86-64 I/O APIC driver implementation
|
||||
use abi::error::Error;
|
||||
use acpi::platform::interrupt::{Apic as AcpiApic, Polarity, TriggerMode};
|
||||
|
||||
use crate::{
|
||||
arch::x86_64::apic::local::BSP_APIC_ID,
|
||||
device::{
|
||||
interrupt::{ExternalInterruptController, InterruptSource},
|
||||
Device, InitializableDevice,
|
||||
use device_api::{
|
||||
interrupt::{
|
||||
ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable,
|
||||
IrqLevel, IrqOptions, IrqTrigger,
|
||||
},
|
||||
mem::ConvertAddress,
|
||||
sync::IrqSafeSpinlock,
|
||||
util::OneTimeInit,
|
||||
Device,
|
||||
};
|
||||
|
||||
use super::{IrqNumber, Table, APIC_EXTERNAL_OFFSET};
|
||||
use crate::{
|
||||
arch::x86_64::{apic::local::BSP_APIC_ID, IrqNumber},
|
||||
mem::ConvertAddress,
|
||||
sync::IrqSafeSpinlock,
|
||||
};
|
||||
|
||||
use super::{APIC_EXTERNAL_OFFSET, MAX_EXTERNAL_VECTORS};
|
||||
|
||||
// IRQ 0 is timer, IRQ 1 reserved (for now?), +32 offset for exception entries
|
||||
const IO_APIC_VECTOR_OFFSET: u32 = 32 + APIC_EXTERNAL_OFFSET;
|
||||
@ -54,10 +56,10 @@ struct Inner {
|
||||
/// I/O APIC interface. Provides a way to route and control how interrupts from external devices
|
||||
/// are handled.
|
||||
pub struct IoApic {
|
||||
inner: OneTimeInit<IrqSafeSpinlock<Inner>>,
|
||||
isa_redirections: OneTimeInit<[Option<IsaRedirection>; 16]>,
|
||||
inner: IrqSafeSpinlock<Inner>,
|
||||
isa_redirections: [Option<IsaRedirection>; 16],
|
||||
|
||||
table: IrqSafeSpinlock<Table>,
|
||||
table: IrqSafeSpinlock<FixedInterruptTable<{ MAX_EXTERNAL_VECTORS as usize }>>,
|
||||
}
|
||||
|
||||
impl Regs {
|
||||
@ -108,21 +110,29 @@ impl Inner {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn configure_gsi(&mut self, gsi: u32, active_low: bool, level_triggered: bool) {
|
||||
fn configure_gsi(&mut self, gsi: u32, options: IrqOptions) {
|
||||
assert!(gsi < self.max_gsi);
|
||||
|
||||
let mut low = self.regs.read(REG_REDIRECTION_BASE + gsi * 2);
|
||||
|
||||
if active_low {
|
||||
low |= ENTRY_LOW_POLARITY_LOW;
|
||||
} else {
|
||||
low &= !ENTRY_LOW_POLARITY_LOW;
|
||||
match options.level {
|
||||
IrqLevel::Default => (),
|
||||
IrqLevel::ActiveLow => {
|
||||
low |= ENTRY_LOW_POLARITY_LOW;
|
||||
}
|
||||
IrqLevel::ActiveHigh => {
|
||||
low &= !ENTRY_LOW_POLARITY_LOW;
|
||||
}
|
||||
}
|
||||
|
||||
if level_triggered {
|
||||
low |= ENTRY_LOW_TRIGGER_LEVEL;
|
||||
} else {
|
||||
low &= !ENTRY_LOW_TRIGGER_LEVEL;
|
||||
match options.trigger {
|
||||
IrqTrigger::Default => (),
|
||||
IrqTrigger::Level => {
|
||||
low |= ENTRY_LOW_TRIGGER_LEVEL;
|
||||
}
|
||||
IrqTrigger::Edge => {
|
||||
low &= !ENTRY_LOW_TRIGGER_LEVEL;
|
||||
}
|
||||
}
|
||||
|
||||
self.regs.write(REG_REDIRECTION_BASE + gsi * 2, low);
|
||||
@ -144,71 +154,69 @@ impl Inner {
|
||||
}
|
||||
|
||||
impl Device for IoApic {
|
||||
fn name(&self) -> &'static str {
|
||||
fn display_name(&self) -> &'static str {
|
||||
"I/O APIC"
|
||||
}
|
||||
|
||||
unsafe fn init(&'static self) -> Result<(), Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ExternalInterruptController for IoApic {
|
||||
type IrqNumber = IrqNumber;
|
||||
|
||||
fn enable_irq(&self, irq: Self::IrqNumber) -> Result<(), Error> {
|
||||
let mut inner = self.inner.get().lock();
|
||||
let gsi = self.translate_irq(irq);
|
||||
inner.set_gsi_enabled(gsi, true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_handler(
|
||||
fn register_irq(
|
||||
&self,
|
||||
irq: Self::IrqNumber,
|
||||
handler: &'static (dyn InterruptSource + Sync),
|
||||
options: IrqOptions,
|
||||
handler: &'static dyn InterruptHandler,
|
||||
) -> Result<(), Error> {
|
||||
let mut inner = self.inner.get().lock();
|
||||
let mut table = self.table.lock();
|
||||
let mut inner = self.inner.lock();
|
||||
let table_vector = self.table.lock().insert_least_loaded(handler)?;
|
||||
|
||||
// Allocate a vector
|
||||
let table_vector = table.least_loaded();
|
||||
let gsi_target_vector = (table_vector as u32) + IO_APIC_VECTOR_OFFSET;
|
||||
let bsp_apic = *BSP_APIC_ID.get();
|
||||
|
||||
infoln!(
|
||||
"Binding {:?} ({}) to {}:{}",
|
||||
irq,
|
||||
handler.name(),
|
||||
handler.display_name(),
|
||||
bsp_apic,
|
||||
table_vector
|
||||
);
|
||||
table.add_handler(table_vector, handler)?;
|
||||
|
||||
let gsi = self.translate_irq(irq);
|
||||
inner.map_gsi(gsi, gsi_target_vector, *BSP_APIC_ID.get())?;
|
||||
inner.configure_gsi(gsi, options);
|
||||
inner.map_gsi(gsi, gsi_target_vector, bsp_apic)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn configure_irq(
|
||||
&self,
|
||||
irq: Self::IrqNumber,
|
||||
active_low: bool,
|
||||
level_triggered: bool,
|
||||
) -> Result<(), Error> {
|
||||
let mut inner = self.inner.get().lock();
|
||||
fn enable_irq(&self, irq: Self::IrqNumber) -> Result<(), Error> {
|
||||
let mut inner = self.inner.lock();
|
||||
let gsi = self.translate_irq(irq);
|
||||
|
||||
inner.configure_gsi(gsi, active_low, level_triggered);
|
||||
|
||||
inner.set_gsi_enabled(gsi, true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_specific_irq(&self, gsi: usize) {
|
||||
let table = self.table.lock();
|
||||
|
||||
if let Some(handler) = table.handler(gsi) {
|
||||
handler.handle_irq();
|
||||
} else {
|
||||
warnln!("No handler set for GSI #{}", gsi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InitializableDevice for IoApic {
|
||||
type Options = AcpiApic;
|
||||
impl IoApic {
|
||||
/// Creates an I/O APIC instance from its ACPI definition
|
||||
pub fn from_acpi(info: &AcpiApic) -> Result<Self, Error> {
|
||||
let ioapic = info.io_apics.first().unwrap();
|
||||
|
||||
unsafe fn init(&self, info: Self::Options) -> Result<(), Error> {
|
||||
let io_apic = info.io_apics.first().unwrap();
|
||||
|
||||
infoln!("I/O APIC at {:#x}", io_apic.address);
|
||||
infoln!("I/O APIC at {:#x}", ioapic.address);
|
||||
|
||||
let mut isa_redirections = [None; 16];
|
||||
|
||||
@ -236,8 +244,9 @@ impl InitializableDevice for IoApic {
|
||||
});
|
||||
}
|
||||
|
||||
// TODO properly map this using DeviceMemory
|
||||
let regs = Regs {
|
||||
base: unsafe { (io_apic.address as usize).virtualize() },
|
||||
base: unsafe { (ioapic.address as usize).virtualize() },
|
||||
};
|
||||
|
||||
let max_gsi = (regs.read(REG_IOAPIC_VERSION) >> 16) & 0xFF;
|
||||
@ -251,36 +260,15 @@ impl InitializableDevice for IoApic {
|
||||
inner.set_gsi_enabled(gsi, false);
|
||||
}
|
||||
|
||||
self.inner.init(IrqSafeSpinlock::new(inner));
|
||||
self.isa_redirections.init(isa_redirections);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IoApic {
|
||||
/// Constructs an uninitialized I/O APIC interface
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
inner: OneTimeInit::new(),
|
||||
isa_redirections: OneTimeInit::new(),
|
||||
table: IrqSafeSpinlock::new(Table::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles an interrupt with given GSI number
|
||||
pub fn handle_irq(&self, gsi: u32) {
|
||||
let table = self.table.lock();
|
||||
|
||||
if let Some(handler) = table.vectors[gsi as usize] {
|
||||
handler.handle_irq().expect("IRQ handler failed");
|
||||
} else {
|
||||
warnln!("No handler set for GSI #{}", gsi);
|
||||
}
|
||||
Ok(Self {
|
||||
isa_redirections,
|
||||
inner: IrqSafeSpinlock::new(inner),
|
||||
table: IrqSafeSpinlock::new(FixedInterruptTable::new()),
|
||||
})
|
||||
}
|
||||
|
||||
fn translate_irq(&self, irq: IrqNumber) -> u32 {
|
||||
let redir = self.isa_redirections.get();
|
||||
let redir = &self.isa_redirections;
|
||||
match irq {
|
||||
IrqNumber::Isa(isa) => redir[isa as usize]
|
||||
.map(|t| t.gsi_index)
|
||||
|
@ -1,4 +1,6 @@
|
||||
//! x86-64 Local APIC driver implementation
|
||||
use device_api::{interrupt::LocalInterruptController, Device};
|
||||
use kernel_util::util::OneTimeInit;
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
@ -6,13 +8,11 @@ use tock_registers::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
arch::x86_64::registers::MSR_IA32_APIC_BASE, device::interrupt::IpiDeliveryTarget,
|
||||
mem::ConvertAddress, util::OneTimeInit,
|
||||
arch::{x86_64::registers::MSR_IA32_APIC_BASE, CpuMessage},
|
||||
mem::ConvertAddress,
|
||||
};
|
||||
|
||||
use super::{
|
||||
APIC_IPI_VECTOR, APIC_LINT0_VECTOR, APIC_LINT1_VECTOR, APIC_SPURIOUS_VECTOR, APIC_TIMER_VECTOR,
|
||||
};
|
||||
use super::{APIC_LINT0_VECTOR, APIC_LINT1_VECTOR, APIC_SPURIOUS_VECTOR, APIC_TIMER_VECTOR};
|
||||
|
||||
const TIMER_INTERVAL: u32 = 150000;
|
||||
|
||||
@ -122,6 +122,31 @@ pub struct LocalApic {
|
||||
regs: &'static Regs,
|
||||
}
|
||||
|
||||
unsafe impl Send for LocalApic {}
|
||||
unsafe impl Sync for LocalApic {}
|
||||
|
||||
impl Device for LocalApic {
|
||||
fn display_name(&self) -> &'static str {
|
||||
"Local APIC"
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalInterruptController for LocalApic {
|
||||
type IpiMessage = CpuMessage;
|
||||
|
||||
fn send_ipi(
|
||||
&self,
|
||||
_target: device_api::interrupt::IpiDeliveryTarget,
|
||||
_msg: Self::IpiMessage,
|
||||
) -> Result<(), abi::error::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
unsafe fn init_ap(&self) -> Result<(), abi::error::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalApic {
|
||||
/// Constructs a new instance of Local APIC.
|
||||
///
|
||||
@ -232,28 +257,28 @@ impl LocalApic {
|
||||
}
|
||||
}
|
||||
|
||||
/// Issues an interprocessor interrupt for the target.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe: this function may break the control flow on the target processors.
|
||||
pub unsafe fn send_ipi(&self, target: IpiDeliveryTarget) {
|
||||
while self.regs.ICR0.matches_all(ICR0::DeliveryStatus::SET) {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
// /// Issues an interprocessor interrupt for the target.
|
||||
// ///
|
||||
// /// # Safety
|
||||
// ///
|
||||
// /// Unsafe: this function may break the control flow on the target processors.
|
||||
// pub unsafe fn send_ipi(&self, target: IpiDeliveryTarget) {
|
||||
// while self.regs.ICR0.matches_all(ICR0::DeliveryStatus::SET) {
|
||||
// core::hint::spin_loop();
|
||||
// }
|
||||
|
||||
match target {
|
||||
IpiDeliveryTarget::AllExceptLocal => {
|
||||
self.regs.ICR1.write(ICR1::PhysicalDestination.val(0));
|
||||
self.regs.ICR0.write(
|
||||
ICR0::Vector.val(APIC_IPI_VECTOR + 32)
|
||||
+ ICR0::Destination::NMI
|
||||
+ ICR0::DestinationType::AllExceptThis,
|
||||
);
|
||||
}
|
||||
IpiDeliveryTarget::Specified(_) => todo!(),
|
||||
}
|
||||
}
|
||||
// match target {
|
||||
// IpiDeliveryTarget::AllExceptLocal => {
|
||||
// self.regs.ICR1.write(ICR1::PhysicalDestination.val(0));
|
||||
// self.regs.ICR0.write(
|
||||
// ICR0::Vector.val(APIC_IPI_VECTOR + 32)
|
||||
// + ICR0::Destination::NMI
|
||||
// + ICR0::DestinationType::AllExceptThis,
|
||||
// );
|
||||
// }
|
||||
// IpiDeliveryTarget::Specified(_) => todo!(),
|
||||
// }
|
||||
// }
|
||||
|
||||
#[inline]
|
||||
fn base() -> usize {
|
||||
|
@ -2,15 +2,15 @@
|
||||
|
||||
use core::arch::global_asm;
|
||||
|
||||
use abi::error::Error;
|
||||
|
||||
use crate::{
|
||||
arch::{x86_64::cpu::Cpu, PLATFORM},
|
||||
device::interrupt::IrqHandler,
|
||||
arch::{x86_64::cpu::Cpu, Architecture},
|
||||
task::process::Process,
|
||||
};
|
||||
|
||||
use super::exception::{self, IrqFrame};
|
||||
use super::{
|
||||
exception::{self, IrqFrame},
|
||||
ARCHITECTURE,
|
||||
};
|
||||
|
||||
pub mod ioapic;
|
||||
pub mod local;
|
||||
@ -32,37 +32,6 @@ pub const APIC_EXTERNAL_OFFSET: u32 = 4;
|
||||
/// Maximum number of APIC vectors allocated for handling IRQs from I/O APIC
|
||||
pub const MAX_EXTERNAL_VECTORS: u32 = 16;
|
||||
|
||||
/// x86-64 interrupt number wrapper
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum IrqNumber {
|
||||
/// Legacy (ISA) interrupt. Can have value in range 0..16.
|
||||
Isa(u8),
|
||||
/// Global system interrupt. Means an external interrupt for I/O APIC.
|
||||
Gsi(u8),
|
||||
}
|
||||
|
||||
struct Table {
|
||||
vectors: [Option<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
|
||||
pub fn setup_vectors(idt: &mut [exception::Entry]) {
|
||||
extern "C" {
|
||||
@ -83,7 +52,9 @@ unsafe extern "C" fn irq_handler(vector: usize, frame: *mut IrqFrame) {
|
||||
let cpu = Cpu::local();
|
||||
let frame = &mut *frame;
|
||||
|
||||
PLATFORM.ioapic.handle_irq(vector as u32);
|
||||
ARCHITECTURE
|
||||
.external_interrupt_controller()
|
||||
.handle_specific_irq(vector);
|
||||
cpu.local_apic().clear_interrupt();
|
||||
|
||||
if let Some(process) = Process::get_current() {
|
||||
|
@ -9,18 +9,21 @@ use yboot_proto::{
|
||||
|
||||
use crate::{
|
||||
arch::{
|
||||
x86_64::{
|
||||
apic::local::LocalApic, cpu::Cpu, cpuid, exception, kernel_main,
|
||||
registers::MSR_IA32_KERNEL_GS_BASE, syscall, CPU_INIT_FENCE,
|
||||
},
|
||||
Architecture, ArchitectureImpl,
|
||||
x86_64::{cpuid, exception, registers::MSR_IA32_KERNEL_GS_BASE, BootData},
|
||||
Architecture, ArchitectureImpl, ARCHITECTURE,
|
||||
},
|
||||
fs::devfs,
|
||||
kernel_main, kernel_secondary_main,
|
||||
mem::{
|
||||
heap,
|
||||
phys::{self, PageUsage},
|
||||
ConvertAddress, KERNEL_VIRT_OFFSET,
|
||||
},
|
||||
device::platform::Platform,
|
||||
mem::KERNEL_VIRT_OFFSET,
|
||||
task,
|
||||
};
|
||||
|
||||
use super::ARCHITECTURE;
|
||||
use super::smp::CPU_COUNT;
|
||||
|
||||
// use super::ARCHITECTURE;
|
||||
|
||||
const BOOT_STACK_SIZE: usize = 65536;
|
||||
|
||||
@ -76,45 +79,58 @@ unsafe extern "C" fn __x86_64_upper_entry() -> ! {
|
||||
ARCHITECTURE.init_mmu(true);
|
||||
core::arch::asm!("wbinvd");
|
||||
|
||||
kernel_main(&YBOOT_DATA)
|
||||
ARCHITECTURE.set_boot_data(BootData::YBoot(&YBOOT_DATA));
|
||||
ARCHITECTURE
|
||||
.init_physical_memory()
|
||||
.expect("Failed to initialize the physical memory manager");
|
||||
|
||||
let heap_base = phys::alloc_pages_contiguous(16, PageUsage::Used)
|
||||
.expect("Couldn't allocate memory for heap");
|
||||
heap::init_heap(heap_base.virtualize(), 16 * 0x1000);
|
||||
|
||||
exception::init_exceptions(0);
|
||||
|
||||
devfs::init();
|
||||
|
||||
// Initializes: local CPU, platform devices (timers/serials/etc), debug output
|
||||
ARCHITECTURE.init_platform(0);
|
||||
|
||||
cpuid::feature_gate();
|
||||
|
||||
kernel_main()
|
||||
}
|
||||
|
||||
/// Application processor entry point
|
||||
pub extern "C" fn __x86_64_ap_entry() -> ! {
|
||||
loop {}
|
||||
// let cpu_id = CPU_COUNT.load(Ordering::Acquire);
|
||||
let cpu_id = CPU_COUNT.load(Ordering::Acquire);
|
||||
|
||||
// MSR_IA32_KERNEL_GS_BASE.set(&UNINIT_CPU as *const _ as u64);
|
||||
// unsafe {
|
||||
// core::arch::asm!("swapgs");
|
||||
// }
|
||||
// MSR_IA32_KERNEL_GS_BASE.set(&UNINIT_CPU as *const _ as u64);
|
||||
// unsafe {
|
||||
// core::arch::asm!("swapgs");
|
||||
// }
|
||||
MSR_IA32_KERNEL_GS_BASE.set(&UNINIT_CPU as *const _ as u64);
|
||||
unsafe {
|
||||
core::arch::asm!("swapgs");
|
||||
}
|
||||
MSR_IA32_KERNEL_GS_BASE.set(&UNINIT_CPU as *const _ as u64);
|
||||
unsafe {
|
||||
core::arch::asm!("swapgs");
|
||||
}
|
||||
|
||||
// // Still not initialized: GDT, IDT, CPU features, syscall, kernel_gs_base
|
||||
// cpuid::feature_gate();
|
||||
// Still not initialized: GDT, IDT, CPU features, syscall, kernel_gs_base
|
||||
cpuid::feature_gate();
|
||||
|
||||
// infoln!("cpu{} initializing", cpu_id);
|
||||
// unsafe {
|
||||
// ARCHITECTURE.init_mmu(false);
|
||||
// core::arch::asm!("wbinvd");
|
||||
infoln!("cpu{} initializing", cpu_id);
|
||||
unsafe {
|
||||
ARCHITECTURE.init_mmu(false);
|
||||
core::arch::asm!("wbinvd");
|
||||
|
||||
// Cpu::init_local(LocalApic::new(), cpu_id as u32);
|
||||
// syscall::init_syscall();
|
||||
// exception::init_exceptions(cpu_id);
|
||||
// Cpu::init_local(LocalApic::new(), cpu_id as u32);
|
||||
// syscall::init_syscall();
|
||||
exception::init_exceptions(cpu_id);
|
||||
|
||||
// ARCHITECTURE.init(false).unwrap();
|
||||
// }
|
||||
ARCHITECTURE.init_platform(cpu_id);
|
||||
}
|
||||
|
||||
// CPU_COUNT.fetch_add(1, Ordering::Release);
|
||||
CPU_COUNT.fetch_add(1, Ordering::Release);
|
||||
|
||||
// CPU_INIT_FENCE.wait_one();
|
||||
|
||||
// infoln!("cpu{} initialized", cpu_id);
|
||||
|
||||
// unsafe { task::enter() }
|
||||
kernel_secondary_main()
|
||||
}
|
||||
|
||||
global_asm!(
|
||||
|
@ -2,12 +2,12 @@
|
||||
use core::ptr::null_mut;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use kernel_util::util::OneTimeInit;
|
||||
use tock_registers::interfaces::Writeable;
|
||||
|
||||
use crate::{
|
||||
arch::x86_64::{gdt, registers::MSR_IA32_KERNEL_GS_BASE},
|
||||
arch::x86_64::{gdt, registers::MSR_IA32_KERNEL_GS_BASE, syscall},
|
||||
task::sched::CpuQueue,
|
||||
util::OneTimeInit,
|
||||
};
|
||||
|
||||
use super::apic::local::LocalApic;
|
||||
@ -53,6 +53,8 @@ impl Cpu {
|
||||
|
||||
MSR_IA32_KERNEL_GS_BASE.set(this as u64);
|
||||
core::arch::asm!("wbinvd; swapgs");
|
||||
|
||||
syscall::init_syscall();
|
||||
}
|
||||
|
||||
/// Returns this CPU's local data structure
|
||||
|
@ -2,50 +2,50 @@
|
||||
use core::{ptr::NonNull, sync::atomic::Ordering};
|
||||
|
||||
use abi::error::Error;
|
||||
use acpi::{
|
||||
platform::ProcessorInfo, AcpiHandler, AcpiTables, HpetInfo, InterruptModel, PhysicalMapping,
|
||||
};
|
||||
use acpi::{AcpiHandler, AcpiTables, HpetInfo, InterruptModel, PhysicalMapping};
|
||||
use alloc::boxed::Box;
|
||||
use cpu::Cpu;
|
||||
use device_api::{
|
||||
input::KeyboardProducer,
|
||||
interrupt::{ExternalInterruptController, IpiDeliveryTarget, LocalInterruptController},
|
||||
timer::MonotonicTimestampProviderDevice,
|
||||
Device,
|
||||
};
|
||||
use git_version::git_version;
|
||||
use yboot_proto::{v1::FramebufferOption, AvailableRegion, IterableMemoryMap, LoadProtocolV1};
|
||||
use kernel_util::util::OneTimeInit;
|
||||
use yboot_proto::{AvailableRegion, IterableMemoryMap};
|
||||
|
||||
use crate::{
|
||||
arch::x86_64::{
|
||||
apic::local::LocalApic,
|
||||
peripherals::serial::ComPort,
|
||||
table::{init_fixed_tables, KERNEL_TABLES},
|
||||
},
|
||||
debug::{self, LogLevel},
|
||||
device::{
|
||||
self,
|
||||
display::{
|
||||
console::{add_console_autoflush, ConsoleBuffer, ConsoleRow, DisplayConsole},
|
||||
console::{self, ConsoleBuffer},
|
||||
fb_console::FramebufferConsole,
|
||||
linear_fb::LinearFramebuffer,
|
||||
},
|
||||
input::KeyboardDevice,
|
||||
interrupt::{ExternalInterruptController, InterruptSource, IpiDeliveryTarget},
|
||||
platform::Platform,
|
||||
timer::TimestampSource,
|
||||
tty::CombinedTerminal,
|
||||
InitializableDevice,
|
||||
tty::combined::CombinedTerminal,
|
||||
},
|
||||
fs::{
|
||||
devfs::{self, CharDeviceType},
|
||||
Initrd, INITRD_DATA,
|
||||
},
|
||||
mem::{
|
||||
heap,
|
||||
phys::{self, reserved::reserve_region, PageUsage, PhysicalMemoryRegion},
|
||||
phys::{self, reserved::reserve_region, PhysicalMemoryRegion},
|
||||
ConvertAddress,
|
||||
},
|
||||
sync::SpinFence,
|
||||
task,
|
||||
util::OneTimeInit,
|
||||
CPU_INIT_FENCE,
|
||||
};
|
||||
|
||||
use self::{
|
||||
apic::{ioapic::IoApic, IrqNumber},
|
||||
apic::ioapic::IoApic,
|
||||
intrinsics::IoPort,
|
||||
peripherals::{hpet::Hpet, ps2::PS2Controller, serial::ComPort},
|
||||
peripherals::{hpet::Hpet, ps2::PS2Controller},
|
||||
smp::CPU_COUNT,
|
||||
};
|
||||
|
||||
@ -67,7 +67,14 @@ pub mod smp;
|
||||
pub mod syscall;
|
||||
pub mod table;
|
||||
|
||||
static CPU_INIT_FENCE: SpinFence = SpinFence::new();
|
||||
/// x86-64 interrupt number wrapper
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum IrqNumber {
|
||||
/// Legacy (ISA) interrupt. Can have value in range 0..16.
|
||||
Isa(u8),
|
||||
/// Global system interrupt. Means an external interrupt for I/O APIC.
|
||||
Gsi(u8),
|
||||
}
|
||||
|
||||
/// Helper trait to provide abstract access to available memory regions
|
||||
pub trait AbstractAvailableRegion {
|
||||
@ -119,6 +126,27 @@ impl<'a, T: IterableMemoryMap<'a> + 'a> AbstractMemoryMap<'a> for T {
|
||||
#[derive(Clone, Copy)]
|
||||
struct AcpiHandlerImpl;
|
||||
|
||||
struct SimpleLogger;
|
||||
|
||||
impl log::Log for SimpleLogger {
|
||||
fn enabled(&self, _metadata: &log::Metadata) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn log(&self, record: &log::Record) {
|
||||
match record.level() {
|
||||
log::Level::Warn => warnln!("{}", record.args()),
|
||||
log::Level::Info => infoln!("{}", record.args()),
|
||||
log::Level::Trace | log::Level::Debug => infoln!("{}", record.args()),
|
||||
log::Level::Error => errorln!("{}", record.args()),
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
static LOGGER: SimpleLogger = SimpleLogger;
|
||||
|
||||
impl AcpiHandler for AcpiHandlerImpl {
|
||||
// No actual address space modification is performed
|
||||
unsafe fn map_physical_region<T>(
|
||||
@ -143,28 +171,44 @@ impl AcpiHandler for AcpiHandlerImpl {
|
||||
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
|
||||
pub struct X86_64 {
|
||||
yboot_framebuffer: OneTimeInit<FramebufferOption>,
|
||||
rsdp_phys_base: OneTimeInit<usize>,
|
||||
boot_data: OneTimeInit<BootData>,
|
||||
acpi: OneTimeInit<AcpiTables<AcpiHandlerImpl>>,
|
||||
|
||||
acpi_processor_info: OneTimeInit<ProcessorInfo>,
|
||||
|
||||
// TODO make this fully dynamic?
|
||||
framebuffer: OneTimeInit<LinearFramebuffer>,
|
||||
fb_console: OneTimeInit<FramebufferConsole>,
|
||||
combined_terminal: OneTimeInit<CombinedTerminal>,
|
||||
|
||||
// Static devices with dynamic addresses
|
||||
ioapic: IoApic,
|
||||
timer: Hpet,
|
||||
|
||||
// Static devices
|
||||
ps2: PS2Controller,
|
||||
com1_3: ComPort,
|
||||
ioapic: OneTimeInit<IoApic>,
|
||||
timer: OneTimeInit<Hpet>,
|
||||
}
|
||||
|
||||
/// x86-64 architecture implementation
|
||||
pub static ARCHITECTURE: X86_64 = X86_64 {
|
||||
boot_data: OneTimeInit::new(),
|
||||
acpi: OneTimeInit::new(),
|
||||
|
||||
framebuffer: OneTimeInit::new(),
|
||||
fb_console: OneTimeInit::new(),
|
||||
combined_terminal: OneTimeInit::new(),
|
||||
|
||||
// Devices
|
||||
ioapic: OneTimeInit::new(),
|
||||
timer: OneTimeInit::new(),
|
||||
};
|
||||
|
||||
impl Architecture for X86_64 {
|
||||
const KERNEL_VIRT_OFFSET: usize = 0xFFFFFF8000000000;
|
||||
|
||||
type IrqNumber = IrqNumber;
|
||||
|
||||
unsafe fn init_mmu(&self, bsp: bool) {
|
||||
if bsp {
|
||||
init_fixed_tables();
|
||||
@ -174,16 +218,6 @@ impl Architecture for X86_64 {
|
||||
core::arch::asm!("wbinvd; mov {0}, %cr3", in(reg) cr3, options(att_syntax));
|
||||
}
|
||||
|
||||
fn map_device_pages(&self, phys: usize, count: usize) -> Result<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) {
|
||||
if mask {
|
||||
core::arch::asm!("cli");
|
||||
@ -201,98 +235,18 @@ impl Architecture for X86_64 {
|
||||
flags & (1 << 9) == 0
|
||||
}
|
||||
|
||||
fn cpu_count() -> usize {
|
||||
CPU_COUNT.load(Ordering::Acquire)
|
||||
}
|
||||
}
|
||||
|
||||
impl Platform for X86_64 {
|
||||
type IrqNumber = apic::IrqNumber;
|
||||
|
||||
const KERNEL_PHYS_BASE: usize = 0x400000;
|
||||
|
||||
unsafe fn init(&'static self, is_bsp: bool) -> Result<(), Error> {
|
||||
if is_bsp {
|
||||
Self::disable_8259();
|
||||
|
||||
// Initialize I/O APIC from ACPI
|
||||
let rsdp = *self.rsdp_phys_base.get();
|
||||
let acpi_tables = AcpiTables::from_rsdp(AcpiHandlerImpl, rsdp).unwrap();
|
||||
|
||||
let hpet = HpetInfo::new(&acpi_tables).unwrap();
|
||||
let platform_info = acpi_tables.platform_info().unwrap();
|
||||
|
||||
if let Some(processor_info) = platform_info.processor_info {
|
||||
self.acpi_processor_info.init(processor_info);
|
||||
}
|
||||
|
||||
let InterruptModel::Apic(apic_info) = platform_info.interrupt_model else {
|
||||
panic!("Processor does not have APIC");
|
||||
};
|
||||
|
||||
self.ioapic.init(apic_info)?;
|
||||
self.timer.init(hpet).unwrap();
|
||||
|
||||
// Enable IRQs for the devices
|
||||
self.ps2.init_irq()?;
|
||||
self.timer.init_irq()?;
|
||||
|
||||
// Add a terminal to the devfs
|
||||
// TODO this is ugly
|
||||
let combined_terminal = CombinedTerminal::new(&self.ps2, FB_CONSOLE.get());
|
||||
self.combined_terminal.init(combined_terminal);
|
||||
self.ps2.attach(self.combined_terminal.get());
|
||||
|
||||
devfs::add_char_device(self.combined_terminal.get(), CharDeviceType::TtyRegular)?;
|
||||
fn wait_for_interrupt() {
|
||||
unsafe {
|
||||
core::arch::asm!("hlt");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn init_debug(&'static self) {
|
||||
// Serial output
|
||||
self.com1_3.init(()).ok();
|
||||
debug::add_sink(self.com1_3.port_a(), LogLevel::Debug);
|
||||
|
||||
// Graphical output
|
||||
if let Some(fb) = self.yboot_framebuffer.try_get() {
|
||||
LINEAR_FB.init(
|
||||
LinearFramebuffer::from_physical_bits(
|
||||
fb.res_address as _,
|
||||
fb.res_size as _,
|
||||
fb.res_stride as _,
|
||||
fb.res_width,
|
||||
fb.res_height,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
FB_CONSOLE.init(FramebufferConsole::from_framebuffer(
|
||||
LINEAR_FB.get(),
|
||||
&bitmap_font::tamzen::FONT_6x12,
|
||||
ConsoleBuffer::fixed(&mut EARLY_CONSOLE_BUFFER, 32),
|
||||
));
|
||||
|
||||
debug::add_sink(FB_CONSOLE.get(), LogLevel::Info);
|
||||
}
|
||||
|
||||
debug::reset();
|
||||
// CPU management
|
||||
unsafe fn reset(&self) -> ! {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"x86-64"
|
||||
}
|
||||
|
||||
fn interrupt_controller(
|
||||
&self,
|
||||
) -> &dyn ExternalInterruptController<IrqNumber = Self::IrqNumber> {
|
||||
&self.ioapic
|
||||
}
|
||||
|
||||
fn timestamp_source(&self) -> &dyn TimestampSource {
|
||||
&self.timer
|
||||
}
|
||||
|
||||
unsafe fn send_ipi(&self, target: IpiDeliveryTarget, _msg: CpuMessage) -> Result<(), Error> {
|
||||
unsafe fn send_ipi(&self, target: IpiDeliveryTarget, msg: CpuMessage) -> Result<(), Error> {
|
||||
if !CPU_INIT_FENCE.try_wait_all(1) {
|
||||
// Don't send an IPI: SMP not initialized yet
|
||||
return Ok(());
|
||||
@ -302,14 +256,132 @@ impl Platform for X86_64 {
|
||||
panic!("Local APIC has not been initialized yet");
|
||||
};
|
||||
|
||||
local_apic.send_ipi(target);
|
||||
local_apic.send_ipi(target, msg)
|
||||
}
|
||||
|
||||
Ok(())
|
||||
unsafe fn start_application_processors(&self) {
|
||||
if let Some(acpi) = self.acpi.try_get() {
|
||||
let Some(pinfo) = acpi.platform_info().ok().and_then(|p| p.processor_info) else {
|
||||
return;
|
||||
};
|
||||
|
||||
smp::start_ap_cores(&pinfo);
|
||||
}
|
||||
}
|
||||
|
||||
fn cpu_count() -> usize {
|
||||
CPU_COUNT.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
// Memory
|
||||
fn map_device_pages(&self, phys: usize, count: usize) -> Result<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 {
|
||||
unsafe fn init_physical_memory<'a, M: AbstractMemoryMap<'a>>(memory_map: &M) {
|
||||
fn set_boot_data(&self, boot_data: BootData) {
|
||||
match &boot_data {
|
||||
BootData::YBoot(data) => {
|
||||
// Setup initrd
|
||||
Self::init_initrd(
|
||||
data.initrd_address as usize,
|
||||
(data.initrd_address + data.initrd_size) as usize,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.boot_data.init(boot_data);
|
||||
}
|
||||
|
||||
fn init_initrd(initrd_start: usize, initrd_end: usize) {
|
||||
if initrd_start == 0 || initrd_end <= initrd_start {
|
||||
infoln!("No initrd loaded");
|
||||
return;
|
||||
}
|
||||
|
||||
let start_aligned = initrd_start & !0xFFF;
|
||||
let end_aligned = initrd_end & !0xFFF;
|
||||
|
||||
let data = unsafe {
|
||||
core::slice::from_raw_parts(
|
||||
initrd_start.virtualize() as *const _,
|
||||
initrd_end - initrd_start,
|
||||
)
|
||||
};
|
||||
|
||||
let initrd = Initrd {
|
||||
phys_page_start: start_aligned,
|
||||
phys_page_len: end_aligned - start_aligned,
|
||||
data,
|
||||
};
|
||||
|
||||
INITRD_DATA.init(initrd);
|
||||
}
|
||||
|
||||
fn init_acpi_from_boot_data(&self) {
|
||||
match *self.boot_data.get() {
|
||||
BootData::YBoot(data) => {
|
||||
self.init_acpi_from_rsdp(data.rsdp_address as usize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn init_acpi_from_rsdp(&self, address: usize) {
|
||||
let acpi_tables = unsafe { AcpiTables::from_rsdp(AcpiHandlerImpl, address).unwrap() };
|
||||
self.acpi.init(acpi_tables);
|
||||
}
|
||||
|
||||
unsafe fn init_physical_memory(&self) -> Result<(), Error> {
|
||||
// Reserve the lower 8MiB of memory
|
||||
reserve_region(
|
||||
"lower-memory",
|
||||
@ -318,9 +390,8 @@ impl X86_64 {
|
||||
size: 8 << 21,
|
||||
},
|
||||
);
|
||||
// Reserve memory map
|
||||
reserve_region("memory-map", memory_map.reserved_range());
|
||||
|
||||
// Reserve initrd
|
||||
if let Some(initrd) = INITRD_DATA.try_get() {
|
||||
reserve_region(
|
||||
"initrd",
|
||||
@ -331,21 +402,125 @@ impl X86_64 {
|
||||
);
|
||||
}
|
||||
|
||||
phys::init_from_iter(memory_map.iter().map(|r| PhysicalMemoryRegion {
|
||||
base: r.start_address(),
|
||||
size: r.page_count() * 0x1000,
|
||||
}))
|
||||
.expect("Failed to initialize the physical memory manager");
|
||||
match *self.boot_data.get() {
|
||||
BootData::YBoot(data) => {
|
||||
let memory_map = &data.memory_map;
|
||||
|
||||
// Reallocate the console buffer
|
||||
FB_CONSOLE
|
||||
.get()
|
||||
.reallocate_buffer()
|
||||
.expect("Failed to reallocate console buffer");
|
||||
reserve_region("memory-map", memory_map.reserved_range());
|
||||
|
||||
phys::init_from_iter(IterableMemoryMap::iter(memory_map).map(|r| {
|
||||
PhysicalMemoryRegion {
|
||||
base: AbstractAvailableRegion::start_address(r),
|
||||
size: AbstractAvailableRegion::page_count(r) * 0x1000,
|
||||
}
|
||||
}))
|
||||
.expect("Failed to initialize the physical memory manager");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_rsdp(&self, phys: usize) {
|
||||
self.rsdp_phys_base.init(phys);
|
||||
unsafe fn init_platform_from_acpi(&self, acpi: &AcpiTables<AcpiHandlerImpl>) {
|
||||
let platform_info = acpi.platform_info().unwrap();
|
||||
|
||||
let InterruptModel::Apic(apic_info) = platform_info.interrupt_model else {
|
||||
panic!("Processor does not have an APIC");
|
||||
};
|
||||
|
||||
self.ioapic.init(IoApic::from_acpi(&apic_info).unwrap());
|
||||
|
||||
let hpet = HpetInfo::new(acpi).unwrap();
|
||||
self.timer.init(Hpet::from_acpi(&hpet).unwrap());
|
||||
}
|
||||
|
||||
unsafe fn init_framebuffer(&'static self) {
|
||||
match *self.boot_data.get() {
|
||||
BootData::YBoot(data) => {
|
||||
let fb = &data.opt_framebuffer;
|
||||
self.framebuffer.init(
|
||||
LinearFramebuffer::from_physical_bits(
|
||||
fb.res_address as _,
|
||||
fb.res_size as _,
|
||||
fb.res_stride as _,
|
||||
fb.res_width as _,
|
||||
fb.res_height as _,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.fb_console.init(FramebufferConsole::from_framebuffer(
|
||||
self.framebuffer.get(),
|
||||
None,
|
||||
ConsoleBuffer::new(32).unwrap(),
|
||||
));
|
||||
|
||||
debug::add_sink(self.fb_console.get(), LogLevel::Info);
|
||||
|
||||
// Add a terminal to the devfs
|
||||
// TODO this is ugly
|
||||
let combined_terminal = CombinedTerminal::new(self.fb_console.get());
|
||||
self.combined_terminal.init(combined_terminal);
|
||||
|
||||
devfs::add_char_device(self.combined_terminal.get(), CharDeviceType::TtyRegular).unwrap();
|
||||
console::add_console_autoflush(self.fb_console.get());
|
||||
}
|
||||
|
||||
unsafe fn init_platform(&'static self, cpu_id: usize) {
|
||||
Cpu::init_local(LocalApic::new(), cpu_id as _);
|
||||
|
||||
if cpu_id == 0 {
|
||||
self.init_acpi_from_boot_data();
|
||||
Self::disable_8259();
|
||||
|
||||
// Initialize debug output as soon as possible
|
||||
let com1_3 = Box::leak(Box::new(ComPort::new(0x3F8, 0x3E8, IrqNumber::Isa(4))));
|
||||
debug::add_sink(com1_3.port_a(), LogLevel::Debug);
|
||||
// devfs::add_char_device(com1_3.port_a(), CharDeviceType::TtySerial).unwrap();
|
||||
|
||||
self.init_framebuffer();
|
||||
debug::reset();
|
||||
|
||||
log::set_logger(&LOGGER)
|
||||
.map(|_| log::set_max_level(log::LevelFilter::Trace))
|
||||
.ok();
|
||||
|
||||
let ps2 = Box::leak(Box::new(PS2Controller::new(
|
||||
IrqNumber::Isa(1),
|
||||
IrqNumber::Isa(12),
|
||||
0x64,
|
||||
0x60,
|
||||
)));
|
||||
ps2.init().unwrap();
|
||||
|
||||
// Print some stuff now that the output is initialized
|
||||
infoln!(
|
||||
"Yggdrasil v{} ({})",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
git_version!()
|
||||
);
|
||||
infoln!("Initializing x86_64 platform");
|
||||
|
||||
if let Some(acpi) = self.acpi.try_get() {
|
||||
self.init_platform_from_acpi(acpi);
|
||||
}
|
||||
|
||||
// Enable IRQs for the devices
|
||||
self.timer.get().init_irq().unwrap();
|
||||
ps2.connect(self.combined_terminal.get());
|
||||
ps2.init_irq().unwrap();
|
||||
|
||||
device::register_device(self.ioapic.get());
|
||||
device::register_device(self.timer.get());
|
||||
device::register_device(ps2);
|
||||
|
||||
infoln!("Device list:");
|
||||
for device in device::manager_lock().devices() {
|
||||
infoln!("* {}", device.display_name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn disable_8259() {
|
||||
@ -370,121 +545,3 @@ impl X86_64 {
|
||||
pic_slave_cmd.write(0x20);
|
||||
}
|
||||
}
|
||||
|
||||
/// x86-64 architecture + platform implementation
|
||||
pub static ARCHITECTURE: X86_64 = X86_64 {
|
||||
rsdp_phys_base: OneTimeInit::new(),
|
||||
yboot_framebuffer: OneTimeInit::new(),
|
||||
|
||||
acpi_processor_info: OneTimeInit::new(),
|
||||
|
||||
combined_terminal: OneTimeInit::new(),
|
||||
|
||||
ioapic: IoApic::new(),
|
||||
timer: Hpet::uninit(),
|
||||
|
||||
ps2: PS2Controller::new(IrqNumber::Isa(1), IrqNumber::Isa(12), 0x64, 0x60),
|
||||
com1_3: ComPort::new(0x3F8, 0x3E8, IrqNumber::Isa(4)),
|
||||
};
|
||||
|
||||
static LINEAR_FB: OneTimeInit<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()
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,11 @@ use core::time::Duration;
|
||||
|
||||
use abi::error::Error;
|
||||
use acpi::hpet::HpetInfo as AcpiHpet;
|
||||
use device_api::{
|
||||
interrupt::{InterruptHandler, IrqLevel, IrqOptions, IrqTrigger},
|
||||
timer::MonotonicTimestampProviderDevice,
|
||||
Device,
|
||||
};
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
@ -10,16 +15,11 @@ use tock_registers::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
arch::{x86_64::apic::IrqNumber, PLATFORM},
|
||||
device::{
|
||||
interrupt::InterruptSource, platform::Platform, timer::TimestampSource, Device,
|
||||
InitializableDevice,
|
||||
},
|
||||
arch::{x86_64::IrqNumber, Architecture, ARCHITECTURE},
|
||||
mem::device::DeviceMemoryIo,
|
||||
proc::wait,
|
||||
sync::IrqSafeSpinlock,
|
||||
task::tasklet,
|
||||
util::OneTimeInit,
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
@ -102,7 +102,7 @@ struct Inner {
|
||||
|
||||
/// HPET timer group interface
|
||||
pub struct Hpet {
|
||||
inner: OneTimeInit<IrqSafeSpinlock<Inner>>,
|
||||
inner: IrqSafeSpinlock<Inner>,
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
@ -135,17 +135,17 @@ impl Inner {
|
||||
}
|
||||
}
|
||||
|
||||
impl TimestampSource for Hpet {
|
||||
fn timestamp(&self) -> Result<Duration, Error> {
|
||||
let inner = self.inner.get().lock();
|
||||
impl MonotonicTimestampProviderDevice for Hpet {
|
||||
fn monotonic_timestamp(&self) -> Result<Duration, Error> {
|
||||
let inner = self.inner.lock();
|
||||
Ok(Duration::from_millis(inner.tim0_counter))
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptSource for Hpet {
|
||||
fn handle_irq(&self) -> Result<bool, Error> {
|
||||
impl InterruptHandler for Hpet {
|
||||
fn handle_irq(&self) -> bool {
|
||||
let now = {
|
||||
let mut inner = self.inner.get().lock();
|
||||
let mut inner = self.inner.lock();
|
||||
inner.regs.GeneralInterruptStatus.set(1);
|
||||
|
||||
inner.tim0_counter = inner.tim0_counter.wrapping_add(1);
|
||||
@ -155,7 +155,13 @@ impl InterruptSource for Hpet {
|
||||
wait::tick(now);
|
||||
tasklet::tick(now);
|
||||
|
||||
Ok(true)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Hpet {
|
||||
fn display_name(&self) -> &'static str {
|
||||
"HPET"
|
||||
}
|
||||
|
||||
unsafe fn init_irq(&'static self) -> Result<(), Error> {
|
||||
@ -163,8 +169,8 @@ impl InterruptSource for Hpet {
|
||||
const FS_IN_MS: u64 = 1000000000000;
|
||||
|
||||
// Configure timer 0
|
||||
let intc = PLATFORM.interrupt_controller();
|
||||
let mut inner = self.inner.get().lock();
|
||||
let intc = ARCHITECTURE.external_interrupt_controller();
|
||||
let mut inner = self.inner.lock();
|
||||
|
||||
// Calculate the interrupt period
|
||||
let clk_period = inner
|
||||
@ -206,9 +212,15 @@ impl InterruptSource for Hpet {
|
||||
let tim0_irq = tim0_irq.expect("Could not pick an IRQ for HPET TIM0");
|
||||
|
||||
// Bind and enable the IRQ
|
||||
let irq = IrqNumber::Gsi(tim0_irq as _);
|
||||
intc.register_handler(irq, self)?;
|
||||
intc.configure_irq(irq, true, true)?;
|
||||
let irq = IrqNumber::Gsi(tim0_irq);
|
||||
intc.register_irq(
|
||||
irq,
|
||||
IrqOptions {
|
||||
level: IrqLevel::ActiveLow,
|
||||
trigger: IrqTrigger::Level,
|
||||
},
|
||||
self,
|
||||
)?;
|
||||
intc.enable_irq(irq)?;
|
||||
|
||||
// Disable FSB interrupt route and 32 bit mode
|
||||
@ -245,32 +257,16 @@ impl InterruptSource for Hpet {
|
||||
}
|
||||
}
|
||||
|
||||
impl InitializableDevice for Hpet {
|
||||
type Options = AcpiHpet;
|
||||
|
||||
unsafe fn init(&self, info: Self::Options) -> Result<(), Error> {
|
||||
impl Hpet {
|
||||
/// Creates a HPET instance from its ACPI definition
|
||||
pub fn from_acpi(info: &AcpiHpet) -> Result<Self, Error> {
|
||||
infoln!("Initializing HPET:");
|
||||
infoln!("Address: {:#x}", info.base_address);
|
||||
|
||||
let inner = unsafe { Inner::new(info.base_address) }?;
|
||||
|
||||
self.inner.init(IrqSafeSpinlock::new(inner));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Hpet {
|
||||
fn name(&self) -> &'static str {
|
||||
"HPET"
|
||||
}
|
||||
}
|
||||
|
||||
impl Hpet {
|
||||
/// Constructs an uninitialized HPET instance
|
||||
pub const fn uninit() -> Self {
|
||||
Self {
|
||||
inner: OneTimeInit::new(),
|
||||
}
|
||||
Ok(Self {
|
||||
inner: IrqSafeSpinlock::new(inner),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,15 @@
|
||||
//! Intel 8042 PS/2 controller driver implemenation
|
||||
use abi::error::Error;
|
||||
use device_api::{
|
||||
input::{KeyboardConsumer, KeyboardProducer},
|
||||
interrupt::InterruptHandler,
|
||||
Device,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
arch::{
|
||||
x86_64::{apic::IrqNumber, intrinsics::IoPort},
|
||||
PLATFORM,
|
||||
},
|
||||
device::{
|
||||
input::KeyboardDevice, interrupt::InterruptSource, platform::Platform, tty::TtyDevice,
|
||||
Device,
|
||||
x86_64::{intrinsics::IoPort, IrqNumber},
|
||||
Architecture, ARCHITECTURE,
|
||||
},
|
||||
sync::IrqSafeSpinlock,
|
||||
};
|
||||
@ -24,7 +25,7 @@ struct Inner {
|
||||
shift: bool,
|
||||
ctrl: bool,
|
||||
|
||||
tty: Option<&'static dyn TtyDevice<16>>,
|
||||
consumer: Option<&'static dyn KeyboardConsumer>, // tty: Option<&'static dyn TtyDevice<16>>,
|
||||
}
|
||||
|
||||
/// PS/2 controller driver
|
||||
@ -80,37 +81,14 @@ impl Inner {
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardDevice for PS2Controller {
|
||||
fn attach(&self, terminal: &'static dyn TtyDevice<16>) {
|
||||
self.inner.lock().tty.replace(terminal);
|
||||
impl KeyboardProducer for PS2Controller {
|
||||
fn connect(&self, to: &'static dyn KeyboardConsumer) {
|
||||
self.inner.lock().consumer.replace(to);
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptSource for PS2Controller {
|
||||
unsafe fn init_irq(&'static self) -> Result<(), Error> {
|
||||
let mut inner = self.inner.lock();
|
||||
let intc = PLATFORM.interrupt_controller();
|
||||
|
||||
intc.register_handler(self.primary_irq, self)?;
|
||||
|
||||
// Disable PS/2 devices from sending any further data
|
||||
inner.send_command(0xAD);
|
||||
inner.send_command(0xA7);
|
||||
|
||||
// Flush the buffer
|
||||
while inner.command.read() & Inner::STATUS_OUTPUT_FULL != 0 {
|
||||
inner.data.read();
|
||||
}
|
||||
|
||||
// Enable primary port
|
||||
inner.send_command(0xAE);
|
||||
|
||||
intc.enable_irq(self.primary_irq)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_irq(&self) -> Result<bool, Error> {
|
||||
impl InterruptHandler for PS2Controller {
|
||||
fn handle_irq(&self) -> bool {
|
||||
let mut count = 0;
|
||||
let mut inner = self.inner.lock();
|
||||
loop {
|
||||
@ -177,21 +155,49 @@ impl InterruptSource for PS2Controller {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(tty) = inner.tty {
|
||||
tty.recv_byte(key);
|
||||
if let Some(consumer) = inner.consumer {
|
||||
consumer.handle_character(key).ok();
|
||||
}
|
||||
|
||||
count += 1;
|
||||
}
|
||||
|
||||
Ok(count != 0)
|
||||
count != 0
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for PS2Controller {
|
||||
fn name(&self) -> &'static str {
|
||||
fn display_name(&self) -> &'static str {
|
||||
"PS/2 Controller"
|
||||
}
|
||||
|
||||
unsafe fn init(&'static self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn init_irq(&'static self) -> Result<(), Error> {
|
||||
let mut inner = self.inner.lock();
|
||||
// let intc = PLATFORM.interrupt_controller();
|
||||
let intc = ARCHITECTURE.external_interrupt_controller();
|
||||
|
||||
intc.register_irq(self.primary_irq, Default::default(), self)?;
|
||||
|
||||
// Disable PS/2 devices from sending any further data
|
||||
inner.send_command(0xAD);
|
||||
inner.send_command(0xA7);
|
||||
|
||||
// Flush the buffer
|
||||
while inner.command.read() & Inner::STATUS_OUTPUT_FULL != 0 {
|
||||
inner.data.read();
|
||||
}
|
||||
|
||||
// Enable primary port
|
||||
inner.send_command(0xAE);
|
||||
|
||||
intc.enable_irq(self.primary_irq)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PS2Controller {
|
||||
@ -207,7 +213,7 @@ impl PS2Controller {
|
||||
data: IoPort::new(data_port),
|
||||
shift: false,
|
||||
ctrl: false,
|
||||
tty: None,
|
||||
consumer: None,
|
||||
};
|
||||
|
||||
Self {
|
||||
|
@ -1,9 +1,10 @@
|
||||
//! Driver for x86 COM ports
|
||||
use abi::error::Error;
|
||||
use device_api::{serial::SerialDevice, Device};
|
||||
|
||||
use crate::{
|
||||
arch::x86_64::{apic::IrqNumber, intrinsics::IoPort},
|
||||
arch::x86_64::{intrinsics::IoPort, IrqNumber},
|
||||
debug::DebugSink,
|
||||
device::{serial::SerialDevice, Device, InitializableDevice},
|
||||
sync::IrqSafeSpinlock,
|
||||
};
|
||||
|
||||
@ -13,11 +14,13 @@ struct Inner {
|
||||
lsr: IoPort<u8>,
|
||||
}
|
||||
|
||||
/// Single port of the COM port pair
|
||||
pub struct Port {
|
||||
inner: IrqSafeSpinlock<Inner>,
|
||||
}
|
||||
|
||||
// Port pair
|
||||
/// COM port pair
|
||||
#[allow(unused)]
|
||||
pub struct ComPort {
|
||||
port_a: Port,
|
||||
port_b: Port,
|
||||
@ -41,22 +44,22 @@ impl SerialDevice for Port {
|
||||
inner.dr.write(byte);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn receive(&self, blocking: bool) -> Result<u8, Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Port {
|
||||
fn name(&self) -> &'static str {
|
||||
"com-port"
|
||||
fn display_name(&self) -> &'static str {
|
||||
"COM port"
|
||||
}
|
||||
|
||||
unsafe fn init(&'static self) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Port {
|
||||
const LSR_THRE: u8 = 1 << 5;
|
||||
|
||||
pub const fn new(base: u16) -> Self {
|
||||
const fn new(base: u16) -> Self {
|
||||
Self {
|
||||
inner: IrqSafeSpinlock::new(Inner {
|
||||
dr: IoPort::new(base),
|
||||
@ -66,16 +69,8 @@ impl Port {
|
||||
}
|
||||
}
|
||||
|
||||
impl InitializableDevice for ComPort {
|
||||
type Options = ();
|
||||
|
||||
unsafe fn init(&self, _opts: Self::Options) -> Result<(), Error> {
|
||||
// TODO proper init
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ComPort {
|
||||
/// Constructs a COM port pair
|
||||
pub const fn new(port_a: u16, port_b: u16, irq: IrqNumber) -> Self {
|
||||
Self {
|
||||
port_a: Port::new(port_a),
|
||||
@ -84,6 +79,7 @@ impl ComPort {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the A port of this COM pair
|
||||
pub fn port_a(&self) -> &Port {
|
||||
&self.port_a
|
||||
}
|
||||
|
@ -2,8 +2,9 @@
|
||||
use core::fmt::{self, Arguments};
|
||||
|
||||
use abi::error::Error;
|
||||
use kernel_util::util::StaticVector;
|
||||
|
||||
use crate::{sync::IrqSafeSpinlock, util::StaticVector};
|
||||
use crate::sync::IrqSafeSpinlock;
|
||||
|
||||
const MAX_DEBUG_SINKS: usize = 4;
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
//! Bus devices
|
||||
|
||||
#[cfg(feature = "device-tree")]
|
||||
pub mod simple_bus;
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Simple "passthrough" bus device
|
||||
|
||||
use crate::{arch::aarch64::devtree::DevTreeIndexNodeExt, device, device_tree_driver};
|
||||
use crate::{device::devtree, device_tree_driver};
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["simple-bus"],
|
||||
@ -11,8 +11,8 @@ device_tree_driver! {
|
||||
let nodes = dt.node.children();
|
||||
|
||||
// Iterate devices on the bus
|
||||
device::enumerate_dt(address_cells, size_cells, nodes, |_, probe| {
|
||||
if let Some((device, _)) = device::probe_dt_node(&probe) {
|
||||
devtree::enumerate_dt(address_cells, size_cells, nodes, |_, probe| {
|
||||
if let Some((device, _)) = devtree::probe_dt_node(&probe) {
|
||||
unsafe {
|
||||
device.init()?;
|
||||
}
|
||||
|
454
src/device/devtree.rs
Normal file
454
src/device/devtree.rs
Normal 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)
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ use core::mem::size_of;
|
||||
use abi::{error::Error, primitive_enum};
|
||||
use alloc::vec::Vec;
|
||||
use bitflags::bitflags;
|
||||
use kernel_util::util::StaticVector;
|
||||
|
||||
use crate::{
|
||||
debug::DebugSink,
|
||||
@ -13,7 +14,6 @@ use crate::{
|
||||
},
|
||||
sync::IrqSafeSpinlock,
|
||||
task::tasklet::TaskFlow,
|
||||
util::StaticVector,
|
||||
};
|
||||
|
||||
const CONSOLE_ROW_LEN: usize = 128;
|
||||
@ -23,36 +23,31 @@ const DEFAULT_FG_COLOR: ColorAttribute = ColorAttribute::White;
|
||||
const DEFAULT_BG_COLOR: ColorAttribute = ColorAttribute::Blue;
|
||||
|
||||
primitive_enum! {
|
||||
#[allow(missing_docs)]
|
||||
#[doc = "Color attribute of a console character"]
|
||||
pub enum ColorAttribute: u8 {
|
||||
#[doc = "..."]
|
||||
Black = 0,
|
||||
#[doc = "..."]
|
||||
Red = 1,
|
||||
#[doc = "..."]
|
||||
Green = 2,
|
||||
#[doc = "..."]
|
||||
Yellow = 3,
|
||||
#[doc = "..."]
|
||||
Blue = 4,
|
||||
#[doc = "..."]
|
||||
Magenta = 5,
|
||||
#[doc = "..."]
|
||||
Cyan = 6,
|
||||
#[doc = "..."]
|
||||
White = 7,
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[doc = "Extra attributes of a console character"]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Attributes: u8 {
|
||||
#[allow(missing_docs)]
|
||||
const BOLD = 1 << 0;
|
||||
}
|
||||
}
|
||||
|
||||
impl ColorAttribute {
|
||||
pub fn from_vt100(val: u8) -> Self {
|
||||
fn from_vt100(val: u8) -> Self {
|
||||
match val {
|
||||
0..=7 => Self::try_from(val).unwrap(),
|
||||
_ => ColorAttribute::Red,
|
||||
@ -102,7 +97,7 @@ pub struct ConsoleBuffer {
|
||||
height: u32,
|
||||
}
|
||||
|
||||
pub enum EscapeState {
|
||||
enum EscapeState {
|
||||
Normal,
|
||||
Escape,
|
||||
Csi,
|
||||
@ -116,7 +111,9 @@ pub struct ConsoleState {
|
||||
pub cursor_col: u32,
|
||||
/// Current foreground color
|
||||
pub fg_color: ColorAttribute,
|
||||
/// Current background color
|
||||
pub bg_color: ColorAttribute,
|
||||
/// Current set of attributes
|
||||
pub attributes: Attributes,
|
||||
|
||||
esc_args: StaticVector<u32, MAX_CSI_ARGS>,
|
||||
@ -177,6 +174,7 @@ impl ConsoleChar {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the attributes of the character
|
||||
#[inline]
|
||||
pub fn attributes(self) -> (ColorAttribute, ColorAttribute, Attributes) {
|
||||
let fg =
|
||||
@ -256,8 +254,20 @@ impl ConsoleRow {
|
||||
|
||||
impl ConsoleBuffer {
|
||||
/// Constructs a fixed-size console buffer
|
||||
pub const fn fixed(rows: &'static mut [ConsoleRow], height: u32) -> Self {
|
||||
Self { rows, height }
|
||||
pub fn new(height: u32) -> Result<Self, Error> {
|
||||
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
|
||||
@ -394,12 +404,12 @@ impl ConsoleState {
|
||||
// Move back one character
|
||||
b'D' => {
|
||||
if self.cursor_col > 0 {
|
||||
self.cursor_col = self.cursor_col - 1;
|
||||
self.cursor_col -= 1;
|
||||
}
|
||||
}
|
||||
// Manipulate display attributes
|
||||
b'm' => {
|
||||
if let Some(arg) = self.esc_args.get(0) {
|
||||
if let Some(arg) = self.esc_args.first() {
|
||||
match arg {
|
||||
// Reset
|
||||
0 => {
|
||||
|
@ -112,11 +112,12 @@ impl FramebufferConsole {
|
||||
/// Constructs an instance of console from its framebuffer reference
|
||||
pub fn from_framebuffer(
|
||||
framebuffer: &'static LinearFramebuffer,
|
||||
font: &'static BitmapFont<'static>,
|
||||
font: Option<&'static BitmapFont<'static>>,
|
||||
buffer: ConsoleBuffer,
|
||||
) -> Self {
|
||||
let char_width = 6;
|
||||
let char_height = 12;
|
||||
let font = font.unwrap_or(&bitmap_font::tamzen::FONT_7x14);
|
||||
let char_width = font.width();
|
||||
let char_height = font.height();
|
||||
let dim = framebuffer.dimensions();
|
||||
|
||||
let inner = Inner {
|
||||
@ -151,12 +152,6 @@ impl Inner {
|
||||
text.draw(self).ok();
|
||||
}
|
||||
|
||||
fn fill_row(&mut self, y: u32, h: u32, val: u32) {
|
||||
let mut fb = unsafe { self.framebuffer.lock() };
|
||||
|
||||
fb.fill_rows(y, h, val);
|
||||
}
|
||||
|
||||
fn fill_rect(&mut self, x: u32, y: u32, w: u32, h: u32, val: u32) {
|
||||
let mut fb = unsafe { self.framebuffer.lock() };
|
||||
|
||||
|
@ -3,8 +3,9 @@
|
||||
use core::ops::{Index, IndexMut};
|
||||
|
||||
use abi::error::Error;
|
||||
use device_api::Device;
|
||||
|
||||
use crate::{device::Device, mem::device::DeviceMemory, sync::IrqSafeSpinlock};
|
||||
use crate::{mem::device::DeviceMemory, sync::IrqSafeSpinlock};
|
||||
|
||||
use super::{DisplayDevice, DisplayDimensions};
|
||||
|
||||
@ -76,7 +77,7 @@ impl LinearFramebuffer {
|
||||
}
|
||||
|
||||
impl Device for LinearFramebuffer {
|
||||
fn name(&self) -> &'static str {
|
||||
fn display_name(&self) -> &'static str {
|
||||
"Linear Framebuffer"
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
use super::Device;
|
||||
|
||||
pub mod console;
|
||||
|
||||
#[cfg(feature = "fb_console")]
|
||||
pub mod fb_console;
|
||||
pub mod linear_fb;
|
||||
|
||||
|
@ -1,178 +1,20 @@
|
||||
//! Device management and interfaces
|
||||
|
||||
use core::mem::size_of;
|
||||
|
||||
use abi::error::Error;
|
||||
use alloc::boxed::Box;
|
||||
use device_api::{manager::DeviceManager, Device, DeviceId};
|
||||
use fdt_rs::{index::DevTreeIndexNode, prelude::PropReader};
|
||||
|
||||
use crate::{
|
||||
arch::aarch64::devtree,
|
||||
sync::{IrqSafeSpinlock, IrqSafeSpinlockGuard},
|
||||
};
|
||||
use crate::sync::{IrqSafeSpinlock, IrqSafeSpinlockGuard};
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub mod devtree;
|
||||
|
||||
pub mod bus;
|
||||
pub mod display;
|
||||
pub mod power;
|
||||
pub mod serial;
|
||||
pub mod tty;
|
||||
|
||||
// pub mod display;
|
||||
|
||||
static DEVICE_MANAGER: IrqSafeSpinlock<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
|
||||
pub fn register_device(device: &'static dyn Device) -> DeviceId {
|
||||
debugln!("Register {:?}", device.display_name());
|
||||
|
@ -6,10 +6,8 @@ use device_api::{CpuBringupDevice, Device, ResetDevice};
|
||||
use fdt_rs::prelude::PropReader;
|
||||
|
||||
use crate::{
|
||||
arch::{
|
||||
aarch64::devtree::{self},
|
||||
Architecture, ArchitectureImpl, ARCHITECTURE,
|
||||
},
|
||||
arch::{Architecture, ArchitectureImpl, ARCHITECTURE},
|
||||
device::devtree,
|
||||
device_tree_driver,
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,10 @@
|
||||
//! Power-management related device drivers
|
||||
|
||||
pub mod arm_psci;
|
||||
pub mod sunxi_rwdog;
|
||||
use cfg_if::cfg_if;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(target_arch = "aarch64")] {
|
||||
pub mod arm_psci;
|
||||
pub mod sunxi_rwdog;
|
||||
}
|
||||
}
|
||||
|
@ -3,19 +3,17 @@
|
||||
use abi::error::Error;
|
||||
use alloc::boxed::Box;
|
||||
use device_api::{Device, ResetDevice};
|
||||
use kernel_util::util::OneTimeInit;
|
||||
use tock_registers::{
|
||||
interfaces::Writeable, register_bitfields, register_structs, registers::ReadWrite,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
arch::{
|
||||
aarch64::devtree::{self, DevTreeIndexNodePropGet, DevTreeIndexPropExt},
|
||||
Architecture, ARCHITECTURE,
|
||||
},
|
||||
arch::{Architecture, ARCHITECTURE},
|
||||
device::devtree::{self, DevTreeIndexNodePropGet, DevTreeIndexPropExt},
|
||||
device_tree_driver,
|
||||
mem::device::DeviceMemoryIo,
|
||||
sync::IrqSafeSpinlock,
|
||||
util::OneTimeInit,
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
|
@ -1,4 +1,10 @@
|
||||
//! Serial device interfaces
|
||||
|
||||
pub mod pl011;
|
||||
pub mod sunxi_uart;
|
||||
use cfg_if::cfg_if;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(target_arch = "aarch64")] {
|
||||
pub mod pl011;
|
||||
pub mod sunxi_uart;
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,8 @@
|
||||
//! ARM PL011 driver
|
||||
use abi::{error::Error, io::DeviceRequest};
|
||||
use alloc::boxed::Box;
|
||||
use device_api::{
|
||||
interrupt::{InterruptHandler, IrqNumber},
|
||||
serial::SerialDevice,
|
||||
Device,
|
||||
};
|
||||
use device_api::{interrupt::InterruptHandler, serial::SerialDevice, Device};
|
||||
use kernel_util::util::OneTimeInit;
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
@ -14,17 +11,16 @@ use tock_registers::{
|
||||
use vfs::CharDevice;
|
||||
|
||||
use crate::{
|
||||
arch::{
|
||||
aarch64::devtree::{self, DevTreeIndexPropExt},
|
||||
Architecture, ARCHITECTURE,
|
||||
},
|
||||
arch::{aarch64::IrqNumber, Architecture, ARCHITECTURE},
|
||||
debug::{self, DebugSink, LogLevel},
|
||||
device::tty::{CharRing, TtyDevice},
|
||||
device::{
|
||||
devtree::{self, DevTreeIndexPropExt},
|
||||
tty::{CharRing, TtyDevice},
|
||||
},
|
||||
device_tree_driver,
|
||||
fs::devfs::{self, CharDeviceType},
|
||||
mem::device::DeviceMemoryIo,
|
||||
sync::IrqSafeSpinlock,
|
||||
util::OneTimeInit,
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
|
@ -2,11 +2,8 @@
|
||||
|
||||
use abi::{error::Error, io::DeviceRequest};
|
||||
use alloc::boxed::Box;
|
||||
use device_api::{
|
||||
interrupt::{InterruptHandler, IrqNumber},
|
||||
serial::SerialDevice,
|
||||
Device,
|
||||
};
|
||||
use device_api::{interrupt::InterruptHandler, serial::SerialDevice, Device};
|
||||
use kernel_util::util::OneTimeInit;
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
@ -15,17 +12,16 @@ use tock_registers::{
|
||||
use vfs::CharDevice;
|
||||
|
||||
use crate::{
|
||||
arch::{
|
||||
aarch64::devtree::{self, DevTreeIndexPropExt},
|
||||
Architecture, ARCHITECTURE,
|
||||
},
|
||||
arch::{aarch64::IrqNumber, Architecture, ARCHITECTURE},
|
||||
debug::{self, DebugSink, LogLevel},
|
||||
device::tty::{CharRing, TtyDevice},
|
||||
device::{
|
||||
devtree::{self, DevTreeIndexPropExt},
|
||||
tty::{CharRing, TtyDevice},
|
||||
},
|
||||
device_tree_driver,
|
||||
fs::devfs::{self, CharDeviceType},
|
||||
mem::device::DeviceMemoryIo,
|
||||
sync::IrqSafeSpinlock,
|
||||
util::OneTimeInit,
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
|
@ -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>;
|
||||
}
|
@ -14,13 +14,13 @@ use crate::{
|
||||
|
||||
#[cfg(feature = "fb_console")]
|
||||
pub mod combined {
|
||||
//! Combined console + keyboard terminal device
|
||||
use abi::{error::Error, io::DeviceRequest};
|
||||
use device_api::{input::KeyboardConsumer, serial::SerialDevice};
|
||||
use vfs::CharDevice;
|
||||
|
||||
use crate::device::{
|
||||
display::{console::DisplayConsole, fb_console::FramebufferConsole},
|
||||
input::KeyboardDevice,
|
||||
serial::SerialDevice,
|
||||
Device,
|
||||
};
|
||||
|
||||
@ -29,21 +29,15 @@ pub mod combined {
|
||||
// TODO rewrite this
|
||||
/// Helper device to combine a display and a keyboard input into a single terminal
|
||||
pub struct CombinedTerminal {
|
||||
#[allow(dead_code)]
|
||||
input: &'static dyn KeyboardDevice,
|
||||
output: &'static dyn DisplayConsole,
|
||||
output: &'static (dyn DisplayConsole + Sync + 'static),
|
||||
|
||||
input_ring: CharRing<16>,
|
||||
}
|
||||
|
||||
impl CombinedTerminal {
|
||||
/// Create a combined terminal device from a keyboard and console output devices
|
||||
pub fn new(
|
||||
input: &'static dyn KeyboardDevice,
|
||||
output: &'static FramebufferConsole,
|
||||
) -> Self {
|
||||
pub fn new(output: &'static FramebufferConsole) -> Self {
|
||||
Self {
|
||||
input,
|
||||
output,
|
||||
input_ring: CharRing::new(),
|
||||
}
|
||||
@ -56,11 +50,14 @@ pub mod combined {
|
||||
}
|
||||
}
|
||||
|
||||
impl SerialDevice for CombinedTerminal {
|
||||
fn receive(&self, _blocking: bool) -> Result<u8, Error> {
|
||||
todo!()
|
||||
impl KeyboardConsumer for CombinedTerminal {
|
||||
fn handle_character(&self, data: u8) -> Result<(), Error> {
|
||||
self.recv_byte(data);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SerialDevice for CombinedTerminal {
|
||||
fn send(&self, byte: u8) -> Result<(), Error> {
|
||||
self.output.write_char(byte);
|
||||
Ok(())
|
||||
@ -68,7 +65,7 @@ pub mod combined {
|
||||
}
|
||||
|
||||
impl Device for CombinedTerminal {
|
||||
fn name(&self) -> &'static str {
|
||||
fn display_name(&self) -> &'static str {
|
||||
"Combined terminal device"
|
||||
}
|
||||
}
|
||||
|
@ -6,12 +6,11 @@ use abi::{
|
||||
io::{FileAttr, FileMode, FileType, OpenOptions},
|
||||
};
|
||||
use alloc::{boxed::Box, format, string::String};
|
||||
use kernel_util::util::OneTimeInit;
|
||||
use vfs::{
|
||||
CharDevice, CharDeviceWrapper, Vnode, VnodeImpl, VnodeKind, VnodeRef, DIR_POSITION_FROM_CACHE,
|
||||
};
|
||||
|
||||
use crate::util::OneTimeInit;
|
||||
|
||||
/// Describes the kind of a character device
|
||||
#[derive(Debug)]
|
||||
pub enum CharDeviceType {
|
||||
@ -24,16 +23,11 @@ pub enum CharDeviceType {
|
||||
struct DevfsDirectory;
|
||||
|
||||
impl VnodeImpl for DevfsDirectory {
|
||||
fn open(
|
||||
&mut self,
|
||||
_node: &VnodeRef,
|
||||
_opts: OpenOptions,
|
||||
_mode: FileMode,
|
||||
) -> Result<u64, Error> {
|
||||
fn open(&self, _node: &VnodeRef, _opts: OpenOptions, _mode: FileMode) -> Result<u64, Error> {
|
||||
Ok(DIR_POSITION_FROM_CACHE)
|
||||
}
|
||||
|
||||
fn metadata(&mut self, _node: &VnodeRef) -> Result<FileAttr, Error> {
|
||||
fn metadata(&self, _node: &VnodeRef) -> Result<FileAttr, Error> {
|
||||
Ok(FileAttr {
|
||||
size: 0,
|
||||
mode: FileMode::default_dir(),
|
||||
|
@ -2,17 +2,15 @@
|
||||
|
||||
use core::ptr::NonNull;
|
||||
|
||||
use kernel_util::util::OneTimeInit;
|
||||
use memfs::block::{self, BlockAllocator};
|
||||
use vfs::VnodeRef;
|
||||
use yggdrasil_abi::{error::Error, io::MountOptions};
|
||||
|
||||
use crate::{
|
||||
mem::{
|
||||
self,
|
||||
phys::{self, PageUsage},
|
||||
ConvertAddress,
|
||||
},
|
||||
util::OneTimeInit,
|
||||
use crate::mem::{
|
||||
self,
|
||||
phys::{self, PageUsage},
|
||||
ConvertAddress,
|
||||
};
|
||||
|
||||
pub mod devfs;
|
||||
|
@ -30,8 +30,8 @@ pub fn kinit() {
|
||||
{
|
||||
use core::time::Duration;
|
||||
|
||||
use device::display::console::task_update_consoles;
|
||||
use task::tasklet;
|
||||
use crate::device::display::console::task_update_consoles;
|
||||
use crate::task::tasklet;
|
||||
|
||||
tasklet::add_periodic(
|
||||
"update-console",
|
||||
|
@ -9,7 +9,8 @@
|
||||
arbitrary_self_types,
|
||||
const_mut_refs,
|
||||
let_chains,
|
||||
linked_list_cursors
|
||||
linked_list_cursors,
|
||||
rustc_private
|
||||
)]
|
||||
#![allow(clippy::new_without_default, clippy::fn_to_numeric_cast)]
|
||||
#![warn(missing_docs)]
|
||||
@ -17,7 +18,7 @@
|
||||
#![no_main]
|
||||
|
||||
use sync::SpinFence;
|
||||
use task::{context::Cpu, spawn_kernel_closure};
|
||||
use task::spawn_kernel_closure;
|
||||
|
||||
use crate::{
|
||||
arch::{Architecture, ArchitectureImpl, ARCHITECTURE},
|
||||
@ -27,6 +28,7 @@ use crate::{
|
||||
extern crate yggdrasil_abi as abi;
|
||||
|
||||
extern crate alloc;
|
||||
extern crate compiler_builtins;
|
||||
|
||||
#[macro_use]
|
||||
pub mod debug;
|
||||
@ -42,7 +44,6 @@ pub mod proc;
|
||||
pub mod sync;
|
||||
pub mod syscall;
|
||||
pub mod task;
|
||||
pub mod util;
|
||||
|
||||
static CPU_INIT_FENCE: SpinFence = SpinFence::new();
|
||||
|
||||
@ -75,7 +76,7 @@ pub fn kernel_main() -> ! {
|
||||
ARCHITECTURE.start_application_processors();
|
||||
}
|
||||
|
||||
Cpu::init_ipi_queues();
|
||||
// Cpu::init_ipi_queues();
|
||||
|
||||
// Wait until all APs initialize
|
||||
CPU_INIT_FENCE.signal();
|
||||
|
@ -286,70 +286,20 @@ pub fn validate_user_region(
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn memcpy(p0: *mut c_void, p1: *const c_void, len: usize) -> *mut c_void {
|
||||
let mut offset = 0;
|
||||
while offset < len {
|
||||
let c = (p1 as *const u8).add(offset).read_volatile();
|
||||
(p0 as *mut u8).add(offset).write_volatile(c);
|
||||
|
||||
offset += 1;
|
||||
}
|
||||
p0
|
||||
compiler_builtins::mem::memcpy(p0 as _, p1 as _, len) as _
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn memcmp(p0: *const c_void, p1: *const c_void, len: usize) -> i32 {
|
||||
let mut offset = 0;
|
||||
|
||||
if len == 0 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while offset < len {
|
||||
let c0 = (p0 as *const u8).add(offset).read_volatile();
|
||||
let c1 = (p1 as *const u8).add(offset).read_volatile();
|
||||
|
||||
#[allow(clippy::comparison_chain)]
|
||||
if c0 > c1 {
|
||||
return (c0 - c1) as i32;
|
||||
} else if c0 < c1 {
|
||||
return -((c1 - c0) as i32);
|
||||
}
|
||||
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
0
|
||||
compiler_builtins::mem::memcmp(p0 as _, p1 as _, len)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn memmove(dst: *mut c_void, src: *const c_void, len: usize) -> *mut c_void {
|
||||
if dst as usize == src as usize {
|
||||
return dst;
|
||||
}
|
||||
|
||||
if (src.add(len) as usize) <= (dst as usize) || (dst.add(len) as usize) <= (src as usize) {
|
||||
return memcpy(dst, src, len);
|
||||
}
|
||||
|
||||
if (dst as usize) < (src as usize) {
|
||||
let a = src as usize - dst as usize;
|
||||
memcpy(dst, src, a);
|
||||
memcpy(src as *mut _, src.add(a), len - a);
|
||||
} else {
|
||||
let a = dst as usize - src as usize;
|
||||
memcpy(dst.add(a), dst, len - a);
|
||||
memcpy(dst, src, a);
|
||||
}
|
||||
|
||||
dst
|
||||
compiler_builtins::mem::memmove(dst as _, src as _, len) as _
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn memset(dst: *mut c_void, val: i32, len: usize) -> *mut c_void {
|
||||
let mut offset = 0;
|
||||
while offset < len {
|
||||
(dst as *mut u8).add(offset).write_volatile(val as u8);
|
||||
offset += 1;
|
||||
}
|
||||
dst
|
||||
compiler_builtins::mem::memset(dst as _, val, len) as _
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
use core::{iter::StepBy, mem::size_of, ops::Range};
|
||||
|
||||
use abi::error::Error;
|
||||
use kernel_util::util::OneTimeInit;
|
||||
|
||||
use crate::{
|
||||
debug::LogLevel,
|
||||
@ -10,7 +11,6 @@ use crate::{
|
||||
ConvertAddress, /*, KERNEL_PHYS_BASE */
|
||||
},
|
||||
sync::IrqSafeSpinlock,
|
||||
util::OneTimeInit,
|
||||
};
|
||||
|
||||
use self::manager::PhysicalMemoryManager;
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Utilities for handling reserved memory regions
|
||||
|
||||
use crate::util::StaticVector;
|
||||
use kernel_util::util::StaticVector;
|
||||
|
||||
use super::PhysicalMemoryRegion;
|
||||
|
||||
|
@ -11,6 +11,7 @@ use abi::{
|
||||
};
|
||||
use alloc::{collections::VecDeque, rc::Rc, string::String};
|
||||
use atomic_enum::atomic_enum;
|
||||
use kernel_util::util::OneTimeInit;
|
||||
use vfs::VnodeRef;
|
||||
|
||||
use crate::{
|
||||
@ -22,7 +23,6 @@ use crate::{
|
||||
},
|
||||
sync::{IrqGuard, IrqSafeSpinlock},
|
||||
task::context::TaskContextImpl,
|
||||
util::OneTimeInit,
|
||||
};
|
||||
|
||||
use super::{context::TaskFrame, sched::CpuQueue, Cpu, ProcessId, TaskContext, PROCESSES};
|
||||
|
@ -3,12 +3,12 @@
|
||||
// use aarch64_cpu::registers::CNTPCT_EL0;
|
||||
use alloc::{collections::VecDeque, rc::Rc, vec::Vec};
|
||||
use cfg_if::cfg_if;
|
||||
use kernel_util::util::OneTimeInit;
|
||||
|
||||
use crate::{
|
||||
// arch::aarch64::{context::TaskContext, cpu::Cpu},
|
||||
arch::{Architecture, ArchitectureImpl},
|
||||
sync::{IrqSafeSpinlock, IrqSafeSpinlockGuard},
|
||||
util::OneTimeInit,
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
Loading…
x
Reference in New Issue
Block a user