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