dev: rework device management (&'static -> Arc)

This commit is contained in:
Mark Poliakov 2024-12-10 11:52:26 +02:00
parent 18bfeaf917
commit 56fbcefa80
125 changed files with 3493 additions and 3407 deletions

18
Cargo.lock generated

@ -1004,6 +1004,7 @@ dependencies = [
"abi-lib",
"async-trait",
"atomic_enum",
"bitflags 2.6.0",
"bytemuck",
"cfg-if",
"crossbeam-queue",
@ -1012,7 +1013,6 @@ dependencies = [
"futures-util",
"kernel-arch",
"libc",
"libk-device",
"libk-mm",
"libk-util",
"log",
@ -1025,16 +1025,6 @@ dependencies = [
"yggdrasil-abi",
]
[[package]]
name = "libk-device"
version = "0.1.0"
dependencies = [
"device-api",
"kernel-arch",
"libk-util",
"yggdrasil-abi",
]
[[package]]
name = "libk-mm"
version = "0.1.0"
@ -2165,6 +2155,7 @@ name = "ygg_driver_input"
version = "0.1.0"
dependencies = [
"async-trait",
"device-api",
"libk",
"libk-mm",
"libk-util",
@ -2179,7 +2170,6 @@ dependencies = [
"bytemuck",
"kernel-fs",
"libk",
"libk-device",
"libk-mm",
"libk-util",
"log",
@ -2225,7 +2215,7 @@ dependencies = [
"acpi",
"bitflags 2.6.0",
"device-api",
"libk-device",
"libk",
"libk-mm",
"libk-util",
"log",
@ -2355,9 +2345,7 @@ dependencies = [
"kernel-arch-interface",
"kernel-arch-x86",
"kernel-arch-x86_64",
"kernel-fs",
"libk",
"libk-device",
"libk-mm",
"libk-util",
"log",

@ -12,7 +12,6 @@ kernel-arch-interface.workspace = true
libk.workspace = true
libk-util.workspace = true
libk-mm.workspace = true
libk-device.workspace = true
elf.workspace = true
chrono.workspace = true
@ -34,7 +33,6 @@ ygg_driver_ahci = { path = "driver/block/ahci" }
ygg_driver_usb_xhci = { path = "driver/usb/xhci" }
ygg_driver_input = { path = "driver/input" }
kernel-fs = { path = "driver/fs/kernel-fs" }
memfs = { path = "driver/fs/memfs" }
ext2 = { path = "driver/fs/ext2" }

@ -7,7 +7,7 @@ extern crate alloc;
use core::sync::atomic::{AtomicUsize, Ordering};
use aarch64_cpu::registers::{DAIF, MPIDR_EL1, TPIDR_EL1};
use alloc::{boxed::Box, vec::Vec};
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use device_api::interrupt::{LocalInterruptController, MessageInterruptController};
use kernel_arch_interface::{
cpu::{CpuImpl, IpiQueue},
@ -29,7 +29,7 @@ pub struct ArchitectureImpl;
pub trait GicInterface: LocalInterruptController {}
pub struct PerCpuData {
pub gic: OneTimeInit<&'static dyn GicInterface>,
pub gic: OneTimeInit<Arc<dyn GicInterface>>,
}
static IPI_QUEUES: OneTimeInit<Vec<IpiQueue<ArchitectureImpl>>> = OneTimeInit::new();
@ -119,16 +119,24 @@ impl Architecture for ArchitectureImpl {
CPU_COUNT.load(Ordering::Acquire)
}
fn local_interrupt_controller() -> Option<&'static dyn LocalInterruptController> {
let local = Self::local_cpu_data()?;
let intc = *local.gic.try_get()?;
Some(intc)
fn message_interrupt_controller() -> Option<&'static dyn MessageInterruptController> {
None
}
fn message_interrupt_controller() -> &'static dyn MessageInterruptController {
fn local_interrupt_controller() -> Option<&'static dyn LocalInterruptController> {
todo!()
}
// fn local_interrupt_controller() -> Option<&'static dyn LocalInterruptController> {
// let local = Self::local_cpu_data()?;
// let intc = *local.gic.try_get()?;
// Some(intc)
// }
// fn message_interrupt_controller() -> &'static dyn MessageInterruptController {
// todo!()
// }
fn cpu_available_features<S: Scheduler>(_cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
None
}

@ -72,7 +72,7 @@ split_spinlock! {
use libk_mm_interface::KernelImageObject;
#[link_section = ".data.tables"]
static KERNEL_TABLES<lock: ArchitectureImpl>: KernelImageObject<FixedTables> =
static KERNEL_TABLES @ inner<lock: ArchitectureImpl>: KernelImageObject<FixedTables> =
unsafe { KernelImageObject::new(FixedTables::zeroed()) };
}

@ -106,11 +106,11 @@ impl Architecture for ArchitectureImpl {
1
}
fn message_interrupt_controller() -> &'static dyn MessageInterruptController {
unimplemented!()
fn local_interrupt_controller() -> Option<&'static dyn LocalInterruptController> {
None
}
fn local_interrupt_controller() -> Option<&'static dyn LocalInterruptController> {
fn message_interrupt_controller() -> Option<&'static dyn MessageInterruptController> {
None
}

@ -26,7 +26,7 @@ split_spinlock! {
use crate::ArchitectureImpl;
#[link_section = ".data.tables"]
static KERNEL_TABLES<lock: ArchitectureImpl>: KernelImageObject<FixedTables> = unsafe {
static KERNEL_TABLES @ inner<lock: ArchitectureImpl>: KernelImageObject<FixedTables> = unsafe {
KernelImageObject::new(FixedTables::zeroed())
};
}

@ -65,11 +65,11 @@ pub trait Architecture: Sized + 'static {
// Architectural devices
fn local_interrupt_controller() -> Option<&'static dyn LocalInterruptController> {
unimplemented!()
None
}
fn message_interrupt_controller() -> &'static dyn MessageInterruptController {
unimplemented!()
fn message_interrupt_controller() -> Option<&'static dyn MessageInterruptController> {
None
}
#[allow(unused)]

@ -160,7 +160,7 @@ pub macro split_spinlock(
$(use $use:path;)*
$(#[$meta:meta])*
static $name:ident<$lock:ident: $arch:ty>: $ty:ty = $init:expr;
static $name:ident @ $field:ident <$lock:ident: $arch:ty>: $ty:ty = $init:expr;
) {
pub use $name::$name;
@ -173,10 +173,14 @@ pub macro split_spinlock(
use core::sync::atomic::{AtomicBool, Ordering};
#[repr(transparent)]
pub struct __Wrapper(UnsafeCell<$ty>);
pub struct __Wrapper {
$field: UnsafeCell<$ty>
}
$(#[$meta])*
pub static $name: __Wrapper = __Wrapper(UnsafeCell::new($init));
pub static $name: __Wrapper = __Wrapper {
$field: UnsafeCell::new($init)
};
static __LOCK: AtomicBool = AtomicBool::new(false);
pub struct __Guard($crate::guard::IrqGuard<$arch>);
@ -197,13 +201,13 @@ pub macro split_spinlock(
type Target = $ty;
fn deref(&self) -> &Self::Target {
unsafe { &*$name.0.get() }
unsafe { &*$name.$field.get() }
}
}
impl core::ops::DerefMut for __Guard {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *$name.0.get() }
unsafe { &mut *$name.$field.get() }
}
}

@ -82,6 +82,13 @@ impl<T> OneTimeInit<T> {
Ok(value)
}
pub fn or_init_with<F: FnOnce() -> T>(&self, f: F) -> &T {
match self.try_init_with(f) {
Some(value) => value,
None => self.get(),
}
}
pub fn try_init_with<F: FnOnce() -> T>(&self, f: F) -> Option<&T> {
if self
.state

@ -9,7 +9,7 @@ use core::{
sync::atomic::{AtomicUsize, Ordering},
};
use alloc::vec::Vec;
use alloc::{boxed::Box, vec::Vec};
use device_api::interrupt::{LocalInterruptController, MessageInterruptController};
use kernel_arch_interface::{
cpu::{CpuImpl, IpiQueue},
@ -52,14 +52,16 @@ pub struct PerCpuData {
// 0x10, used in assembly
pub tmp_address: usize,
pub local_apic: &'static dyn LocalApicInterface,
pub local_apic: Box<dyn LocalApicInterface>,
// pub local_apic: &'static dyn LocalApicInterface,
pub available_features: CpuFeatures,
pub enabled_features: CpuFeatures,
}
impl PerCpuData {
pub fn local_apic(&self) -> &'static dyn LocalApicInterface {
self.local_apic
#[inline]
pub fn local_apic(&self) -> &dyn LocalApicInterface {
self.local_apic.as_ref()
}
}
@ -182,12 +184,12 @@ impl Architecture for ArchitectureImpl {
fn local_interrupt_controller() -> Option<&'static dyn LocalInterruptController> {
let cpu = Self::local_cpu_data()?;
Some(cpu.local_apic)
Some(cpu.local_apic.as_ref())
}
fn message_interrupt_controller() -> &'static dyn MessageInterruptController {
let local = Self::local_cpu_data().unwrap();
local.local_apic
fn message_interrupt_controller() -> Option<&'static dyn MessageInterruptController> {
let cpu = Self::local_cpu_data()?;
Some(cpu.local_apic.as_ref())
}
fn cpu_enabled_features<S: Scheduler>(cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {

@ -56,7 +56,7 @@ split_spinlock! {
use libk_mm_interface::KernelImageObject;
#[link_section = ".data.tables"]
static KERNEL_TABLES<lock: ArchitectureImpl>: KernelImageObject<FixedTables> =
static KERNEL_TABLES @ inner<lock: ArchitectureImpl>: KernelImageObject<FixedTables> =
unsafe { KernelImageObject::new(FixedTables::zeroed()) };
}

@ -166,7 +166,7 @@ impl CommandTable {
}
assert_eq!(prd, command.prd_count());
self.prdt[prd..].fill_with(|| PhysicalRegionDescriptor::zeroed());
self.prdt[prd..].fill_with(PhysicalRegionDescriptor::zeroed);
}
Ok(())

@ -4,19 +4,15 @@
extern crate alloc;
use alloc::{boxed::Box, format, vec, vec::Vec};
use alloc::{format, sync::Arc, vec::Vec};
use bytemuck::Zeroable;
use data::ReceivedFis;
use device_api::{
device::Device,
interrupt::{InterruptAffinity, InterruptHandler},
Device,
};
use error::AhciError;
use kernel_fs::devfs;
use libk::{
task::runtime,
vfs::block::{probe_partitions, NgBlockDeviceWrapper},
};
use libk::{devfs, device::manager::probe_partitions, task::runtime};
use libk_mm::{address::AsPhysicalAddress, device::DeviceMemoryIo, PageBox};
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use port::AhciPort;
@ -43,7 +39,7 @@ const MAX_DRIVES: usize = (b'z' - b'a') as usize;
pub struct AhciController {
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
ports: OneTimeInit<Vec<&'static AhciPort>>,
ports: OneTimeInit<Vec<Arc<AhciPort>>>,
received_fis_buffers: OneTimeInit<[Option<PageBox<ReceivedFis>>; 16]>,
version: Version,
@ -53,7 +49,7 @@ pub struct AhciController {
}
impl AhciController {
async fn late_init(&'static self) -> Result<(), AhciError> {
async fn late_init(self: Arc<Self>) -> Result<(), AhciError> {
log::info!("Initializing AHCI SATA Controller {:?}", self.version);
let regs = self.regs.lock();
@ -70,7 +66,7 @@ impl AhciController {
let pi = regs.PI.get();
let mut ports = vec![];
let mut ports = Vec::new();
drop(regs);
@ -117,7 +113,7 @@ impl AhciController {
drop(regs);
let port = match AhciPort::create(port, self, i) {
let port = match AhciPort::create(port, self.clone(), i) {
Ok(port) => port,
Err(error) => {
log::warn!("Port {} init error: {:?}", i, error);
@ -134,13 +130,13 @@ impl AhciController {
self.regs.lock().GHC.modify(GHC::IE::SET);
// Setup the detected ports
for (i, &port) in ports.iter().enumerate() {
for (i, port) in ports.iter().enumerate() {
log::info!("Init port {}", i);
port.init().await?;
port.init_inner().await?;
}
// Dump info about the drives
for (i, &port) in ports.iter().enumerate() {
for (i, port) in ports.iter().enumerate() {
let info = port.info().unwrap();
log::info!(
"Port {}: model={:?}, serial={:?}, lba_count={}",
@ -151,25 +147,8 @@ impl AhciController {
);
}
{
let mut lock = SATA_DRIVES.lock();
for &port in ports.iter() {
let n = lock.len();
if n >= MAX_DRIVES {
todo!("Too many drives, ran out of letters");
}
let n = n as u8;
lock.push(port);
let name = format!("sd{}", (n + b'a') as char);
let blk = NgBlockDeviceWrapper::new(port);
devfs::add_named_block_device(blk, name.clone()).ok();
probe_partitions(blk, move |index, partition| {
devfs::add_block_device_partition(name.clone(), index, partition)
})
.ok();
}
for port in ports.iter() {
register_sata_drive(port.clone(), true);
}
log::debug!("All ports initialized");
@ -179,7 +158,7 @@ impl AhciController {
}
impl InterruptHandler for AhciController {
fn handle_irq(&self, _vector: Option<usize>) -> bool {
fn handle_irq(self: Arc<Self>, _vector: Option<usize>) -> bool {
let regs = self.regs.lock();
let is = regs.IS.get();
@ -188,7 +167,7 @@ impl InterruptHandler for AhciController {
// Clear global interrupt status
regs.IS.set(u32::MAX);
for &port in ports {
for port in ports {
if is & (1 << port.index) != 0 {
port.handle_pending_interrupts();
}
@ -198,23 +177,27 @@ impl InterruptHandler for AhciController {
false
}
fn display_name(&self) -> &str {
"AHCI IRQ"
}
}
impl Device for AhciController {
unsafe fn init(&'static self) -> Result<(), Error> {
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
// Do the init in background
runtime::spawn(self.late_init())?;
Ok(())
}
fn display_name(&self) -> &'static str {
"AHCI SATA Controller"
fn display_name(&self) -> &str {
"AHCI Controller"
}
}
static SATA_DRIVES: IrqSafeSpinlock<Vec<&'static AhciPort>> = IrqSafeSpinlock::new(Vec::new());
static SATA_DRIVES: IrqSafeSpinlock<Vec<Arc<AhciPort>>> = IrqSafeSpinlock::new(Vec::new());
pub fn probe(info: &PciDeviceInfo) -> Result<&'static dyn Device, Error> {
pub fn probe(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
let bar5 = info.config_space.bar(5).ok_or(Error::InvalidOperation)?;
let bar5 = bar5.as_memory().ok_or(Error::InvalidOperation)?;
@ -241,7 +224,7 @@ pub fn probe(info: &PciDeviceInfo) -> Result<&'static dyn Device, Error> {
// TODO extract Number of Command Slots
let ahci = Box::leak(Box::new(AhciController {
let ahci = Arc::new(AhciController {
regs: IrqSafeSpinlock::new(regs),
ports: OneTimeInit::new(),
received_fis_buffers: OneTimeInit::new(),
@ -249,10 +232,43 @@ pub fn probe(info: &PciDeviceInfo) -> Result<&'static dyn Device, Error> {
max_port_count,
ahci_only,
has_64_bit,
}));
});
// TODO use multiple vectors if capable
info.map_interrupt(InterruptAffinity::Any, ahci)?;
info.map_interrupt(InterruptAffinity::Any, ahci.clone())?;
Ok(ahci)
}
pub fn register_sata_drive(drive: Arc<AhciPort>, probe: bool) {
let index = {
let mut drives = SATA_DRIVES.lock();
let index = drives.len();
if index >= MAX_DRIVES {
log::error!("Cannot add a SATA drive: too many of them");
return;
}
drives.push(drive.clone());
index
};
let letter = (index as u8 + b'a') as char;
let name = format!("sd{letter}");
log::info!("Register SATA drive: {name}");
devfs::add_named_block_device(drive.clone(), name.clone()).ok();
if probe {
runtime::spawn(async move {
let name = name;
log::info!("Probing partitions for {name}");
probe_partitions(drive, |index, partition| {
let partition_name = format!("{name}{}", index + 1);
devfs::add_named_block_device(Arc::new(partition), partition_name).ok();
})
.await
.ok();
})
.ok();
}
}

@ -5,12 +5,18 @@ use core::{
task::{Context, Poll},
};
use alloc::{boxed::Box, string::String};
use alloc::{boxed::Box, string::String, sync::Arc};
use async_trait::async_trait;
use bytemuck::Zeroable;
use device_api::device::Device;
use futures_util::task::AtomicWaker;
use libk::vfs::block::NgBlockDevice;
use libk_mm::{address::AsPhysicalAddress, device::DeviceMemoryIo, PageBox, PageSlice};
use libk::{device::block::BlockDevice, error::Error};
use libk_mm::{
address::{AsPhysicalAddress, PhysicalAddress},
device::DeviceMemoryIo,
table::MapAttributes,
PageBox, PageProvider, PageSlice,
};
use libk_util::{sync::IrqSafeSpinlock, waker::QueueWaker, OneTimeInit};
use tock_registers::interfaces::{Readable, Writeable};
@ -44,7 +50,7 @@ pub struct PortInfo {
#[allow(unused)]
pub struct AhciPort {
inner: IrqSafeSpinlock<PortInner>,
ahci: &'static AhciController,
ahci: Arc<AhciController>,
ty: PortType,
pub(crate) index: usize,
info: OneTimeInit<PortInfo>,
@ -120,9 +126,9 @@ impl PortInner {
impl AhciPort {
pub fn create(
regs: DeviceMemoryIo<'static, PortRegs>,
ahci: &'static AhciController,
ahci: Arc<AhciController>,
index: usize,
) -> Result<&'static Self, AhciError> {
) -> Result<Arc<Self>, AhciError> {
log::debug!("Initialize port {}", index);
regs.stop()?;
@ -160,7 +166,7 @@ impl AhciPort {
let command_available = QueueWaker::new();
let command_allocation = IrqSafeSpinlock::new(0);
Ok(Box::leak(Box::new(Self {
let port = Arc::new(Self {
inner: IrqSafeSpinlock::new(inner),
ty: PortType::Sata,
info: OneTimeInit::new(),
@ -170,10 +176,12 @@ impl AhciPort {
command_completion,
command_allocation,
command_available,
})))
});
Ok(port)
}
pub async fn init(&'static self) -> Result<(), AhciError> {
pub async fn init_inner(&self) -> Result<(), AhciError> {
let identify = self.perform_command(AtaIdentify::create()?).await?;
let model = identify.model_number.to_string();
@ -293,32 +301,38 @@ impl AhciPort {
}
#[async_trait]
impl NgBlockDevice for AhciPort {
type Error = AhciError;
async fn read(
impl BlockDevice for AhciPort {
async fn read_aligned(
&self,
lba: u64,
position: u64,
buffer: &mut PageSlice<MaybeUninit<u8>>,
) -> Result<(), AhciError> {
) -> Result<(), Error> {
if position % SECTOR_SIZE as u64 != 0 {
return Err(Error::InvalidOperation);
}
if buffer.len() % SECTOR_SIZE != 0 {
return Err(AhciError::InvalidBufferSize(buffer.len()));
return Err(Error::InvalidOperation);
}
let lba = position / SECTOR_SIZE as u64;
let command = AtaReadDmaEx::new(lba, buffer.len() / SECTOR_SIZE, buffer);
self.submit(&command).await?.wait_for_completion().await
self.submit(&command)
.await?
.wait_for_completion()
.await
.map_err(AhciError::into)
}
async fn write(&self, _lba: u64, _buffer: &PageSlice<u8>) -> Result<(), AhciError> {
// TODO AtaDmaWriteEx
Err(AhciError::FeatureNotImplemented)
async fn write_aligned(&self, _position: u64, _buffer: &PageSlice<u8>) -> Result<(), Error> {
// TODO AtaWriteDmaEx
Err(Error::NotImplemented)
}
fn block_size(&self) -> usize {
SECTOR_SIZE
}
fn block_count(&self) -> usize {
fn block_count(&self) -> u64 {
self.info().as_ref().map(|i| i.lba_count).unwrap() as _
}
@ -326,3 +340,28 @@ impl NgBlockDevice for AhciPort {
(MAX_PRD_SIZE * 2) / SECTOR_SIZE
}
}
impl Device for AhciPort {
fn display_name(&self) -> &str {
"AHCI SATA Drive"
}
}
impl PageProvider for AhciPort {
fn get_page(&self, _offset: u64) -> Result<PhysicalAddress, Error> {
todo!()
}
fn clone_page(
&self,
_offset: u64,
_src_phys: PhysicalAddress,
_src_attrs: MapAttributes,
) -> Result<PhysicalAddress, Error> {
todo!()
}
fn release_page(&self, _offset: u64, _phys: PhysicalAddress) -> Result<(), Error> {
todo!()
}
}

@ -1,30 +1,34 @@
use core::mem::MaybeUninit;
use alloc::{boxed::Box, format};
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use kernel_fs::devfs;
use libk::vfs::block::{probe_partitions, NgBlockDevice, NgBlockDeviceWrapper};
use libk_mm::{address::AsPhysicalAddress, PageSlice};
use device_api::device::Device;
use libk::{device::block::BlockDevice, error::Error};
use libk_mm::{
address::{AsPhysicalAddress, PhysicalAddress},
table::MapAttributes,
PageProvider, PageSlice,
};
use crate::{command::IdentifyNamespaceRequest, IoDirection};
use crate::{command::IdentifyNamespaceRequest, register_nvme_namespace, IoDirection};
use super::{error::NvmeError, NvmeController};
#[allow(unused)]
pub struct NvmeDrive {
controller: &'static NvmeController,
pub struct NvmeNamespace {
controller: Arc<NvmeController>,
nsid: u32,
total_lba_count: u64,
lba_size: u64,
max_lba_per_request: usize,
}
impl NvmeDrive {
impl NvmeNamespace {
pub async fn create(
controller: &'static NvmeController,
controller: Arc<NvmeController>,
nsid: u32,
max_transfer_size: usize,
) -> Result<&'static NvmeDrive, NvmeError> {
) -> Result<Arc<NvmeNamespace>, NvmeError> {
let admin_q = controller.admin_q.get();
let identify = admin_q.request(IdentifyNamespaceRequest { nsid }).await?;
@ -41,36 +45,44 @@ impl NvmeDrive {
(total_lba_count * lba_size) / (1024 * 1024),
max_lba_per_request,
);
let dev = Box::leak(Box::new(NvmeDrive {
let dev = NvmeNamespace {
controller,
nsid,
total_lba_count,
lba_size,
max_lba_per_request,
}));
};
let dev = Arc::new(dev);
let node_name = format!("nvme{}n{}", controller.controller_id.get(), nsid);
let blk = NgBlockDeviceWrapper::new(dev);
devfs::add_named_block_device(blk, node_name.clone()).ok();
probe_partitions(blk, move |index, partition| {
devfs::add_block_device_partition(format!("{}p", node_name), index, partition)
})
.ok();
register_nvme_namespace(dev.clone(), true);
Ok(dev)
}
pub fn controller_id(&self) -> u32 {
*self.controller.controller_id.get()
}
pub fn id(&self) -> u32 {
self.nsid
}
}
impl Device for NvmeNamespace {
fn display_name(&self) -> &str {
"NVMe Namespace"
}
}
#[async_trait]
impl NgBlockDevice for NvmeDrive {
type Error = NvmeError;
async fn read(
impl BlockDevice for NvmeNamespace {
async fn read_aligned(
&self,
lba: u64,
position: u64,
buffer: &mut PageSlice<MaybeUninit<u8>>,
) -> Result<(), NvmeError> {
) -> Result<(), Error> {
debug_assert_eq!(position % self.block_size() as u64, 0);
let lba = position / self.block_size() as u64;
debug_assert_eq!(buffer.len() % self.block_size(), 0);
let buffer_address = unsafe { buffer.as_physical_address() };
debug_assert_eq!(buffer_address.into_u64() % self.block_size() as u64, 0);
@ -90,9 +102,12 @@ impl NgBlockDevice for NvmeDrive {
log::info!(target: "io", "read #{lba}, {lba_count} blocks -> {result:?} @ {buffer_address:#x}");
result
result.map_err(NvmeError::into)
}
async fn write(&self, lba: u64, buffer: &PageSlice<u8>) -> Result<(), NvmeError> {
async fn write_aligned(&self, position: u64, buffer: &PageSlice<u8>) -> Result<(), Error> {
debug_assert_eq!(position % self.block_size() as u64, 0);
let lba = position / self.block_size() as u64;
debug_assert_eq!(buffer.len() % self.block_size(), 0);
let buffer_address = unsafe { buffer.as_physical_address() };
debug_assert_eq!(buffer_address.into_u64() % self.block_size() as u64, 0);
@ -112,17 +127,37 @@ impl NgBlockDevice for NvmeDrive {
log::info!(target: "io", "write -> #{lba}, {lba_count} blocks -> {result:?} @ {buffer_address:#x}");
result
result.map_err(NvmeError::into)
}
fn block_size(&self) -> usize {
self.lba_size as _
}
fn block_count(&self) -> usize {
self.total_lba_count as _
fn block_count(&self) -> u64 {
self.total_lba_count
}
fn max_blocks_per_request(&self) -> usize {
self.max_lba_per_request
}
}
impl PageProvider for NvmeNamespace {
fn get_page(&self, _offset: u64) -> Result<PhysicalAddress, Error> {
todo!()
}
fn clone_page(
&self,
_offset: u64,
_src_phys: PhysicalAddress,
_src_attrs: MapAttributes,
) -> Result<PhysicalAddress, Error> {
todo!()
}
fn release_page(&self, _offset: u64, _phys: PhysicalAddress) -> Result<(), Error> {
todo!()
}
}

@ -10,14 +10,18 @@ use core::{
time::Duration,
};
use alloc::{boxed::Box, collections::BTreeMap, vec::Vec};
use alloc::{collections::BTreeMap, format, sync::Arc, vec::Vec};
use command::{IdentifyActiveNamespaceIdListRequest, IdentifyControllerRequest};
use device_api::{
device::Device,
interrupt::{InterruptAffinity, InterruptHandler},
Device,
};
use drive::NvmeDrive;
use libk::task::{cpu_count, cpu_index, runtime};
use drive::NvmeNamespace;
use libk::{
devfs,
device::manager::probe_partitions,
task::{cpu_count, cpu_index, runtime},
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo, L3_PAGE_SIZE};
use libk_util::{
sync::{IrqGuard, IrqSafeSpinlock},
@ -126,8 +130,8 @@ pub struct NvmeController {
admin_q: OneTimeInit<QueuePair>,
ioqs: OneTimeInit<Vec<QueuePair>>,
io_queue_count: AtomicUsize,
drive_table: IrqSafeSpinlock<BTreeMap<u32, &'static NvmeDrive>>,
controller_id: OneTimeInit<usize>,
drive_table: IrqSafeSpinlock<BTreeMap<u32, Arc<NvmeNamespace>>>,
controller_id: OneTimeInit<u32>,
pci: PciDeviceInfo,
@ -153,7 +157,7 @@ impl NvmeController {
const ADMIN_QUEUE_SIZE: usize = 32;
const IO_QUEUE_SIZE: usize = 32;
async fn create_queues(&'static self) -> Result<(), NvmeError> {
async fn create_queues(&self) -> Result<(), NvmeError> {
let admin_q = self.admin_q.get();
let io_queue_count = self.io_queue_count.load(Ordering::Acquire);
@ -205,14 +209,16 @@ impl NvmeController {
Ok(())
}
async fn late_init(&'static self) -> Result<(), NvmeError> {
async fn late_init(self: Arc<Self>) -> Result<(), NvmeError> {
register_nvme_controller(self.clone());
let io_queue_count = cpu_count();
self.io_queue_count.store(io_queue_count, Ordering::Release);
{
let range = self
.pci
.map_interrupt_multiple(0..io_queue_count + 1, InterruptAffinity::Any, self)
.map_interrupt_multiple(0..io_queue_count + 1, InterruptAffinity::Any, self.clone())
.unwrap();
// TODO handle different MSI range allocations
@ -221,8 +227,6 @@ impl NvmeController {
}
}
register_nvme_controller(self);
let admin_q = self.admin_q.get();
// Identify the controller
@ -244,7 +248,7 @@ impl NvmeController {
}
async fn enumerate_namespaces(
&'static self,
self: &Arc<Self>,
max_transfer_size: usize,
) -> Result<(), NvmeError> {
let admin_q = self.admin_q.get();
@ -257,7 +261,7 @@ impl NvmeController {
let list = &namespaces.entries[..count];
for &nsid in list {
match NvmeDrive::create(self, nsid, max_transfer_size).await {
match NvmeNamespace::create(self.clone(), nsid, max_transfer_size).await {
Ok(drive) => {
self.drive_table.lock().insert(nsid, drive);
}
@ -271,7 +275,7 @@ impl NvmeController {
}
pub async fn perform_io(
&'static self,
&self,
nsid: u32,
lba: u64,
lba_count: usize,
@ -320,7 +324,7 @@ impl NvmeController {
}
impl InterruptHandler for NvmeController {
fn handle_irq(&self, vector: Option<usize>) -> bool {
fn handle_irq(self: Arc<Self>, vector: Option<usize>) -> bool {
let vector = vector.expect("Only MSI-X interrupts are supported");
if vector == 0 {
@ -333,10 +337,14 @@ impl InterruptHandler for NvmeController {
false
}
}
fn display_name(&self) -> &str {
"NVMe IRQ"
}
}
impl Device for NvmeController {
unsafe fn init(&'static self) -> Result<(), Error> {
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
let regs = self.regs.lock();
let timeout = Duration::from_millis(regs.CAP.read(CAP::TO) * 500);
@ -400,12 +408,12 @@ impl Device for NvmeController {
self.admin_q.init(admin_q);
// Schedule late_init task
runtime::spawn(self.late_init())?;
runtime::spawn(self.clone().late_init())?;
Ok(())
}
fn display_name(&self) -> &'static str {
fn display_name(&self) -> &str {
"NVM Express Controller"
}
}
@ -413,10 +421,10 @@ impl Device for NvmeController {
// TODO
unsafe impl Sync for NvmeController {}
static NVME_CONTROLLERS: IrqSafeSpinlock<Vec<&'static NvmeController>> =
static NVME_CONTROLLERS: IrqSafeSpinlock<Vec<Arc<NvmeController>>> =
IrqSafeSpinlock::new(Vec::new());
pub fn probe(info: &PciDeviceInfo) -> Result<&'static dyn Device, Error> {
pub fn probe(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
let bar0 = info
.config_space
.bar(0)
@ -444,7 +452,7 @@ pub fn probe(info: &PciDeviceInfo) -> Result<&'static dyn Device, Error> {
return Err(Error::InvalidArgument);
}
Ok(Box::leak(Box::new(NvmeController {
let device = NvmeController {
regs: IrqSafeSpinlock::new(regs),
admin_q: OneTimeInit::new(),
ioqs: OneTimeInit::new(),
@ -456,12 +464,34 @@ pub fn probe(info: &PciDeviceInfo) -> Result<&'static dyn Device, Error> {
io_queue_count: AtomicUsize::new(1),
doorbell_shift,
min_page_size,
})))
};
Ok(Arc::new(device))
}
pub fn register_nvme_controller(ctrl: &'static NvmeController) {
pub fn register_nvme_controller(controller: Arc<NvmeController>) {
let mut list = NVME_CONTROLLERS.lock();
let id = list.len();
list.push(ctrl);
ctrl.controller_id.init(id);
list.push(controller.clone());
controller.controller_id.init(id as u32);
}
pub fn register_nvme_namespace(namespace: Arc<NvmeNamespace>, probe: bool) {
let name = format!("nvme{}n{}", namespace.controller_id(), namespace.id());
log::info!("Register NVMe namespace: {name}");
devfs::add_named_block_device(namespace.clone(), name.clone()).ok();
if probe {
runtime::spawn(async move {
let name = name;
log::info!("Probing partitions for {name}");
probe_partitions(namespace, |index, partition| {
let partition_name = format!("{name}p{}", index + 1);
devfs::add_named_block_device(Arc::new(partition), partition_name).ok();
})
.await
.inspect_err(|error| log::error!("{name}: partition probe failed: {error:?}"))
})
.ok();
}
}

@ -8,8 +8,8 @@ authors = ["Mark Poliakov <mark@alnyan.me>"]
yggdrasil-abi.workspace = true
device-api = { workspace = true, features = ["derive"] }
libk-mm.workspace = true
libk-device.workspace = true
libk-util.workspace = true
libk.workspace = true
log.workspace = true
bitflags.workspace = true

@ -1,6 +1,6 @@
//! PCI capability structures and queries
use alloc::{vec, vec::Vec};
use alloc::{sync::Arc, vec, vec::Vec};
use device_api::interrupt::{
InterruptAffinity, InterruptHandler, MessageInterruptController, MsiInfo,
};
@ -310,7 +310,7 @@ impl MsiXVectorTable<'_> {
end: usize,
ic: &C,
affinity: InterruptAffinity,
handler: &'static dyn InterruptHandler,
handler: Arc<dyn InterruptHandler>,
) -> Result<Vec<MsiInfo>, Error> {
assert!(end > start);
let mut range = vec![
@ -349,7 +349,7 @@ impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiData<'s, S> {
&mut self,
ic: &C,
affinity: InterruptAffinity,
handler: &'static dyn InterruptHandler,
handler: Arc<dyn InterruptHandler>,
) -> Result<MsiInfo, Error> {
let info = ic.register_msi(affinity, handler)?;

@ -2,10 +2,10 @@ use core::ops::Range;
use alloc::{sync::Arc, vec::Vec};
use device_api::{
device::Device,
interrupt::{InterruptAffinity, InterruptHandler, IrqOptions, MsiInfo},
Device,
};
use libk_device::{message_interrupt_controller, register_global_interrupt};
use libk::device::{message_interrupt_controller, register_global_interrupt};
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
use yggdrasil_abi::error::Error;
@ -77,13 +77,13 @@ pub enum PciMatch {
pub struct PciDriver {
pub(crate) name: &'static str,
pub(crate) check: PciMatch,
pub(crate) probe: fn(&PciDeviceInfo) -> Result<&'static dyn Device, Error>,
pub(crate) probe: fn(&PciDeviceInfo) -> Result<Arc<dyn Device>, Error>,
}
/// Used to store PCI bus devices which were enumerated by the kernel
pub struct PciBusDevice {
pub(crate) info: PciDeviceInfo,
pub(crate) driver: Option<&'static dyn Device>,
pub(crate) driver: Option<Arc<dyn Device>>,
}
impl PciDeviceInfo {
@ -138,14 +138,14 @@ impl PciDeviceInfo {
pub fn map_interrupt(
&self,
affinity: InterruptAffinity,
handler: &'static dyn InterruptHandler,
handler: Arc<dyn InterruptHandler>,
) -> Result<Option<MsiInfo>, Error> {
let mut irq = self.interrupt_config.get().write();
match &mut irq.configured_mode {
ConfiguredInterruptMode::MsiX(msix) => {
let info =
msix.register_range(0, 1, message_interrupt_controller(), affinity, handler)?;
msix.register_range(0, 1, message_interrupt_controller()?, affinity, handler)?;
Ok(Some(info[0]))
}
ConfiguredInterruptMode::Msi => {
@ -154,7 +154,7 @@ impl PciDeviceInfo {
.capability::<MsiCapability>()
.ok_or(Error::InvalidOperation)?;
let info = msi.register(message_interrupt_controller(), affinity, handler)?;
let info = msi.register(message_interrupt_controller()?, affinity, handler)?;
Ok(Some(info))
}
@ -174,7 +174,7 @@ impl PciDeviceInfo {
&self,
vector_range: Range<usize>,
affinity: InterruptAffinity,
handler: &'static dyn InterruptHandler,
handler: Arc<dyn InterruptHandler>,
) -> Result<Vec<MsiInfo>, Error> {
let mut irq = self.interrupt_config.get().write();
let start = vector_range.start;
@ -184,7 +184,7 @@ impl PciDeviceInfo {
ConfiguredInterruptMode::MsiX(msix) => msix.register_range(
start,
end,
message_interrupt_controller(),
message_interrupt_controller()?,
affinity,
handler,
),
@ -195,7 +195,7 @@ impl PciDeviceInfo {
fn try_map_legacy(
&self,
pin: PciInterruptPin,
handler: &'static dyn InterruptHandler,
handler: Arc<dyn InterruptHandler>,
) -> Result<(), Error> {
let src = PciInterrupt {
address: self.address,
@ -220,7 +220,7 @@ impl PciDeviceInfo {
fn try_map_legacy_line(
&self,
line: u8,
handler: &'static dyn InterruptHandler,
handler: Arc<dyn InterruptHandler>,
) -> Result<(), Error> {
log::debug!("PCI {} -> IRQ#{}", self.address, line);

@ -11,7 +11,7 @@ use acpi::mcfg::McfgEntry;
use alloc::{collections::BTreeMap, sync::Arc, vec::Vec};
use bitflags::bitflags;
use device::{PciBusDevice, PciDeviceInfo, PciDriver, PciInterrupt, PciInterruptRoute, PciMatch};
use device_api::Device;
use device_api::device::Device;
use libk_mm::address::PhysicalAddress;
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use space::legacy;
@ -578,9 +578,7 @@ fn setup_bus_device(device: &mut PciBusDevice) -> Result<(), Error> {
log::debug!(" -> {:?}", driver.name);
let instance = (driver.probe)(&device.info)?;
unsafe {
instance.init()?;
}
unsafe { instance.clone().init() }?;
device.driver.replace(instance);
break;
@ -613,7 +611,7 @@ pub fn register_class_driver(
class: u8,
subclass: Option<u8>,
prog_if: Option<u8>,
probe: fn(&PciDeviceInfo) -> Result<&'static dyn Device, Error>,
probe: fn(&PciDeviceInfo) -> Result<Arc<dyn Device>, Error>,
) {
PCI_DRIVERS.lock().push(PciDriver {
name,
@ -626,7 +624,7 @@ pub fn register_vendor_driver(
name: &'static str,
vendor_id: u16,
device_id: u16,
probe: fn(&PciDeviceInfo) -> Result<&'static dyn Device, Error>,
probe: fn(&PciDeviceInfo) -> Result<Arc<dyn Device>, Error>,
) {
PCI_DRIVERS.lock().push(PciDriver {
name,
@ -638,7 +636,7 @@ pub fn register_vendor_driver(
pub fn register_generic_driver(
name: &'static str,
check: fn(&PciDeviceInfo) -> bool,
probe: fn(&PciDeviceInfo) -> Result<&'static dyn Device, Error>,
probe: fn(&PciDeviceInfo) -> Result<Arc<dyn Device>, Error>,
) {
PCI_DRIVERS.lock().push(PciDriver {
name,

@ -10,14 +10,14 @@ use crate::{
};
pub struct UsbBusManager {
busses: IrqSafeRwLock<BTreeMap<u16, &'static dyn UsbHostController>>,
busses: IrqSafeRwLock<BTreeMap<u16, Arc<dyn UsbHostController>>>,
devices: IrqSafeRwLock<BTreeMap<UsbBusAddress, Arc<UsbDeviceAccess>>>,
last_bus_address: AtomicU16,
}
impl UsbBusManager {
pub fn register_bus(hc: &'static dyn UsbHostController) -> u16 {
pub fn register_bus(hc: Arc<dyn UsbHostController>) -> u16 {
let i = BUS_MANAGER.last_bus_address.fetch_add(1, Ordering::AcqRel);
BUS_MANAGER.busses.write().insert(i, hc);
i

@ -275,7 +275,7 @@ pub mod hid_keyboard {
unsafe { MaybeUninit::slice_assume_init_ref(&events[..event_count]) };
for &event in events {
log::debug!("Generic Keyboard: {:?}", event);
log::trace!("Generic Keyboard: {:?}", event);
ygg_driver_input::send_event(event);
}
}

@ -53,7 +53,7 @@ pub trait UsbDevice: Send + Sync {
fn port_number(&self) -> u8;
fn bus_address(&self) -> UsbBusAddress;
fn speed(&self) -> UsbSpeed;
fn controller(&self) -> &'static dyn UsbHostController;
fn controller_ref(&self) -> &dyn UsbHostController;
fn handle_detach(&self);

@ -19,4 +19,4 @@ pub use communication::{UsbControlTransfer, UsbDirection, UsbTransferStatus, Usb
pub trait UsbEndpoint: Sync {}
pub trait UsbHostController: Sync {}
pub trait UsbHostController: Sync + Send {}

@ -318,7 +318,7 @@ impl DirectoryNode {
let mut pos = pos;
let mut entry_count = 0;
let iter = DirentIter::new(&self.fs, &block, offset);
let iter = DirentIter::new(&self.fs, block, offset);
for (dirent, name, entry_offset) in iter {
pos = (index * self.fs.block_size as u64) + entry_offset as u64;

@ -12,11 +12,9 @@ use dir::DirectoryNode;
use file::RegularNode;
use inode::{InodeAccess, InodeCache};
use libk::{
device::block::{cache::DeviceMapper, BlockDevice},
error::Error,
vfs::{
block::{cache::DeviceMapper, BlockDevice},
Filesystem, FilesystemMountOption, NodeRef,
},
vfs::{Filesystem, FilesystemMountOption, NodeRef},
};
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
@ -99,7 +97,7 @@ impl Filesystem for Ext2Fs {
impl Ext2Fs {
pub async fn create<'a, I: Iterator<Item = FilesystemMountOption<'a>>>(
device: &'static dyn BlockDevice,
device: Arc<dyn BlockDevice>,
options: I,
) -> Result<NodeRef, Error> {
let mut cached = true;
@ -182,7 +180,7 @@ impl Ext2Fs {
(!unsupported, enabled)
}
async fn create_fs(device: &'static dyn BlockDevice, cached: bool) -> Result<Self, Error> {
async fn create_fs(device: Arc<dyn BlockDevice>, cached: bool) -> Result<Self, Error> {
let mut superblock = ExtendedSuperblock::zeroed();
device
.read_exact(
@ -461,7 +459,7 @@ impl Ext2Fs {
let block_offset = (pos % self.block_size as u64) as usize;
let amount = remaining.min(self.block_size - block_offset);
self.with_inode_block(&inode, block_index as u32, |block| {
self.with_inode_block(inode, block_index as u32, |block| {
buffer[offset..offset + amount]
.copy_from_slice(&block[block_offset..block_offset + amount]);
Ok(())

@ -1,124 +0,0 @@
//! Device virtual file system
use core::sync::atomic::{AtomicUsize, Ordering};
use alloc::{format, string::String};
use libk::vfs::{
block::BlockDevice,
impls::{fixed_symlink, MemoryDirectory},
AccessToken, CharDevice, Node, NodeFlags, NodeRef,
};
use libk_util::OneTimeInit;
use yggdrasil_abi::error::Error;
/// Describes the kind of a character device
#[derive(Debug)]
pub enum CharDeviceType {
/// Regular terminal
TtyRegular,
/// Serial terminal
TtySerial,
}
/// Describes the kind of a block device
#[derive(Debug)]
pub enum BlockDeviceType {
/// Framebuffer device
Framebuffer,
}
static DEVFS_ROOT: OneTimeInit<NodeRef> = OneTimeInit::new();
/// Sets up the device filesystem
pub fn init() {
let root = MemoryDirectory::empty();
DEVFS_ROOT.init(root);
}
/// Returns the root of the devfs.
///
/// # Panics
///
/// Will panic if the devfs hasn't yet been initialized.
pub fn root() -> &'static NodeRef {
DEVFS_ROOT.get()
}
pub fn set_node(name: &str, node: NodeRef) -> Result<(), Error> {
log::info!("Update node: {name}");
let root = DEVFS_ROOT.get();
root.remove_file(name, unsafe { AccessToken::authorized() })
.ok();
root.add_child(name, fixed_symlink(node))
}
/// Adds a character device with a custom name
pub fn add_named_char_device(dev: &'static dyn CharDevice, name: String) -> Result<(), Error> {
log::info!("Add char device: {}", name);
let node = Node::char(dev, NodeFlags::IN_MEMORY_PROPS);
DEVFS_ROOT.get().add_child(name, node)
}
/// Adds a block device with a custom name
pub fn add_named_block_device<S: Into<String>>(
dev: &'static dyn BlockDevice,
name: S,
) -> Result<NodeRef, Error> {
let name = name.into();
log::info!("Add block device: {}", name);
let node = Node::block(dev, NodeFlags::IN_MEMORY_PROPS);
DEVFS_ROOT.get().add_child(name, node.clone())?;
Ok(node)
}
pub fn add_block_device(
dev: &'static dyn BlockDevice,
kind: BlockDeviceType,
) -> Result<NodeRef, Error> {
static FB_COUNT: AtomicUsize = AtomicUsize::new(0);
let (count, prefix) = match kind {
BlockDeviceType::Framebuffer => (&FB_COUNT, "fb"),
};
let value = count.fetch_add(1, Ordering::AcqRel);
let name = format!("{}{}", prefix, value);
add_named_block_device(dev, name)
}
pub fn add_block_device_partition<S: Into<String>>(
base_name: S,
index: usize,
partition: &'static dyn BlockDevice,
) -> Result<(), Error> {
let base_name = base_name.into();
let name = format!("{}{}", base_name, index + 1);
log::info!("Add partition: {}", name);
let node = Node::block(partition, NodeFlags::IN_MEMORY_PROPS);
DEVFS_ROOT.get().add_child(name, node)
}
/// Adds a character device to the devfs
pub fn add_char_device(dev: &'static dyn CharDevice, kind: CharDeviceType) -> Result<(), Error> {
static TTY_COUNT: AtomicUsize = AtomicUsize::new(0);
static TTYS_COUNT: AtomicUsize = AtomicUsize::new(0);
let (count, prefix) = match kind {
CharDeviceType::TtyRegular => (&TTY_COUNT, "tty"),
CharDeviceType::TtySerial => (&TTYS_COUNT, "ttyS"),
};
let value = count.fetch_add(1, Ordering::AcqRel);
let name = format!("{}{}", prefix, value);
add_named_char_device(dev, name)
}

@ -117,7 +117,6 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
FileType::Directory => Ok(DirectoryNode::<A>::new()),
FileType::Symlink => {
let target = hdr.symlink_target()?;
log::info!("symlink {:?}", target);
Ok(fixed_path_symlink(target))
}
_ => todo!(),
@ -127,13 +126,6 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
fn from_slice_internal(self: &Rc<Self>, tar_data: &'static [u8]) -> Result<NodeRef, Error> {
let root = DirectoryNode::<A>::new();
for item in TarIterator::new(tar_data) {
let Ok((hdr, _)) = item else {
continue;
};
log::info!("{:?}", hdr.name.as_str().unwrap());
}
// 1. Create paths in tar
for item in TarIterator::new(tar_data) {
let Ok((hdr, _)) = item else {
@ -141,7 +133,6 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
};
let path = Path::from_str(hdr.name.as_str()?.trim_matches('/'));
log::debug!("Make path {:?}", path);
let (dirname, filename) = path.split_right();
let parent = Self::make_path(&root, dirname, true)?;

@ -8,5 +8,6 @@ yggdrasil-abi.workspace = true
libk-util.workspace = true
libk-mm.workspace = true
libk.workspace = true
device-api.workspace = true
async-trait.workspace = true

@ -4,10 +4,11 @@ extern crate alloc;
use core::task::{Context, Poll};
use alloc::boxed::Box;
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use libk::vfs::{CharDevice, FileReadiness};
use libk_util::ring::LossyRingQueue;
use device_api::device::Device;
use libk::{device::char::CharDevice, vfs::FileReadiness};
use libk_util::{ring::LossyRingQueue, OneTimeInit};
use yggdrasil_abi::{
error::Error,
io::{DeviceRequest, KeyboardKeyEvent},
@ -22,9 +23,15 @@ impl FileReadiness for KeyboardDevice {
}
}
impl Device for KeyboardDevice {
fn display_name(&self) -> &str {
"Keyboard input pseudo-device"
}
}
#[async_trait]
impl CharDevice for KeyboardDevice {
async fn read(&'static self, buf: &mut [u8]) -> Result<usize, Error> {
async fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {
if buf.len() < 4 {
return Ok(0);
}
@ -36,7 +43,7 @@ impl CharDevice for KeyboardDevice {
Ok(4)
}
fn read_nonblocking(&'static self, buf: &mut [u8]) -> Result<usize, Error> {
fn read_nonblocking(&self, buf: &mut [u8]) -> Result<usize, Error> {
if buf.len() < 4 {
return Ok(0);
}
@ -48,7 +55,7 @@ impl CharDevice for KeyboardDevice {
Ok(4)
}
fn is_writable(&self) -> bool {
fn is_writeable(&self) -> bool {
false
}
@ -62,7 +69,13 @@ impl CharDevice for KeyboardDevice {
}
static INPUT_QUEUE: LossyRingQueue<KeyboardKeyEvent> = LossyRingQueue::with_capacity(32);
pub static KEYBOARD_DEVICE: KeyboardDevice = KeyboardDevice;
static KEYBOARD_DEVICE: OneTimeInit<Arc<KeyboardDevice>> = OneTimeInit::new();
pub fn setup() -> Arc<KeyboardDevice> {
KEYBOARD_DEVICE
.or_init_with(|| Arc::new(KeyboardDevice))
.clone()
}
pub fn send_event(ev: KeyboardKeyEvent) {
INPUT_QUEUE.write(ev);

@ -7,7 +7,6 @@ edition = "2021"
yggdrasil-abi = { workspace = true, features = ["serde_kernel", "bytemuck"] }
libk-mm.workspace = true
libk-util.workspace = true
libk-device.workspace = true
libk.workspace = true
kernel-fs = { path = "../../fs/kernel-fs" }

@ -17,7 +17,7 @@ use yggdrasil_abi::{
use crate::l3::{arp::ArpTable, Route};
pub trait NetworkDevice: Sync {
pub trait NetworkDevice: Sync + Send {
fn transmit(&self, packet: PageBox<[u8]>) -> Result<(), Error>;
fn packet_prefix_size(&self) -> usize;
@ -26,7 +26,7 @@ pub trait NetworkDevice: Sync {
pub struct NetworkInterface {
pub(crate) name: Box<str>,
pub(crate) device: &'static dyn NetworkDevice,
pub(crate) device: Arc<dyn NetworkDevice>,
pub(crate) mac: MacAddress,
pub(crate) address: IrqSafeRwLock<Option<IpAddr>>,
@ -113,7 +113,7 @@ impl NetworkInterface {
pub fn register_interface(
ty: NetworkInterfaceType,
dev: &'static dyn NetworkDevice,
dev: Arc<dyn NetworkDevice>,
) -> Arc<NetworkInterface> {
let name = match ty {
NetworkInterfaceType::Ethernet => {

@ -12,9 +12,9 @@ use libk::{
block,
error::Error,
task::runtime::with_timeout,
time::monotonic_time,
vfs::{ConnectionSocket, FileReadiness, ListenerSocket, Socket},
};
use libk_device::monotonic_timestamp;
use libk_util::{
sync::{
spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockWriteGuard},
@ -245,8 +245,8 @@ impl TcpSocket {
let socket = {
let mut sockets = TCP_SOCKETS.write();
sockets.try_insert_with_ephemeral_port(local_ip, remote, |port| {
let t = monotonic_timestamp()?;
let tx_seq = t.as_micros() as u32;
let t = monotonic_time();
let tx_seq = t.as_millis() as u32;
let local = SocketAddr::new(local_ip, port);
let connection =
TcpConnection::new(local, remote, 16384, tx_seq, 0, TcpConnectionState::Closed);

@ -1,5 +1,8 @@
#![no_std]
extern crate alloc;
use alloc::sync::Arc;
use libk_mm::PageBox;
use libk_util::OneTimeInit;
use ygg_driver_net_core::{
@ -27,11 +30,10 @@ impl NetworkDevice for LoopbackDevice {
}
}
static LOOPBACK: OneTimeInit<LoopbackDevice> = OneTimeInit::new();
static LOOPBACK_ID: OneTimeInit<u32> = OneTimeInit::new();
pub fn init() {
let loopback = LOOPBACK.init(LoopbackDevice);
let loopback = Arc::new(LoopbackDevice);
let interface =
ygg_driver_net_core::register_interface(NetworkInterfaceType::Loopback, loopback);

@ -2,7 +2,7 @@ use core::{future::poll_fn, sync::atomic::Ordering, task::Poll, time::Duration};
use alloc::{boxed::Box, collections::BTreeMap, sync::Arc, vec, vec::Vec};
use atomic_enum::atomic_enum;
use device_api::{interrupt::InterruptHandler, Device};
use device_api::{device::Device, interrupt::InterruptHandler};
use futures_util::task::AtomicWaker;
use libk::task::runtime;
use libk_mm::{
@ -139,7 +139,7 @@ impl Xhci {
}
async fn assign_device(
&'static self,
self: Arc<Self>,
speed: PortSpeed,
slot_id: u8,
root_hub_port_number: u8,
@ -154,12 +154,12 @@ impl Xhci {
self.register_device_context(slot_id, unsafe { context.output.as_physical_address() });
self.command_ring
.address_device(self, slot_id, &mut input)
.address_device(&*self, slot_id, &mut input)
.await?;
self.register_endpoint(slot_id, 1, ring.clone());
let pipe = ControlPipe::new(self, slot_id, ring.clone());
let pipe = ControlPipe::new(self.clone(), slot_id, ring.clone());
// TODO: If the device is a Full-speed one, determine its max packet size for the control
// endpoint
@ -175,7 +175,7 @@ impl Xhci {
};
let device = XhciBusDevice {
xhci: self,
xhci: self.clone(),
slot_id,
port_id: root_hub_port_number,
bus_address,
@ -188,7 +188,7 @@ impl Xhci {
Ok(Box::new(device))
}
async fn port_task(&'static self, index: usize) -> Result<(), UsbError> {
async fn port_task(self: Arc<Self>, index: usize) -> Result<(), UsbError> {
let state = &self.port_states[index];
self.reset_port(index).await?;
@ -197,9 +197,12 @@ impl Xhci {
let speed =
PortSpeed::try_from(regs.portsc.port_speed()).map_err(|_| UsbError::PortInitFailed)?;
let slot_id = self.command_ring.enable_slot(self).await?;
let slot_id = self.command_ring.enable_slot(self.as_ref()).await?;
let device = self.assign_device(speed, slot_id, (index + 1) as _).await?;
let device = self
.clone()
.assign_device(speed, slot_id, (index + 1) as _)
.await?;
let device = UsbDeviceAccess::setup(device).await?;
let old = state.address.write().replace(device.bus_address());
@ -212,7 +215,7 @@ impl Xhci {
Ok(())
}
fn handle_device_attached(&'static self, port: usize) -> Result<(), UsbError> {
fn handle_device_attached(self: Arc<Self>, port: usize) -> Result<(), UsbError> {
log::info!("Port {}: device attached", port);
if let Err(err) = self.port_states[port].state.compare_exchange(
@ -229,7 +232,7 @@ impl Xhci {
Ok(())
}
fn handle_device_detached(&'static self, port: usize) -> Result<(), UsbError> {
fn handle_device_detached(&self, port: usize) -> Result<(), UsbError> {
let state = &self.port_states[port];
match state.state.swap(PortState::Disconnected, Ordering::Release) {
@ -252,7 +255,7 @@ impl Xhci {
}
}
fn handle_port_event(&'static self, port: usize) -> Result<(), UsbError> {
fn handle_port_event(self: Arc<Self>, port: usize) -> Result<(), UsbError> {
let state = &self.port_states[port];
let port_regs = self.regs.ports.read(port);
@ -299,11 +302,11 @@ impl Xhci {
}
}
fn handle_event(&'static self) {
fn handle_event(self: Arc<Self>) {
while let Some(event) = self.event_ring.try_dequeue() {
match event {
Event::PortChange(port) => {
self.handle_port_event(port - 1).ok();
self.clone().handle_port_event(port - 1).ok();
}
Event::CommandCompletion { address, reply } => {
self.command_ring.notify(address, reply);
@ -333,7 +336,7 @@ impl CommandExecutor for Xhci {
}
impl Device for Xhci {
unsafe fn init(&'static self) -> Result<(), Error> {
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
log::info!("Init USB xHCI");
self.regs.reset();
@ -345,31 +348,26 @@ impl Device for Xhci {
self.regs
.configure(&dcbaa, &self.command_ring, &self.event_ring, &erst);
let bus = UsbBusManager::register_bus(self);
let bus = UsbBusManager::register_bus(self.clone());
self.bus_address.init(bus);
for port in 0..self.port_count {
let p = self.regs.ports.read(port);
if p.portsc.current_connect_status() {
self.handle_device_attached(port).ok();
self.clone().handle_device_attached(port).ok();
}
}
Ok(())
}
unsafe fn init_irq(&'static self) -> Result<(), Error> {
log::info!("Init USB xHCI IRQ");
Ok(())
}
fn display_name(&self) -> &'static str {
fn display_name(&self) -> &str {
"USB xHCI"
}
}
impl InterruptHandler for Xhci {
fn handle_irq(&'static self, _vector: Option<usize>) -> bool {
fn handle_irq(self: Arc<Self>, _vector: Option<usize>) -> bool {
if let Some(status) = self.regs.handle_interrupt() {
if status.event_interrupt() {
self.handle_event();
@ -380,4 +378,8 @@ impl InterruptHandler for Xhci {
false
}
}
fn display_name(&self) -> &str {
"xHCI IRQ"
}
}

@ -25,7 +25,7 @@ pub struct XhciBusDevice {
pub(crate) speed: PortSpeed,
pub(crate) xhci: &'static Xhci,
pub(crate) xhci: Arc<Xhci>,
pub(crate) context: Arc<XhciContext<8>>,
pub(crate) rings: IrqSafeRwLock<Vec<Arc<dyn GenericTransferRing>>>,
@ -91,7 +91,7 @@ impl XhciBusDevice {
self.xhci
.command_ring
.configure_endpoint(self.xhci, self.slot_id, &mut input)
.configure_endpoint(self.xhci.as_ref(), self.slot_id, &mut input)
.await?;
self.rings.write().push(ring.clone());
@ -114,8 +114,8 @@ impl UsbDevice for XhciBusDevice {
self.bus_address
}
fn controller(&self) -> &'static dyn UsbHostController {
self.xhci
fn controller_ref(&self) -> &dyn UsbHostController {
self.xhci.as_ref()
}
fn open_interrupt_in_pipe(
@ -134,7 +134,7 @@ impl UsbDevice for XhciBusDevice {
)
.await?;
let pipe = InterruptInPipe::new(self.xhci, self.slot_id, number, dci, ring);
let pipe = InterruptInPipe::new(self.xhci.clone(), self.slot_id, number, dci, ring);
Ok(UsbInterruptInPipeAccess(Box::new(pipe)))
}

@ -10,9 +10,9 @@ mod pipe;
mod regs;
mod ring;
use alloc::boxed::Box;
use alloc::sync::Arc;
pub use controller::Xhci;
use device_api::{interrupt::InterruptAffinity, Device};
use device_api::{device::Device, interrupt::InterruptAffinity};
use libk_mm::PageBox;
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use regs::{Mapper, PortSpeed};
@ -86,7 +86,7 @@ impl XhciContext<8> {
}
}
pub fn probe(info: &PciDeviceInfo) -> Result<&'static dyn Device, Error> {
pub fn probe(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
// TODO Chip Hardware Reset
let bar0 = info
.config_space
@ -101,10 +101,10 @@ pub fn probe(info: &PciDeviceInfo) -> Result<&'static dyn Device, Error> {
info.config_space.set_command(cmd.bits());
let regs = unsafe { xhci_lib::Registers::new(bar0.try_into_usize().unwrap(), Mapper::new()) };
let xhci = Box::leak(Box::new(Xhci::new(regs)?));
let xhci = Arc::new(Xhci::new(regs)?);
info.init_interrupts(PreferredInterruptMode::Msi)?;
info.map_interrupt(InterruptAffinity::Any, xhci)?;
info.map_interrupt(InterruptAffinity::Any, xhci.clone())?;
Ok(xhci)
}

@ -17,13 +17,13 @@ use crate::{
};
pub struct ControlPipe {
xhci: &'static Xhci,
xhci: Arc<Xhci>,
ring: Arc<ControlTransferRing>,
}
#[allow(unused)]
pub struct InterruptInPipe {
xhci: &'static Xhci,
xhci: Arc<Xhci>,
slot_id: u8,
endpoint_id: u8,
@ -40,7 +40,7 @@ impl UsbControlPipe for ControlPipe {
setup: ControlTransferSetup,
data: Option<(PhysicalAddress, usize, UsbDirection)>,
) -> Result<UsbControlTransfer, UsbError> {
self.ring.start_transfer(self.xhci, setup, data)
self.ring.start_transfer(self.xhci.as_ref(), setup, data)
}
fn complete_transfer(&self, transfer: UsbControlTransfer) {
@ -49,7 +49,7 @@ impl UsbControlPipe for ControlPipe {
}
impl ControlPipe {
pub fn new(xhci: &'static Xhci, _slot_id: u8, ring: Arc<ControlTransferRing>) -> Self {
pub fn new(xhci: Arc<Xhci>, _slot_id: u8, ring: Arc<ControlTransferRing>) -> Self {
Self { xhci, ring }
}
}
@ -58,7 +58,7 @@ impl UsbGenericPipe for InterruptInPipe {}
impl UsbInterruptInPipe for InterruptInPipe {
fn start_read(&self, buffer: &mut PageBox<[u8]>) -> Result<UsbInterruptTransfer, UsbError> {
self.ring.start_transfer(self.xhci, buffer)
self.ring.start_transfer(self.xhci.as_ref(), buffer)
}
fn complete_transfer(&self, transfer: UsbInterruptTransfer) {
@ -68,7 +68,7 @@ impl UsbInterruptInPipe for InterruptInPipe {
impl InterruptInPipe {
pub fn new(
xhci: &'static Xhci,
xhci: Arc<Xhci>,
slot_id: u8,
endpoint_id: u8,
dci: u8,

@ -4,12 +4,14 @@ extern crate alloc;
use core::mem::MaybeUninit;
use alloc::{boxed::Box, vec::Vec};
use alloc::{sync::Arc, vec::Vec};
use command::{ControlLock, ScanoutInfo};
use device_api::Device;
use libk::device::display::{
register_display_device, DisplayDevice, DisplayDeviceWrapper, DisplayMode, DisplayOwner,
DriverFlags, FramebufferInfo, PixelFormat,
use device_api::device::Device;
use libk::device::{
display::{
DisplayDevice, DisplayMode, DisplayOwner, DriverFlags, FramebufferInfo, PixelFormat,
},
manager::DEVICE_REGISTRY,
};
use libk_mm::{
address::{PhysicalAddress, Virtualize},
@ -66,7 +68,7 @@ pub struct VirtioGpu<T: Transport> {
num_scanouts: usize,
}
impl<T: Transport> VirtioGpu<T> {
impl<T: Transport + 'static> VirtioGpu<T> {
pub fn new(transport: T, info: Option<PciDeviceInfo>) -> Result<Self, Error> {
// Read num-scanouts from device config
let Some(device_cfg) = transport.device_cfg() else {
@ -78,7 +80,7 @@ impl<T: Transport> VirtioGpu<T> {
num_scanouts.copy_from_slice(&device_cfg[8..12]);
let num_scanouts = u32::from_le_bytes(num_scanouts);
if num_scanouts < 1 || num_scanouts > 16 {
if !(1..=16).contains(&num_scanouts) {
log::error!("virtio-gpu has invalid num_scanouts: {num_scanouts}");
return Err(Error::InvalidArgument);
}
@ -135,7 +137,7 @@ impl<T: Transport> VirtioGpu<T> {
transport.write_device_status(status | DeviceStatus::DRIVER_OK);
}
fn setup_queues(&'static self) -> Result<(), Error> {
fn setup_queues(&self) -> Result<(), Error> {
// TODO cursorq
let mut transport = self.transport.lock();
@ -157,7 +159,7 @@ impl<T: Transport> VirtioGpu<T> {
ControlLock::new(control, transport)
}
fn setup_display(&'static self) -> Result<(), Error> {
fn setup_display(&self) -> Result<(), Error> {
let mut control = self.control();
let mut config = self.config.write();
@ -251,7 +253,7 @@ impl<T: Transport> VirtioGpu<T> {
}
impl<T: Transport + 'static> Device for VirtioGpu<T> {
unsafe fn init(&'static self) -> Result<(), Error> {
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
let status = self.begin_init()?;
self.setup_queues()?;
self.finish_init(status);
@ -261,16 +263,12 @@ impl<T: Transport + 'static> Device for VirtioGpu<T> {
self.setup_mode(0)?;
register_display_device(Box::leak(Box::new(DisplayDeviceWrapper::new(self))));
DEVICE_REGISTRY.display.register(self.clone(), false)?;
Ok(())
}
unsafe fn init_irq(&'static self) -> Result<(), Error> {
Ok(())
}
fn display_name(&self) -> &'static str {
fn display_name(&self) -> &str {
"VirtIO GPU Device"
}
}
@ -355,7 +353,7 @@ impl<T: Transport + 'static> DisplayDevice for VirtioGpu<T> {
let config = self.config.read();
if let Some(framebuffer) = config.framebuffer.as_ref() {
if output.len() < 1 {
if output.is_empty() {
return Err(1);
}
@ -391,7 +389,7 @@ impl<T: Transport + 'static> DisplayDevice for VirtioGpu<T> {
}
}
pub fn probe(info: &PciDeviceInfo) -> Result<&'static dyn Device, Error> {
pub fn probe(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
let space = &info.config_space;
let transport = PciTransport::from_config_space(space)
@ -400,8 +398,8 @@ pub fn probe(info: &PciDeviceInfo) -> Result<&'static dyn Device, Error> {
})
.map_err(|_| Error::InvalidArgument)?;
let device = VirtioGpu::new(transport, Some(info.clone()))?;
let device = Box::leak(Box::new(device));
let device = Arc::new(device);
// let device = Box::leak(Box::new(device));
Ok(device)
}

@ -5,11 +5,11 @@ extern crate alloc;
use core::mem::size_of;
use alloc::{boxed::Box, collections::BTreeMap};
use alloc::{collections::BTreeMap, sync::Arc};
use bytemuck::{Pod, Zeroable};
use device_api::{
device::Device,
interrupt::{InterruptAffinity, InterruptHandler},
Device,
};
use libk_mm::PageBox;
use libk_util::{
@ -68,7 +68,7 @@ impl Queues {
}
}
impl<T: Transport> VirtioNet<T> {
impl<T: Transport + 'static> VirtioNet<T> {
const PACKET_SIZE: usize = 4096;
pub fn new(transport: T, pci_device_info: Option<PciDeviceInfo>) -> Self {
@ -171,13 +171,13 @@ impl<T: Transport> VirtioNet<T> {
}
unsafe fn setup_queues(
&'static self,
self: Arc<Self>,
receive_count: usize,
transmit_count: usize,
) -> Result<(), Error> {
let receive_vector = if let Some(pci) = self.pci_device_info.as_ref() {
pci.init_interrupts(PreferredInterruptMode::Msi)?;
let info = pci.map_interrupt(InterruptAffinity::Any, self)?;
let info = pci.map_interrupt(InterruptAffinity::Any, self.clone())?;
info.map(|info| info.vector as u16)
} else {
@ -229,7 +229,7 @@ impl<T: Transport + 'static> NetworkDevice for VirtioNet<T> {
}
impl<T: Transport + 'static> InterruptHandler for VirtioNet<T> {
fn handle_irq(&self, vector: Option<usize>) -> bool {
fn handle_irq(self: Arc<Self>, vector: Option<usize>) -> bool {
#[allow(clippy::redundant_pattern_matching)]
if let Some(_) = vector {
// MSI/MSI-X
@ -253,6 +253,10 @@ impl<T: Transport + 'static> InterruptHandler for VirtioNet<T> {
queue_irq || config_irq
}
}
fn display_name(&self) -> &str {
"virtio-net transport IRQ"
}
}
// impl<T: Transport + 'static> MsiHandler for VirtioNet<T> {
@ -263,26 +267,27 @@ impl<T: Transport + 'static> InterruptHandler for VirtioNet<T> {
// }
impl<T: Transport + 'static> Device for VirtioNet<T> {
fn display_name(&self) -> &'static str {
fn display_name(&self) -> &str {
"VirtIO Network Device"
}
unsafe fn init(&'static self) -> Result<(), Error> {
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
let status = self.begin_init()?;
// TODO multiqueue
self.setup_queues(1, 1)?;
self.clone().setup_queues(1, 1)?;
self.finish_init(status);
let iface = ygg_driver_net_core::register_interface(NetworkInterfaceType::Ethernet, self);
let iface =
ygg_driver_net_core::register_interface(NetworkInterfaceType::Ethernet, self.clone());
self.interface_id.init(iface.id());
self.listen(64);
Ok(())
}
unsafe fn init_irq(&'static self) -> Result<(), Error> {
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
Ok(())
}
}
@ -295,13 +300,13 @@ fn cvt_error(error: ygg_driver_virtio_core::error::Error) -> Error {
}
}
pub fn probe(info: &PciDeviceInfo) -> Result<&'static dyn Device, Error> {
pub fn probe(info: &PciDeviceInfo) -> Result<Arc<dyn Device>, Error> {
let space = &info.config_space;
let transport = PciTransport::from_config_space(space).unwrap();
let device = VirtioNet::new(transport, Some(info.clone()));
let device = Box::leak(Box::new(device));
let device = Arc::new(device);
Ok(device)
}

@ -1,7 +0,0 @@
use yggdrasil_abi::error::Error;
use crate::{manager::DeviceManager, Device};
pub trait Bus: Device {
fn enumerate(&self, manager: &mut DeviceManager) -> Result<(), Error>;
}

@ -1,11 +1,8 @@
use alloc::sync::Arc;
use yggdrasil_abi::error::Error;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
#[repr(transparent)]
pub struct DeviceId(u64);
pub trait Device: Sync + 'static {
fn display_name(&self) -> &'static str;
pub trait Device: Sync + Send {
fn display_name(&self) -> &str;
/// Initializes the device, making it ready for operation.
/// The method is also responsible for registering the device with appropriate OS subsystems
@ -14,7 +11,7 @@ pub trait Device: Sync + 'static {
/// # Safety
///
/// The caller must make sure the function is only called once.
unsafe fn init(&'static self) -> Result<(), Error> {
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
Ok(())
}
@ -25,13 +22,18 @@ pub trait Device: Sync + 'static {
///
/// The caller must make sure the function is only called once. The caller must also make sure
/// the function is not called before the device's [Device::init] is called.
unsafe fn init_irq(&'static self) -> Result<(), Error> {
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
Ok(())
}
/// Deinitializes device into an "unusable"/"destroyed" state when it's removed. The device
/// should return an error for all following requests after calling this method.
///
/// # Safety
///
/// This function directly affects the device state, the caller must take care of proper
/// synchronization.
unsafe fn deinit(&self) -> Result<(), Error> {
Ok(())
}
}
impl From<usize> for DeviceId {
fn from(value: usize) -> Self {
Self(value as u64)
}
}

@ -1,24 +1,14 @@
use alloc::sync::Arc;
use yggdrasil_abi::error::Error;
use crate::Device;
use crate::device::Device;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Irq {
Private(u32),
External(u32),
}
/// Describes messages sent from some CPU to others
#[derive(Clone, Copy, PartialEq, Debug)]
#[repr(u64)]
pub enum IpiMessage {
/// Indicates that the sender CPU entered kernel panic and wants other CPUs to follow
Panic,
/// Indicates that the cores should either halt and wait for the caller to shut the system
/// down, or they should shut down by themselves, depending on the platform
Shutdown,
}
#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u32)]
pub enum IrqLevel {
@ -37,6 +27,12 @@ pub enum IrqTrigger {
Level,
}
#[derive(Default, Clone, Copy, Debug)]
pub struct IrqOptions {
pub level: IrqLevel,
pub trigger: IrqTrigger,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum IpiDeliveryTarget {
Specific(usize),
@ -44,12 +40,6 @@ pub enum IpiDeliveryTarget {
OtherCpus,
}
#[derive(Default, Clone, Copy, Debug)]
pub struct IrqOptions {
pub level: IrqLevel,
pub trigger: IrqTrigger,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct MsiInfo {
pub address: usize,
@ -65,15 +55,58 @@ pub enum InterruptAffinity {
Specific(usize),
}
pub trait InterruptTable {
fn handler(&self, index: usize) -> Option<&'static dyn InterruptHandler>;
/// Describes messages sent from some CPU to others
#[derive(Clone, Copy, PartialEq, Debug)]
#[repr(u64)]
pub enum IpiMessage {
/// Indicates that the sender CPU entered kernel panic and wants other CPUs to follow
Panic,
/// Indicates that the cores should either halt and wait for the caller to shut the system
/// down, or they should shut down by themselves, depending on the platform
Shutdown,
}
pub trait InterruptHandler: Sync + Send {
fn handle_irq(self: Arc<Self>, vector: Option<usize>) -> bool;
fn display_name(&self) -> &str;
}
pub trait InterruptTable: Sync {
fn handler(&self, index: usize) -> Option<&Arc<dyn InterruptHandler>>;
}
pub trait ExternalInterruptController: Device {
/// Performs IRQ delivery method configuration and registers a handler to execute when it is
/// fired
fn register_irq(
&self,
irq: Irq,
options: IrqOptions,
handler: Arc<dyn InterruptHandler>,
) -> Result<(), Error>;
/// Enables the specified IRQ (unmasks it)
fn enable_irq(&self, irq: Irq) -> Result<(), Error>;
/// Handles a single pending interrupt on this controller.
/// The function is intended for interrupt controllers which have internal registers to track
/// the IRQ index and the order of interrupt handling (if multiple are handled in sequence) is
/// platform/controller specific.
fn handle_pending_irqs(&self) {}
/// Handles a single pending interrupt with a known index on this controller.
/// The function is intended for interrupt controllers where vectors "know" their interrupt
/// index.
fn handle_specific_irq(&self, index: usize) {
let _ = index;
}
}
pub trait MessageInterruptController {
fn register_msi(
&self,
affinity: InterruptAffinity,
handler: &'static dyn InterruptHandler,
handler: Arc<dyn InterruptHandler>,
) -> Result<MsiInfo, Error> {
let mut range = [MsiInfo {
affinity,
@ -87,7 +120,7 @@ pub trait MessageInterruptController {
fn register_msi_range(
&self,
range: &mut [MsiInfo],
handler: &'static dyn InterruptHandler,
handler: Arc<dyn InterruptHandler>,
) -> Result<(), Error> {
Err(Error::NotImplemented)
}
@ -95,49 +128,75 @@ pub trait MessageInterruptController {
fn handle_msi(&self, #[allow(unused)] vector: usize) {}
}
pub trait ExternalInterruptController {
/// Performs IRQ delivery method configuration and registers a handler to execute when it is
/// fired
fn register_irq(
&self,
irq: Irq,
options: IrqOptions,
handler: &'static dyn InterruptHandler,
) -> Result<(), Error>;
/// Enables the specified IRQ (unmasks it)
fn enable_irq(&self, irq: Irq) -> Result<(), Error>;
/// Handles a single pending interrupt on this controller.
/// The function is intended for interrupt controllers which have internal registers to track
/// the IRQ index and the order of interrupt handling (if multiple are handled in sequence) is
/// platform/controller specific.
fn handle_pending_irqs(&self) {}
/// Handles a single pending interrupt with a known index on this controller.
/// The function is intended for interrupt controllers where vectors "know" their interrupt
/// index.
fn handle_specific_irq(&self, #[allow(unused)] index: usize) {}
}
pub trait LocalInterruptController {
fn send_ipi(&self, target: IpiDeliveryTarget, msg: IpiMessage) -> Result<(), Error>;
pub trait LocalInterruptController: Device {
/// Initializes the local interrupt controller for an Application Processor instance.
///
/// # Safety
///
/// The caller must ensure this function is only called once per each AP (and only for APs).
unsafe fn init_ap(&self) -> Result<(), Error>;
fn send_ipi(&self, target: IpiDeliveryTarget, msg: IpiMessage) -> Result<(), Error>;
}
pub trait InterruptHandler: Device {
fn handle_irq(&'static self, vector: Option<usize>) -> bool;
pub struct FixedInterruptTable<const N: usize> {
rows: [Option<Arc<dyn InterruptHandler>>; N],
}
pub struct FixedInterruptTable<const SIZE: usize> {
entries: [Option<&'static dyn InterruptHandler>; SIZE],
impl<F: Fn(Option<usize>) -> bool + Sync + Send> InterruptHandler for F {
fn handle_irq(self: Arc<Self>, vector: Option<usize>) -> bool {
self(vector)
}
fn display_name(&self) -> &str {
"<closure>"
}
}
impl<const N: usize> FixedInterruptTable<N> {
pub const fn new() -> Self {
Self {
rows: [const { None }; N],
}
}
pub fn insert(&mut self, index: usize, entry: Arc<dyn InterruptHandler>) -> Result<(), Error> {
let row = self.rows.get_mut(index).ok_or(Error::InvalidArgument)?;
if row.is_some() {
return Err(Error::AlreadyExists);
}
*row = Some(entry);
Ok(())
}
pub fn insert_least_loaded(
&mut self,
handler: Arc<dyn InterruptHandler>,
) -> Result<usize, Error> {
let index = self
.rows
.iter()
.position(|p| p.is_none())
.ok_or(Error::InvalidArgument)?;
self.rows[index].replace(handler);
Ok(index)
}
}
impl<const N: usize> InterruptTable for FixedInterruptTable<N> {
#[inline]
fn handler(&self, index: usize) -> Option<&Arc<dyn InterruptHandler>> {
self.rows.get(index).and_then(|p| p.as_ref())
}
}
// pub trait LocalInterruptController {
// }
//
// pub struct FixedInterruptTable<const SIZE: usize> {
// entries: [Option<&'static dyn InterruptHandler>; SIZE],
// }
//
impl IrqLevel {
pub fn override_default(self, value: IrqLevel) -> Self {
match self {
@ -155,39 +214,3 @@ impl IrqTrigger {
}
}
}
impl<const SIZE: usize> FixedInterruptTable<SIZE> {
pub const fn new() -> Self {
Self {
entries: [None; SIZE],
}
}
pub fn insert(
&mut self,
index: usize,
handler: &'static dyn InterruptHandler,
) -> Result<(), Error> {
if self.entries[index].is_some() {
todo!();
}
self.entries[index] = Some(handler);
Ok(())
}
pub fn insert_least_loaded(
&mut self,
handler: &'static dyn InterruptHandler,
) -> Result<usize, Error> {
let index = self.entries.iter().position(|p| p.is_none()).unwrap();
self.entries[index].replace(handler);
Ok(index)
}
}
impl<const SIZE: usize> InterruptTable for FixedInterruptTable<SIZE> {
fn handler(&self, index: usize) -> Option<&'static dyn InterruptHandler> {
self.entries[index]
}
}

@ -4,14 +4,12 @@
extern crate alloc;
pub mod bus;
pub mod device;
pub mod interrupt;
pub mod manager;
pub mod serial;
pub mod timer;
pub use device::{Device, DeviceId};
use device::Device;
use yggdrasil_abi::error::Error;
pub trait CpuBringupDevice: Device {

@ -1,25 +0,0 @@
use alloc::vec::Vec;
use crate::{Device, DeviceId};
pub struct DeviceManager {
devices: Vec<&'static dyn Device>,
}
impl DeviceManager {
pub const fn new() -> Self {
Self {
devices: Vec::new(),
}
}
pub fn register(&mut self, device: &'static dyn Device) -> DeviceId {
let id = DeviceId::from(self.devices.len());
self.devices.push(device);
id
}
pub fn devices(&self) -> impl Iterator<Item = &'static dyn Device> + '_ {
self.devices.iter().copied()
}
}

@ -1,7 +1,8 @@
use yggdrasil_abi::error::Error;
use crate::Device;
use crate::device::Device;
pub trait SerialDevice: Device {
fn send(&self, byte: u8) -> Result<(), Error>;
fn send_byte(&self, byte: u8) -> Result<(), Error>;
fn is_terminal(&self) -> bool;
}

@ -1,10 +1,8 @@
//! Interfaces for time-providing devices
use core::time::Duration;
use yggdrasil_abi::{error::Error, time::SystemTime};
use crate::Device;
use crate::device::Device;
/// Interface for precise timing devices
pub trait MonotonicTimestampProviderDevice: Device {
@ -15,7 +13,7 @@ pub trait MonotonicTimestampProviderDevice: Device {
///
/// * Is not an accurate wall-clock time or real-world time.
/// * Cannot be used for date/time managament purposes.
fn monotonic_timestamp(&self) -> Result<Duration, Error>;
fn monotonic_timestamp(&self) -> Result<SystemTime, Error>;
}
/// Interface for real-world time-telling devices

@ -2,8 +2,8 @@
use core::mem::size_of;
use alloc::boxed::Box;
use device_api::{Device, DeviceId};
use alloc::sync::Arc;
use device_api::device::Device;
use fdt_rs::index::DevTreeIndexNode;
use yggdrasil_abi::error::Error;
@ -39,7 +39,7 @@ macro_rules! device_tree_driver {
static __COMPATIBLE: [&str; __COMPATIBLE_LEN] = [$($compatible),+];
fn __probe($node: &$crate::dt::DevTreeNodeInfo) ->
Option<alloc::boxed::Box<dyn device_api::Device>> $probe_body
Option<alloc::sync::Arc<dyn device_api::device::Device>> $probe_body
core::arch::global_asm!(r#"
.pushsection .dt_probes, "a"
@ -57,7 +57,7 @@ macro_rules! device_tree_driver {
struct DevTreeProbe<'a> {
compatible: &'static [&'static str],
probe_func: fn(&'a DevTreeNodeInfo<'a, 'a, 'a>) -> Option<Box<dyn Device>>,
probe_func: fn(&'a DevTreeNodeInfo<'a, 'a, 'a>) -> Option<Arc<dyn Device>>,
}
fn iter_dt_probes<'a>() -> impl Iterator<Item = DevTreeProbe<'a>> {
@ -94,28 +94,24 @@ fn dt_match_compatible(compatible: &str) -> Option<DevTreeProbe> {
/// "Probes" a device tree node for any matching device, registering it if a compatible driver is
/// found
pub fn probe_dt_node<F: FnOnce(&'static dyn Device) -> DeviceId>(
dt: &DevTreeNodeInfo,
register: F,
) -> Option<(&'static dyn Device, DeviceId)> {
pub fn probe_dt_node(dt: &DevTreeNodeInfo) -> Option<Arc<dyn Device>> {
// TODO use list, not just the first item
let compatible = dt.node.prop("compatible")?;
let probe = dt_match_compatible(compatible)?;
let device = Box::leak((probe.probe_func)(dt)?);
let id = register(device);
Some((device, id))
let device = (probe.probe_func)(dt)?;
Some(device)
}
/// 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>,
F: FnMut(&str, DevTreeNodeInfo) -> Result<(), Error>,
>(
address_cells: usize,
size_cells: usize,
nodes: I,
f: F,
mut f: F,
) -> Result<(), usize> {
let mut failed_count = 0;

@ -12,7 +12,6 @@ crate-type = ["rlib", "dylib"]
[dependencies]
libk-mm.workspace = true
libk-util.workspace = true
libk-device.workspace = true
kernel-arch.workspace = true
abi-lib.workspace = true
yggdrasil-abi = { workspace = true, features = ["alloc", "serde"] }
@ -27,6 +26,7 @@ crossbeam-queue.workspace = true
serde_json.workspace = true
serde.workspace = true
bytemuck.workspace = true
bitflags.workspace = true
async-trait.workspace = true
static_assertions.workspace = true
elf.workspace = true

@ -1,11 +0,0 @@
[package]
name = "libk-device"
version = "0.1.0"
edition = "2021"
[dependencies]
libk-util.workspace = true
kernel-arch.workspace = true
yggdrasil-abi.workspace = true
device-api = { workspace = true, features = ["derive"] }

@ -1,81 +0,0 @@
#![no_std]
use core::time::Duration;
use device_api::{
interrupt::{
ExternalInterruptController, InterruptHandler, Irq, IrqOptions, LocalInterruptController,
MessageInterruptController,
},
timer::{MonotonicTimestampProviderDevice, RealTimeProviderDevice},
};
use kernel_arch::{Architecture, ArchitectureImpl};
use libk_util::OneTimeInit;
use yggdrasil_abi::{error::Error, time::SystemTime};
macro_rules! register_get {
($register_name:ident, $get_name:ident, $global:ident, $ty:ty) => {
static $global: OneTimeInit<$ty> = OneTimeInit::new();
pub fn $register_name(intc: $ty) {
$global.init(intc);
}
pub fn $get_name() -> $ty {
*$global.get()
}
};
}
register_get!(
register_external_interrupt_controller,
external_interrupt_controller,
EXTERNAL_INTC,
&'static dyn ExternalInterruptController
);
register_get!(
register_monotonic_timestamp_provider,
monotonic_timestamp_provider,
MONOTONIC_TIMER,
&'static dyn MonotonicTimestampProviderDevice
);
register_get!(
register_real_time_provider,
real_time_provider,
REAL_TIME_TIMER,
&'static dyn RealTimeProviderDevice
);
pub fn local_interrupt_controller() -> Option<&'static dyn LocalInterruptController> {
ArchitectureImpl::local_interrupt_controller()
}
pub fn message_interrupt_controller() -> &'static dyn MessageInterruptController {
ArchitectureImpl::message_interrupt_controller()
}
pub fn monotonic_timestamp() -> Result<Duration, Error> {
monotonic_timestamp_provider().monotonic_timestamp()
}
pub fn real_time() -> Result<SystemTime, Error> {
real_time_provider().real_timestamp()
}
#[inline]
pub fn register_global_interrupt(
irq: u32,
options: IrqOptions,
handler: &'static dyn InterruptHandler,
) -> Result<(), Error> {
let intc = external_interrupt_controller();
let irq = Irq::External(irq);
intc.register_irq(irq, options, handler)?;
intc.enable_irq(irq)?;
Ok(())
}

@ -95,12 +95,9 @@ pub struct StaticVector<T, const N: usize> {
impl<T, const N: usize> StaticVector<T, N> {
/// Constructs an empty instance of [StaticVector]
pub const fn new() -> Self
where
T: Copy,
{
pub const fn new() -> Self {
Self {
data: [MaybeUninit::uninit(); N],
data: [const { MaybeUninit::uninit() }; N],
len: 0,
}
}

@ -1,11 +1,14 @@
//! Utilities for debug information logging
// TODO
#![allow(missing_docs)]
use core::{
fmt::{self, Arguments},
sync::atomic::{AtomicBool, Ordering},
};
use abi::error::Error;
use libk::task::{process::Process, thread::Thread};
use alloc::sync::Arc;
use libk_util::{
ring::RingBuffer,
sync::{
@ -14,6 +17,9 @@ use libk_util::{
},
StaticVector,
};
use yggdrasil_abi::error::Error;
use crate::task::{process::Process, thread::Thread};
const MAX_DEBUG_SINKS: usize = 4;
const RING_LOGGER_CAPACITY: usize = 65536;
@ -71,42 +77,67 @@ pub trait DebugSink: Sync {
}
}
#[derive(Clone, Copy)]
struct DebugSinkWrapper {
inner: &'static dyn DebugSink,
level: LogLevel,
#[derive(Clone)]
pub enum DebugSinkWrapper {
Arc(LogLevel, Arc<dyn DebugSink>),
}
struct SinkWriter {
sink: &'static dyn DebugSink,
unsafe impl Send for DebugSinkWrapper {}
unsafe impl Sync for DebugSinkWrapper {}
// #[derive(Clone, Copy)]
// struct DebugSinkWrapper {
// // inner: &'static dyn DebugSink,
// level: LogLevel,
// }
struct SinkWriter<'a> {
sink: &'a dyn DebugSink,
}
impl fmt::Write for SinkWriter {
impl fmt::Write for SinkWriter<'_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.sink.puts(s).map_err(|_| fmt::Error)
}
}
impl DebugSinkWrapper {
#[inline]
pub fn sink(&self) -> &dyn DebugSink {
match self {
Self::Arc(_, arc) => arc.as_ref(),
}
}
#[inline]
pub fn level(&self) -> LogLevel {
match self {
Self::Arc(level, _) => *level,
}
}
}
impl log::Log for DebugSinkWrapper {
fn enabled(&self, metadata: &log::Metadata) -> bool {
LogLevel::from(metadata.level()) >= self.level
LogLevel::from(metadata.level()) >= self.level()
}
fn log(&self, record: &log::Record) {
use core::fmt::Write;
let level = LogLevel::from(record.level());
let sink = self.sink();
let file = record.file().unwrap_or("<???>");
let line = record.line().unwrap_or(0);
let args = record.args();
let (prefix, suffix) = if self.inner.supports_control_sequences() {
let (prefix, suffix) = if sink.supports_control_sequences() {
(level.log_prefix(), level.log_suffix())
} else {
("", "")
};
let mut writer = SinkWriter { sink: self.inner };
let mut writer = SinkWriter { sink };
match record.target() {
"program" => {
write!(writer, "{args}").ok();
@ -282,10 +313,8 @@ static DEBUG_SINKS: IrqSafeRwLock<StaticVector<DebugSinkWrapper, MAX_DEBUG_SINKS
pub static RING_LOGGER_SINK: RingLoggerSink = RingLoggerSink::new();
/// Adds a debugging output sink
pub fn add_sink(sink: &'static dyn DebugSink, level: LogLevel) {
DEBUG_SINKS
.write()
.push(DebugSinkWrapper { inner: sink, level });
pub fn add_sink(sink: Arc<dyn DebugSink>, level: LogLevel) {
DEBUG_SINKS.write().push(DebugSinkWrapper::Arc(level, sink));
}
/// Print a trace message coming from a process

64
kernel/libk/src/devfs.rs Normal file

@ -0,0 +1,64 @@
//! Device virtual file system
use alloc::{string::String, sync::Arc};
use libk_util::OneTimeInit;
use yggdrasil_abi::error::Error;
use crate::{
device::{block::BlockDevice, char::CharDevice},
vfs::{
impls::{fixed_symlink, MemoryDirectory},
AccessToken, Node, NodeFlags, NodeRef,
},
};
static DEVFS_ROOT: OneTimeInit<NodeRef> = OneTimeInit::new();
/// Sets up the device filesystem
pub fn init() {
let root = MemoryDirectory::empty();
DEVFS_ROOT.init(root);
}
/// Returns the root of the devfs.
///
/// # Panics
///
/// Will panic if the devfs hasn't yet been initialized.
pub fn root() -> &'static NodeRef {
DEVFS_ROOT.get()
}
pub fn set_node(name: &str, node: NodeRef) -> Result<(), Error> {
log::info!("Update node: {name}");
let root = DEVFS_ROOT.get();
root.remove_file(name, unsafe { AccessToken::authorized() })
.ok();
root.add_child(name, fixed_symlink(node))
}
/// Adds a character device with a custom name
pub fn add_named_char_device(dev: Arc<dyn CharDevice>, name: String) -> Result<(), Error> {
log::info!("Add char device: {}", name);
let node = Node::char(dev, NodeFlags::IN_MEMORY_PROPS);
DEVFS_ROOT.get().add_child(name, node)
}
/// Adds a block device with a custom name
pub fn add_named_block_device<S: Into<String>>(
dev: Arc<dyn BlockDevice>,
name: S,
) -> Result<NodeRef, Error> {
let name = name.into();
log::info!("Add block device: {}", name);
let node = Node::block(dev, NodeFlags::IN_MEMORY_PROPS);
DEVFS_ROOT.get().add_child(name, node.clone())?;
Ok(node)
}

@ -0,0 +1,650 @@
#![allow(clippy::missing_transmute_annotations)]
use core::{
marker::PhantomData,
ops::{Deref, DerefMut},
};
use alloc::sync::Arc;
use kernel_arch::mem::PhysicalMemoryAllocator;
use libk_mm::{address::PhysicalAddress, phys::GlobalPhysicalAllocator, PageBox};
use libk_util::{lru_hash_table::LruCache, sync::spin_rwlock::IrqSafeRwLock};
use yggdrasil_abi::error::Error;
use crate::task::sync::AsyncMutex;
use super::BlockDevice;
pub struct CachedSegment<
A: PhysicalMemoryAllocator<Address = PhysicalAddress> = GlobalPhysicalAllocator,
> {
data: PageBox<[u8], A>,
dirty: bool,
}
pub struct UncachedCache<
A: PhysicalMemoryAllocator<Address = PhysicalAddress> = GlobalPhysicalAllocator,
> {
device: Arc<dyn BlockDevice>,
block_size: usize,
_pd: PhantomData<A>,
}
pub enum DeviceMapper<
A: PhysicalMemoryAllocator<Address = PhysicalAddress> = GlobalPhysicalAllocator,
> {
Uncached(UncachedCache<A>),
Cached(BlockCache<A>),
}
pub struct BlockCache<
A: PhysicalMemoryAllocator<Address = PhysicalAddress> = GlobalPhysicalAllocator,
> {
device: Arc<dyn BlockDevice>,
block_size: usize,
segment_size: usize,
cache: AsyncMutex<LruCache<u64, Arc<IrqSafeRwLock<CachedSegment<A>>>>>,
}
impl DeviceMapper {
pub fn cached_with_capacity(
device: Arc<dyn BlockDevice>,
block_size: usize,
segment_size: usize,
bucket_capacity: usize,
bucket_count: usize,
) -> DeviceMapper {
DeviceMapper::cached_with_capacity_in(
device,
block_size,
segment_size,
bucket_capacity,
bucket_count,
)
}
pub fn uncached(device: Arc<dyn BlockDevice>, block_size: usize) -> DeviceMapper {
DeviceMapper::uncached_in(device, block_size)
}
}
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> DeviceMapper<A> {
pub fn cached_with_capacity_in(
device: Arc<dyn BlockDevice>,
block_size: usize,
segment_size: usize,
bucket_capacity: usize,
bucket_count: usize,
) -> DeviceMapper<A> {
let cache = BlockCache::<A>::with_capacity_in(
device,
block_size,
segment_size,
bucket_capacity,
bucket_count,
);
DeviceMapper::<A>::Cached(cache)
}
pub fn uncached_in(device: Arc<dyn BlockDevice>, block_size: usize) -> DeviceMapper<A> {
if block_size % device.block_size() != 0 {
panic!("Cache block size is not multiple of device block size");
}
let uncache = UncachedCache::<A> {
device,
block_size,
_pd: PhantomData,
};
Self::Uncached(uncache)
}
pub fn device(&self) -> &Arc<dyn BlockDevice> {
match self {
Self::Uncached(uncache) => uncache.device(),
Self::Cached(cache) => cache.device(),
}
}
pub async fn try_with<T, F: FnOnce(&[u8]) -> Result<T, Error>>(
&self,
pos: u64,
mapper: F,
) -> Result<T, Error> {
match self {
Self::Uncached(uncache) => uncache.try_with(pos, mapper).await,
Self::Cached(cache) => cache.try_with(pos, mapper).await,
}
}
pub async fn try_with_mut<T, F: FnOnce(&mut [u8]) -> Result<T, Error>>(
&self,
pos: u64,
size: usize,
mapper: F,
) -> Result<T, Error> {
match self {
Self::Uncached(uncache) => uncache.try_with_mut(pos, size, mapper).await,
Self::Cached(cache) => cache.try_with_mut(pos, size, mapper).await,
}
}
pub async fn flush(&self) -> Result<(), Error> {
match self {
Self::Uncached(_) => Ok(()),
Self::Cached(cache) => {
cache.flush().await;
Ok(())
}
}
}
}
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> UncachedCache<A> {
pub fn device(&self) -> &Arc<dyn BlockDevice> {
&self.device
}
pub async fn try_with<T, F: FnOnce(&[u8]) -> Result<T, Error>>(
&self,
pos: u64,
mapper: F,
) -> Result<T, Error> {
let mut data = PageBox::<_, A>::new_uninit_slice_in(self.block_size)?;
self.device.read_aligned(pos, data.as_slice_mut()).await?;
let result = mapper(unsafe { data.assume_init_slice_ref() })?;
Ok(result)
}
pub async fn try_with_mut<T, F: FnOnce(&mut [u8]) -> Result<T, Error>>(
&self,
pos: u64,
size: usize,
mapper: F,
) -> Result<T, Error> {
let mut data = PageBox::<_, A>::new_uninit_slice_in(self.block_size)?;
// No need to read a block only to then fully rewrite it
if size != self.block_size {
self.device.read_aligned(pos, data.as_slice_mut()).await?;
}
let mut data = unsafe { data.assume_init_slice() };
let result = mapper(&mut data[..])?;
self.device.write_aligned(pos, data.as_slice()).await?;
Ok(result)
}
}
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> BlockCache<A> {
pub fn with_capacity_in(
device: Arc<dyn BlockDevice>,
block_size: usize,
segment_size: usize,
bucket_capacity: usize,
bucket_count: usize,
) -> BlockCache<A> {
assert_eq!(
block_size % device.block_size(),
0,
"Block size is not a multiple of device block size"
);
assert_eq!(
segment_size % block_size,
0,
"Segment size is not a multiple of block size"
);
assert!(
segment_size >= block_size,
"Segment size is less than block size"
);
log::info!("New block cache: block: {block_size}B, segment: {segment_size}B, geometry: {bucket_capacity}x{bucket_count}");
log::info!(
"Worst-case memory usage: {}K",
(segment_size * bucket_capacity * bucket_count) / 1024
);
BlockCache {
device,
block_size,
segment_size,
cache: AsyncMutex::new(LruCache::with_capacity(bucket_capacity, bucket_count)),
}
}
pub fn device(&self) -> &Arc<dyn BlockDevice> {
&self.device
}
async fn evict_block(
&self,
segment_position: u64,
block: Arc<IrqSafeRwLock<CachedSegment<A>>>,
) {
let read = block.read();
if read.dirty {
assert_eq!(segment_position % self.segment_size as u64, 0);
if let Err(err) = self
.device
.write_aligned(segment_position, read.data.as_slice())
.await
{
log::error!("Disk error: flushing block {}: {:?}", segment_position, err);
}
}
}
async fn fetch_block(
&self,
segment_position: u64,
) -> Result<Arc<IrqSafeRwLock<CachedSegment<A>>>, Error> {
let mut data = PageBox::new_uninit_slice_in(self.segment_size)?;
self.device
.read_aligned(segment_position, data.as_slice_mut())
.await?;
let data = unsafe { data.assume_init_slice() };
Ok(Arc::new(IrqSafeRwLock::new(CachedSegment {
data,
dirty: false,
})))
}
async fn entry(
&self,
segment_position: u64,
) -> Result<Arc<IrqSafeRwLock<CachedSegment<A>>>, Error> {
assert_eq!(segment_position % self.segment_size as u64, 0);
let mut lock = self.cache.lock().await;
let (value, evicted) = lock
.try_get_or_insert_with_async(segment_position, || self.fetch_block(segment_position))
.await?;
if let Some((segment_position, block)) = evicted {
self.evict_block(segment_position, block).await;
}
Ok(value.clone())
}
pub async fn try_with<T, F: FnOnce(&[u8]) -> Result<T, Error>>(
&self,
block_position: u64,
mapper: F,
) -> Result<T, Error> {
let segment_position = block_position & !(self.segment_size as u64 - 1);
let segment_offset = (block_position & (self.segment_size as u64 - 1)) as usize;
let block = self.entry(segment_position).await?;
let result = mapper(&block.read()[segment_offset..segment_offset + self.block_size])?;
Ok(result)
}
pub async fn try_with_mut<T, F: FnOnce(&mut [u8]) -> Result<T, Error>>(
&self,
block_position: u64,
_size: usize,
mapper: F,
) -> Result<T, Error> {
let segment_position = block_position & !(self.segment_size as u64 - 1);
let segment_offset = (block_position & (self.segment_size as u64 - 1)) as usize;
let block = self.entry(segment_position).await?;
let mut block = block.write();
block.dirty = true;
let result = mapper(&mut block[segment_offset..segment_offset + self.block_size])?;
Ok(result)
}
pub async fn flush(&self) {
for (pos, block) in self.cache.lock().await.flush() {
self.evict_block(pos, block).await;
}
}
}
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> CachedSegment<A> {
pub fn set_dirty(&mut self) {
self.dirty = true;
}
}
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> Deref for CachedSegment<A> {
type Target = PageBox<[u8], A>;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> DerefMut for CachedSegment<A> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.dirty = true;
&mut self.data
}
}
// #[cfg(test)]
// mod tests {
// use core::{
// ffi::c_void,
// mem::MaybeUninit,
// ptr::null_mut,
// sync::atomic::{AtomicBool, Ordering},
// };
// use std::{io, sync::Mutex};
//
// use async_trait::async_trait;
// use kernel_arch::mem::PhysicalMemoryAllocator;
// use libk_mm::{address::PhysicalAddress, PageBox, PageSlice};
// use yggdrasil_abi::error::Error;
//
// use crate::vfs::block::{BlockDevice, NgBlockDevice, NgBlockDeviceWrapper};
//
// use super::BlockCache;
//
// struct DummyBlock {
// block_size: usize,
// block_count: usize,
// deny_writes: AtomicBool,
// data: Mutex<Vec<u8>>,
// }
//
// struct PA;
//
// impl DummyBlock {
// pub fn new(block_size: usize, block_count: usize) -> Self {
// let mut data = vec![0; block_size * block_count];
// for i in 0..block_count {
// let block = &mut data[block_size * i..block_size * (i + 1)];
// block.fill(i as u8);
// }
// Self {
// data: Mutex::new(data),
// deny_writes: AtomicBool::new(false),
// block_size,
// block_count,
// }
// }
// }
//
// #[async_trait::async_trait]
// impl NgBlockDevice for DummyBlock {
// type Error = Error;
//
// async fn read(
// &self,
// lba: u64,
// buffer: &mut PageSlice<MaybeUninit<u8>>,
// ) -> Result<(), Error> {
// let start = lba as usize * self.block_size;
// let end = start + buffer.len();
//
// if end > self.block_count * self.block_size {
// return Err(Error::InvalidArgument);
// }
//
// let data = self.data.lock().unwrap();
// let buffer = unsafe { MaybeUninit::slice_assume_init_mut(&mut buffer[..]) };
// buffer.copy_from_slice(&data[start..end]);
//
// Ok(())
// }
//
// async fn write(&self, lba: u64, buffer: &PageSlice<u8>) -> Result<(), Error> {
// if self.deny_writes.load(Ordering::Acquire) {
// panic!("write() with deny_writes = true");
// }
//
// let start = lba as usize * self.block_size;
// let end = start + buffer.len();
//
// if end > self.block_count * self.block_size {
// return Err(Error::InvalidArgument);
// }
//
// let mut data = self.data.lock().unwrap();
// data[start..end].copy_from_slice(&buffer[..]);
//
// Ok(())
// }
//
// fn block_size(&self) -> usize {
// self.block_size
// }
//
// fn block_count(&self) -> usize {
// self.block_count
// }
// }
//
// impl PhysicalMemoryAllocator for PA {
// type Address = PhysicalAddress;
//
// unsafe fn free_page(page: Self::Address) {
// let base = page.try_into_usize().unwrap();
// let base = core::ptr::with_exposed_provenance_mut::<c_void>(base);
// if unsafe { libc::munmap(base, 0x1000) } != 0 {
// let err = io::Error::last_os_error();
// panic!("free_page: munmap returned {err}");
// }
// }
//
// fn allocate_page() -> Result<Self::Address, Error> {
// Self::allocate_contiguous_pages(1)
// }
//
// fn allocate_contiguous_pages(count: usize) -> Result<Self::Address, Error> {
// let base = unsafe {
// libc::mmap(
// null_mut(),
// count * 0x1000,
// libc::PROT_READ | libc::PROT_WRITE,
// libc::MAP_ANON | libc::MAP_PRIVATE,
// -1,
// 0,
// )
// };
// if base != libc::MAP_FAILED {
// let base = base.addr();
// Ok(PhysicalAddress::from_usize(base))
// } else {
// Err(Error::OutOfMemory)
// }
// }
// }
//
// const BS: usize = 1024;
//
// // The test must not crash with denied writes
// #[tokio::test]
// async fn test_no_modification() {
// let device = Box::leak(Box::new(DummyBlock::new(BS, 1024)));
// let wrapper = NgBlockDeviceWrapper::new(device);
// let cache = BlockCache::<PA>::with_capacity_in(wrapper, BS, BS, 64, 8);
//
// device.deny_writes.store(true, Ordering::Release);
// cache
// .try_with(1 * BS as u64, |block| {
// assert!(block.iter().all(|x| *x == 1));
// Ok(())
// })
// .await
// .unwrap();
// cache
// .try_with(2 * BS as u64, |block| {
// assert!(block.iter().all(|x| *x == 2));
// Ok(())
// })
// .await
// .unwrap();
//
// cache.flush().await;
// }
//
// #[tokio::test]
// async fn test_partial_modification() {
// let device = Box::leak(Box::new(DummyBlock::new(BS, 1024)));
// let wrapper = NgBlockDeviceWrapper::new(device);
// // 8 * 8
// let cache = BlockCache::<PA>::with_capacity_in(wrapper, BS, BS, 8, 8);
//
// const LBA: u64 = 1;
// cache
// .try_with_mut(LBA * BS as u64, 16, |block| {
// block[0..16].fill(0x12);
// Ok(())
// })
// .await
// .unwrap();
// cache.flush().await;
//
// {
// let mut buffer = PageBox::<_, PA>::new_uninit_slice_in(BS).unwrap();
// device.read(LBA, buffer.as_slice_mut()).await.unwrap();
// let buffer = unsafe { buffer.assume_init_slice() };
// buffer[0..16].iter().for_each(|&x| {
// assert_eq!(x, 0x12);
// });
// buffer[16..].iter().for_each(|&x| {
// assert_eq!(x, LBA as u8);
// });
// }
//
// cache
// .try_with_mut(LBA * BS as u64, 16, |block| {
// block[16..32].fill(0x23);
// Ok(())
// })
// .await
// .unwrap();
// cache
// .try_with_mut(LBA * BS as u64, 16, |block| {
// block[48..64].fill(0x34);
// Ok(())
// })
// .await
// .unwrap();
// cache
// .try_with_mut(LBA * BS as u64, 128, |block| {
// block[128..256].fill(0xF1);
// Ok(())
// })
// .await
// .unwrap();
// cache.flush().await;
//
// {
// let mut buffer = PageBox::<_, PA>::new_uninit_slice_in(BS).unwrap();
// device.read(LBA, buffer.as_slice_mut()).await.unwrap();
// let buffer = unsafe { buffer.assume_init_slice() };
// buffer[0..16].iter().for_each(|&x| {
// assert_eq!(x, 0x12);
// });
// buffer[16..32].iter().for_each(|&x| {
// assert_eq!(x, 0x23);
// });
// buffer[48..64].iter().for_each(|&x| {
// assert_eq!(x, 0x34);
// });
// buffer[128..256].iter().for_each(|&x| {
// assert_eq!(x, 0xF1);
// });
// buffer[32..48].iter().for_each(|&x| {
// assert_eq!(x, LBA as u8);
// });
// buffer[64..128].iter().for_each(|&x| {
// assert_eq!(x, LBA as u8);
// });
// buffer[256..].iter().for_each(|&x| {
// assert_eq!(x, LBA as u8);
// });
// }
// }
//
// #[tokio::test]
// async fn test_implicit_eviction() {
// let device = Box::leak(Box::new(DummyBlock::new(BS, 1024)));
// let wrapper = NgBlockDeviceWrapper::new(device);
// // 8 * 8
// let cache = BlockCache::<PA>::with_capacity_in(wrapper, BS, BS, 8, 8);
//
// fn mapper(x: u64) -> u8 {
// (x + 3) as u8
// }
//
// // Go through all blocks, fill those with some values
// for i in 0..1024 {
// cache
// .try_with_mut(i * BS as u64, BS, |block| {
// block.fill(mapper(i));
// Ok(())
// })
// .await
// .unwrap();
// }
// cache.flush().await;
//
// for i in 0..1024 {
// let mut buffer = PageBox::<_, PA>::new_uninit_slice_in(BS).unwrap();
// device.read(i, buffer.as_slice_mut()).await.unwrap();
// let buffer = unsafe { buffer.assume_init_slice() };
// assert!(buffer.iter().all(|x| *x == mapper(i)));
// }
//
// for i in 0..1023 {
// cache
// .try_with_mut(i * BS as u64, BS, |block| {
// block.fill(0x12);
// Ok(())
// })
// .await
// .unwrap();
// cache
// .try_with_mut((i + 1) * BS as u64, BS, |block| {
// block.fill(0x23);
// Ok(())
// })
// .await
// .unwrap();
// }
//
// for i in 0..1023 {
// cache
// .try_with_mut(i * BS as u64, BS, |block| {
// block.iter_mut().for_each(|x| *x += 1);
// Ok(())
// })
// .await
// .unwrap();
// cache
// .try_with_mut((i + 1) * BS as u64, BS, |block| {
// block.iter_mut().for_each(|x| *x += 2);
// Ok(())
// })
// .await
// .unwrap();
// }
//
// cache.flush().await;
//
// {
// let mut buffer = PageBox::<_, PA>::new_uninit_slice_in(BS).unwrap();
// device.read(0, buffer.as_slice_mut()).await.unwrap();
// let buffer = unsafe { buffer.assume_init_slice() };
// buffer.iter().for_each(|&x| {
// assert_eq!(x, 0x13, "block 0 mismatch");
// });
// }
// for i in 1..1023 {
// let mut buffer = PageBox::<_, PA>::new_uninit_slice_in(BS).unwrap();
// device.read(i, buffer.as_slice_mut()).await.unwrap();
// let buffer = unsafe { buffer.assume_init_slice() };
// buffer.iter().for_each(|&x| {
// assert_eq!(x, 0x15, "block {i} mismatch");
// });
// }
// {
// let mut buffer = PageBox::<_, PA>::new_uninit_slice_in(BS).unwrap();
// device.read(1023, buffer.as_slice_mut()).await.unwrap();
// let buffer = unsafe { buffer.assume_init_slice() };
// buffer.iter().for_each(|&x| {
// assert_eq!(x, 0x25, "block 1023 mismatch");
// });
// }
// }
// }

@ -0,0 +1,222 @@
use core::{any::Any, mem::MaybeUninit, ops::Deref};
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use device_api::device::Device;
use libk_mm::{PageBox, PageProvider, PageSlice};
use yggdrasil_abi::{error::Error, io::DeviceRequest};
use crate::vfs::{CommonImpl, NodeRef};
pub mod cache;
pub mod partition;
struct Chunked {
bs: usize,
bpr: usize,
position: u64,
offset: usize,
lba: u64,
remaining_lba: usize,
remaining: usize,
}
impl Chunked {
pub fn begin<D: BlockDevice + ?Sized>(
device: &D,
position: u64,
len: usize,
) -> Option<(Self, usize)> {
let bc = device.block_count();
let bs = device.block_size();
let bpr = device.max_blocks_per_request();
let start_lba = position / bs as u64;
let end_lba = (position + len as u64).div_ceil(bs as u64).min(bc);
if start_lba >= end_lba {
return None;
}
let remaining_lba = (end_lba - start_lba) as usize;
let remaining = len;
let max_lba_count = remaining_lba.min(bpr);
Some((
Self {
bs,
bpr,
position,
lba: start_lba,
remaining_lba,
remaining,
offset: 0,
},
max_lba_count,
))
}
}
impl Iterator for Chunked {
// 0: lba
// 1: lba_count
// 2: offset in block
// 3: offset in buffer
// 4: amount
type Item = (u64, usize, usize, usize, usize);
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
assert_eq!(self.remaining_lba, 0);
return None;
}
let block_count = self.remaining_lba.min(self.bpr);
let block_offset = (self.position % self.bs as u64) as usize;
let amount = (block_count * self.bs - block_offset).min(self.remaining);
let item = (self.lba, block_count, block_offset, self.offset, amount);
self.lba += block_count as u64;
self.remaining_lba -= block_count;
self.remaining -= amount;
self.offset += amount;
Some(item)
}
}
#[async_trait]
pub trait BlockDevice: Device + PageProvider {
async fn read_aligned(
&self,
position: u64,
buffer: &mut PageSlice<MaybeUninit<u8>>,
) -> Result<(), Error> {
let _ = (position, buffer);
Err(Error::NotImplemented)
}
async fn write_aligned(&self, position: u64, buffer: &PageSlice<u8>) -> Result<(), Error> {
let _ = (position, buffer);
Err(Error::NotImplemented)
}
async fn read(&self, position: u64, buffer: &mut [u8]) -> Result<usize, Error> {
let bs = self.block_size();
let Some((iter, max_lba_count)) = Chunked::begin(self, position, buffer.len()) else {
return Ok(0);
};
let mut read_buffer = PageBox::new_uninit_slice(max_lba_count * bs)?;
let mut total = 0;
for (lba, block_count, block_offset, offset, amount) in iter {
let read_buffer_slice = read_buffer.as_slice_mut().subslice_mut(..block_count * bs);
self.read_aligned(lba * bs as u64, read_buffer_slice)
.await?;
let src = unsafe {
MaybeUninit::slice_assume_init_ref(
&read_buffer[block_offset..block_offset + amount],
)
};
let dst = &mut buffer[offset..offset + amount];
dst.copy_from_slice(src);
total += amount;
}
Ok(total)
}
async fn write(&self, mut pos: u64, mut buf: &[u8]) -> Result<usize, Error> {
let bs = self.block_size();
let len = buf.len();
let mut remaining = buf.len();
while remaining != 0 {
let block_offset = (pos % bs as u64) as usize;
let lba = pos / bs as u64;
let amount = core::cmp::min(bs - block_offset, buf.len());
let mut block = PageBox::new_uninit_slice(bs)?;
if amount != bs {
// Need to read the block first -- it's modified partially
self.read_aligned(lba * bs as u64, block.as_slice_mut())
.await?;
}
let mut block = unsafe { block.assume_init_slice() };
block[block_offset..block_offset + amount].copy_from_slice(&buf[..amount]);
// Write the block back
self.write_aligned(lba * bs as u64, block.as_slice())
.await?;
buf = &buf[amount..];
remaining -= amount;
pos += amount as u64;
}
Ok(len)
}
async fn read_exact(&self, position: u64, buffer: &mut [u8]) -> Result<(), Error> {
match self.read(position, buffer).await {
Ok(len) if len == buffer.len() => Ok(()),
Ok(_) => Err(Error::InvalidArgument),
Err(error) => Err(error),
}
}
async fn write_exact(&self, position: u64, buffer: &[u8]) -> Result<(), Error> {
match self.write(position, buffer).await {
Ok(len) if len == buffer.len() => Ok(()),
Ok(_) => Err(Error::InvalidArgument),
Err(error) => Err(error),
}
}
fn is_readable(&self) -> bool {
true
}
fn is_writeable(&self) -> bool {
true
}
fn block_size(&self) -> usize;
fn block_count(&self) -> u64;
fn max_blocks_per_request(&self) -> usize;
fn device_request(&self, request: &mut DeviceRequest) -> Result<(), Error> {
let _ = request;
Err(Error::InvalidOperation)
}
}
#[derive(Clone)]
pub struct BlockDeviceFile(pub(crate) Arc<dyn BlockDevice>);
impl CommonImpl for BlockDeviceFile {
fn size(&self, node: &NodeRef) -> Result<u64, Error> {
let _ = node;
Ok(self.0.block_count() * self.0.block_size() as u64)
}
fn as_any(&self) -> &dyn Any {
self as _
}
}
impl Deref for BlockDeviceFile {
type Target = dyn BlockDevice;
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}

@ -0,0 +1,94 @@
use alloc::{sync::Arc, vec::Vec};
use bytemuck::{Pod, Zeroable};
use libk_mm::PageBox;
use static_assertions::const_assert_eq;
use uuid::Uuid;
use yggdrasil_abi::error::Error;
use crate::device::block::BlockDevice;
use super::Partition;
#[derive(Clone, Copy)]
#[repr(C)]
struct GptHeader {
signature: [u8; 8],
revision: u32,
header_size: u32,
crc32: u32,
_0: u32,
header_lba: u64,
alternate_header_lba: u64,
first_usable_lba: u64,
last_usable_lba: u64,
guid: [u8; 16],
partition_table_lba: u64,
partition_table_len: u32,
partition_table_entry_size: u32,
partition_table_crc32: u32,
_1: [u8; 420],
}
#[derive(Clone, Copy, Zeroable, Pod)]
#[repr(C)]
struct GptEntry {
type_guid: Uuid,
part_guid: Uuid,
lba_start: u64,
lba_end: u64,
attrs: u64,
}
const_assert_eq!(size_of::<GptHeader>(), 512);
async unsafe fn read_struct_lba<T>(dev: &dyn BlockDevice, lba: u64) -> Result<PageBox<T>, Error> {
assert_eq!(size_of::<T>(), 512);
let mut data = PageBox::new_uninit()?;
dev.read_aligned(lba * 512, PageBox::as_bytes_mut(&mut data))
.await?;
Ok(data.assume_init())
}
pub(crate) async fn probe_gpt(dev: &Arc<dyn BlockDevice>) -> Result<Option<Vec<Partition>>, Error> {
let header = unsafe { read_struct_lba::<GptHeader>(dev.as_ref(), 1) }.await?;
if &header.signature != b"EFI PART" {
// Not a GPT partition table
return Ok(None);
}
let pt_entsize = header.partition_table_entry_size as usize;
let pt_len = header.partition_table_len as usize;
let mut pt_data = PageBox::new_slice(0, pt_len * pt_entsize)?;
log::info!("pt_entsize = {}, pt_len = {}", pt_entsize, pt_len);
assert!(size_of::<GptEntry>() <= pt_entsize);
dev.read_exact(header.partition_table_lba * 512, &mut pt_data)
.await?;
let mut partitions = Vec::new();
for i in 0..pt_len {
let pt_entry_data = &pt_data[i * pt_entsize..i * pt_entsize + size_of::<GptEntry>()];
let pt_entry: &GptEntry = bytemuck::from_bytes(pt_entry_data);
if pt_entry.type_guid.is_nil() {
break;
}
log::debug!(
"Found partition: guid={}, start={}, end={}",
pt_entry.type_guid,
pt_entry.lba_start,
pt_entry.lba_end
);
partitions.push(Partition {
device: dev.clone(),
lba_start: pt_entry.lba_start,
lba_end: pt_entry.lba_end,
});
}
Ok(Some(partitions))
}

@ -0,0 +1,118 @@
use core::mem::MaybeUninit;
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use device_api::device::Device;
use libk_mm::{address::PhysicalAddress, table::MapAttributes, PageProvider, PageSlice};
use yggdrasil_abi::{error::Error, io::DeviceRequest};
use super::BlockDevice;
pub mod gpt;
pub(crate) use gpt::probe_gpt;
pub struct Partition {
device: Arc<dyn BlockDevice>,
lba_start: u64,
lba_end: u64,
}
impl Partition {
pub fn new(device: Arc<dyn BlockDevice>, lba_start: u64, lba_end: u64) -> Self {
assert!(lba_start < lba_end);
Self {
device,
lba_start,
lba_end,
}
}
}
impl PageProvider for Partition {
fn get_page(&self, _offset: u64) -> Result<PhysicalAddress, Error> {
todo!()
}
fn clone_page(
&self,
_offset: u64,
_src_phys: PhysicalAddress,
_src_attrs: MapAttributes,
) -> Result<PhysicalAddress, Error> {
todo!()
}
fn release_page(&self, _offset: u64, _phys: PhysicalAddress) -> Result<(), Error> {
todo!()
}
}
impl Device for Partition {
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
todo!()
}
unsafe fn deinit(&self) -> Result<(), Error> {
todo!()
}
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
todo!()
}
fn display_name(&self) -> &str {
"Partition"
}
}
#[async_trait]
impl BlockDevice for Partition {
async fn read_aligned(
&self,
position: u64,
buffer: &mut PageSlice<MaybeUninit<u8>>,
) -> Result<(), Error> {
// TODO check against partition range
debug_assert_eq!(position % self.device.block_size() as u64, 0);
debug_assert_eq!(buffer.len() % self.device.block_size(), 0);
self.device
.read_aligned(self.lba_start * self.block_size() as u64 + position, buffer)
.await
}
async fn write_aligned(&self, position: u64, buffer: &PageSlice<u8>) -> Result<(), Error> {
// TODO check against partition range
debug_assert_eq!(position % self.device.block_size() as u64, 0);
debug_assert_eq!(buffer.len() % self.device.block_size(), 0);
self.device
.write_aligned(self.lba_start * self.block_size() as u64 + position, buffer)
.await
}
fn block_size(&self) -> usize {
self.device.block_size()
}
fn block_count(&self) -> u64 {
self.lba_end - self.lba_start
}
fn is_readable(&self) -> bool {
self.device.is_readable()
}
fn is_writeable(&self) -> bool {
self.device.is_writeable()
}
fn device_request(&self, request: &mut DeviceRequest) -> Result<(), Error> {
self.device.device_request(request)
}
fn max_blocks_per_request(&self) -> usize {
self.device.max_blocks_per_request()
}
}

@ -0,0 +1,64 @@
use core::{any::Any, ops::Deref};
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use device_api::device::Device;
use yggdrasil_abi::{error::Error, io::DeviceRequest};
use crate::vfs::{CommonImpl, FileReadiness, NodeRef};
#[async_trait]
pub trait CharDevice: Device + FileReadiness {
async fn read(&self, buffer: &mut [u8]) -> Result<usize, Error> {
self.read_nonblocking(buffer)
}
async fn write(&self, buffer: &[u8]) -> Result<usize, Error> {
self.write_nonblocking(buffer)
}
fn read_nonblocking(&self, buffer: &mut [u8]) -> Result<usize, Error> {
let _ = buffer;
Err(Error::NotImplemented)
}
fn write_nonblocking(&self, buffer: &[u8]) -> Result<usize, Error> {
let _ = buffer;
Err(Error::NotImplemented)
}
fn is_readable(&self) -> bool {
true
}
fn is_writeable(&self) -> bool {
true
}
fn is_terminal(&self) -> bool {
false
}
fn device_request(&self, request: &mut DeviceRequest) -> Result<(), Error> {
let _ = request;
Err(Error::InvalidOperation)
}
}
#[derive(Clone)]
pub struct CharDeviceFile(pub(crate) Arc<dyn CharDevice>);
impl CommonImpl for CharDeviceFile {
fn size(&self, node: &NodeRef) -> Result<u64, Error> {
let _ = node;
Ok(0)
}
fn as_any(&self) -> &dyn Any {
self as _
}
}
impl Deref for CharDeviceFile {
type Target = dyn CharDevice;
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}

@ -1,21 +1,23 @@
//! Display device structures and interfaces
use core::ops::{Deref, DerefMut};
use abi::error::Error;
use libk::device::display::{Color, DisplayDevice, DisplayOwner, PixelFormat};
use alloc::sync::Arc;
use yggdrasil_abi::error::Error;
use crate::device::display::{Color, DisplayDevice, DisplayOwner, PixelFormat};
/// Convenience wrapper for accessing display framebuffers from the kernel side
pub struct KernelFramebufferAccess<'a> {
display: &'a dyn DisplayDevice,
pub struct KernelFramebufferAccess {
display: Arc<dyn DisplayDevice>,
base: usize,
stride: usize,
format: PixelFormat,
size: usize,
}
impl<'a> KernelFramebufferAccess<'a> {
impl KernelFramebufferAccess {
/// Attempts to acquire kernel access to a framebuffer of a display device
pub fn acquire(display: &'a dyn DisplayDevice) -> Result<Self, Error> {
pub fn acquire(display: Arc<dyn DisplayDevice>) -> Result<Self, Error> {
// Lock the display by the kernel
display.lock(DisplayOwner::Kernel)?;
@ -69,7 +71,7 @@ impl<'a> KernelFramebufferAccess<'a> {
}
}
impl Deref for KernelFramebufferAccess<'_> {
impl Deref for KernelFramebufferAccess {
type Target = [u32];
#[inline]
@ -80,7 +82,7 @@ impl Deref for KernelFramebufferAccess<'_> {
}
}
impl DerefMut for KernelFramebufferAccess<'_> {
impl DerefMut for KernelFramebufferAccess {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe {
core::slice::from_raw_parts_mut(
@ -91,7 +93,7 @@ impl DerefMut for KernelFramebufferAccess<'_> {
}
}
impl Drop for KernelFramebufferAccess<'_> {
impl Drop for KernelFramebufferAccess {
fn drop(&mut self) {
self.display.unlock(DisplayOwner::Kernel).ok();
}

@ -0,0 +1,72 @@
use super::PixelFormat;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
impl Color {
pub const BLACK: Self = Self::from_rgb_u32(0x000000);
pub const WHITE: Self = Self::from_rgb_u32(0xFFFFFF);
pub const RED: Self = Self::from_rgb_u32(0xFF0000);
#[inline]
pub const fn from_rgba_u32(v: u32) -> Self {
// 0x11223344 -> [0x44, 0x33, 0x22, 0x11]
// a b g r
let x = v.to_le_bytes();
Self {
a: x[0],
b: x[1],
g: x[2],
r: x[3],
}
}
#[inline]
pub const fn from_rgb_u32(v: u32) -> Self {
let x = v.to_le_bytes();
Self {
a: u8::MAX,
b: x[0],
g: x[1],
r: x[2],
}
}
#[inline]
pub const fn to_rgba_u32(self) -> u32 {
u32::from_ne_bytes([self.r, self.g, self.b, self.a])
}
#[inline]
pub const fn to_bgra_u32(self) -> u32 {
u32::from_ne_bytes([self.b, self.g, self.r, self.a])
}
#[inline]
pub const fn to_pixel_u32(self, format: PixelFormat) -> u32 {
match format {
PixelFormat::B8G8R8A8 => self.to_bgra_u32(),
PixelFormat::R8G8B8A8 => self.to_rgba_u32(),
_ => unimplemented!(),
}
}
pub const fn rgb_mul(self, v: u8) -> Self {
Self {
a: self.a,
r: self.r.saturating_mul(v),
g: self.g.saturating_mul(v),
b: self.b.saturating_mul(v),
}
}
// pub fn rgb_mul_assign(&mut self, v: u8) {
// self.r = self.r.saturating_mul(v);
// self.g = self.g.saturating_mul(v);
// self.b = self.b.saturating_mul(v);
// }
}

@ -0,0 +1,555 @@
//! Console device interfaces
use core::time::Duration;
use alloc::{sync::Arc, vec, vec::Vec};
use bitflags::bitflags;
use libk_util::{
sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock},
StaticVector,
};
use yggdrasil_abi::{error::Error, io::TerminalSize, primitive_enum};
use crate::{task::runtime, vfs::TerminalOutput};
use super::Color;
const CONSOLE_ROW_LEN: usize = 80;
const MAX_CSI_ARGS: usize = 8;
const DEFAULT_FG_COLOR: ColorAttribute = ColorAttribute::White;
/// Default console background color
pub const DEFAULT_BG_COLOR: ColorAttribute = ColorAttribute::Blue;
primitive_enum! {
#[allow(missing_docs)]
#[doc = "Color attribute of a console character"]
pub enum ColorAttribute: u8 {
Black = 0,
Red = 1,
Green = 2,
Yellow = 3,
Blue = 4,
Magenta = 5,
Cyan = 6,
White = 7,
}
}
bitflags! {
#[derive(Clone, Copy)]
pub struct Attributes: u8 {
const BOLD = 1 << 0;
}
}
impl ColorAttribute {
fn from_vt100(val: u8) -> Self {
match val {
0..=7 => Self::try_from(val).unwrap(),
_ => ColorAttribute::Red,
}
}
/// Converts the attribute to RGBA representation
pub fn as_rgba(&self, bold: bool) -> Color {
const TABLE: &[Color] = &[
Color::from_rgb_u32(0x000000),
Color::from_rgb_u32(0x7F0000),
Color::from_rgb_u32(0x007F00),
Color::from_rgb_u32(0x7F7F00),
Color::from_rgb_u32(0x00007F),
Color::from_rgb_u32(0x7F007F),
Color::from_rgb_u32(0x007F7F),
Color::from_rgb_u32(0x7F7F7F),
];
let color = TABLE[*self as usize];
if bold {
color.rgb_mul(2)
} else {
color
}
}
}
/// Represents a single character with its attributes
#[derive(Clone, Copy)]
#[repr(C)]
pub struct ConsoleChar {
attributes: u16,
char: u8,
_pad: u8,
}
/// Represents a single line in the console buffer
#[derive(Clone, Copy)]
pub struct ConsoleRow {
dirty: u8,
chars: [ConsoleChar; CONSOLE_ROW_LEN],
}
/// Buffer that contains text rows of the console with their attributes + tracks dirty rows which
/// need to be flushed to the display
pub struct ConsoleBuffer {
rows: Vec<ConsoleRow>,
height: u32,
}
/// Console wrapper for adding it into devfs as a char device
pub struct ConsoleWrapper(pub Arc<dyn DisplayConsole>);
enum EscapeState {
Normal,
Escape,
Csi,
}
/// Common state for console output devices
pub struct ConsoleState {
/// Current cursor row
pub cursor_row: u32,
/// Current cursor column
pub cursor_col: u32,
/// Current foreground color
pub fg_color: ColorAttribute,
/// Current background color
pub bg_color: ColorAttribute,
/// Current set of attributes
pub attributes: Attributes,
esc_args: StaticVector<u32, MAX_CSI_ARGS>,
esc_state: EscapeState,
/// Row buffer
pub buffer: ConsoleBuffer,
}
/// Helper type to iterate over dirty rows in the buffer
pub struct RowIter<'a> {
buffer: &'a mut ConsoleBuffer,
index: u32,
}
/// Interface to implement buffered console semantics on an abstract console output device
pub trait DisplayConsole: Sync + Send {
/// Returns the state lock
fn state(&self) -> &IrqSafeSpinlock<ConsoleState>;
/// Flushes the data from console buffer to the display
fn flush(&self, state: &mut ConsoleState);
/// Writes characters to the backing buffer + handles special control sequences
fn write_char(&self, c: u8) {
let mut state = self.state().lock();
if state.putc(c) {
self.flush(&mut state);
}
}
/// Returns the dimensions of the console in chars: (rows, columns)
fn text_dimensions(&self) -> (usize, usize) {
let state = self.state().lock();
(state.buffer.height as _, CONSOLE_ROW_LEN as _)
}
}
impl ConsoleChar {
/// Empty character
pub const BLANK: Self = Self {
attributes: 0,
char: 0,
_pad: 0,
};
/// Constructs a console character from a char and its attributes
#[inline(always)]
pub fn from_parts(
char: u8,
fg: ColorAttribute,
bg: ColorAttribute,
attributes: Attributes,
) -> Self {
let attributes = ((attributes.bits() as u16) << 8)
| ((u8::from(bg) as u16) << 4)
| (u8::from(fg) as u16);
Self {
attributes,
char,
_pad: 0,
}
}
/// Returns the attributes of the character
#[inline(always)]
pub fn attributes(self) -> (ColorAttribute, ColorAttribute, Attributes) {
let fg =
ColorAttribute::try_from((self.attributes & 0xF) as u8).unwrap_or(DEFAULT_FG_COLOR);
let bg = ColorAttribute::try_from(((self.attributes >> 4) & 0xF) as u8)
.unwrap_or(DEFAULT_BG_COLOR);
let attributes =
Attributes::from_bits((self.attributes >> 8) as u8).unwrap_or(Attributes::empty());
(fg, bg, attributes)
}
/// Returns the character data of this [ConsoleChar]
#[inline(always)]
pub const fn character(self) -> u8 {
self.char
}
}
impl RowIter<'_> {
/// Returns the next dirty row
pub fn next_dirty(&mut self) -> Option<(u32, &[ConsoleChar])> {
loop {
if self.index == self.buffer.height {
return None;
}
if !self.buffer.rows[self.index as usize].clear_dirty() {
self.index += 1;
continue;
}
let row_index = self.index;
let row = &self.buffer.rows[self.index as usize];
self.index += 1;
return Some((row_index, &row.chars));
}
}
}
impl ConsoleRow {
/// Constructs a row filled with blank characters
pub const fn zeroed() -> Self {
Self {
dirty: 1,
chars: [ConsoleChar {
attributes: ((DEFAULT_BG_COLOR as u8) as u16) << 4,
char: b' ',
_pad: 0,
}; CONSOLE_ROW_LEN],
}
}
/// Returns `true` if the row's dirty flag is set
#[inline]
pub const fn is_dirty(&self) -> bool {
self.dirty != 0
}
/// Clears "dirty" flag for the row
#[inline]
pub fn clear_dirty(&mut self) -> bool {
let old = self.dirty;
self.dirty = 0;
old == 1
}
/// Clears the console row with blank characters
pub fn clear(&mut self, bg: ColorAttribute) {
self.dirty = 1;
self.chars
.fill(ConsoleChar::from_parts(b' ', bg, bg, Attributes::empty()));
}
}
impl ConsoleBuffer {
/// Constructs a fixed-size console buffer
pub fn new(height: u32) -> Result<Self, Error> {
// let size = size_of::<ConsoleRow>() * (height as usize);
let mut rows = vec![ConsoleRow::zeroed(); height as usize];
for row in rows.iter_mut() {
row.clear(DEFAULT_BG_COLOR);
}
Ok(Self { rows, height })
}
#[inline(never)]
fn set_char(&mut self, row: u32, col: u32, c: ConsoleChar) {
self.rows[row as usize].dirty = 1;
self.rows[row as usize].chars[col as usize] = c;
}
#[inline(never)]
fn set_dirty(&mut self, row: u32) {
self.rows[row as usize].dirty = 1;
}
/// Returns an iterator over dirty rows, while clearing dirty flag for them
pub fn flush_rows(&mut self) -> RowIter {
RowIter {
buffer: self,
index: 0,
}
}
fn clear(&mut self, bg: ColorAttribute) {
for row in self.rows.iter_mut() {
row.clear(bg);
}
}
fn clear_row(&mut self, row: u32, bg: ColorAttribute) {
self.rows[row as usize].dirty = 1;
self.rows[row as usize].clear(bg);
}
fn erase_in_row(&mut self, row: u32, start: usize, bg: ColorAttribute) {
self.rows[row as usize].dirty = 1;
self.rows[row as usize].chars[start..].fill(ConsoleChar::from_parts(
b' ',
DEFAULT_FG_COLOR,
bg,
Attributes::empty(),
));
}
fn scroll_once(&mut self, bg: ColorAttribute) {
self.rows.copy_within(1.., 0);
self.rows[(self.height - 1) as usize].clear(bg);
// Mark everything dirty
self.rows.iter_mut().for_each(|row| {
row.dirty = 1;
});
}
}
impl ConsoleState {
/// Constructs a new console state with given buffer
pub fn new(buffer: ConsoleBuffer) -> Self {
Self {
cursor_row: 0,
cursor_col: 0,
fg_color: DEFAULT_FG_COLOR,
bg_color: DEFAULT_BG_COLOR,
attributes: Attributes::empty(),
esc_args: StaticVector::new(),
esc_state: EscapeState::Normal,
buffer,
}
}
fn putc_normal(&mut self, c: u8) -> bool {
let mut flush = false;
match c {
c if c >= 127 => {
self.buffer.set_char(
self.cursor_row,
self.cursor_col,
ConsoleChar::from_parts(
b'?',
self.fg_color,
ColorAttribute::Red,
self.attributes,
),
);
self.cursor_col += 1;
}
b'\x1b' => {
self.esc_state = EscapeState::Escape;
return false;
}
b'\r' => {
self.cursor_col = 0;
}
b'\n' => {
self.cursor_row += 1;
self.cursor_col = 0;
flush = true;
}
_ => {
self.buffer.set_char(
self.cursor_row,
self.cursor_col,
ConsoleChar::from_parts(c, self.fg_color, self.bg_color, self.attributes),
);
self.cursor_col += 1;
}
}
if self.cursor_col == CONSOLE_ROW_LEN as u32 {
self.cursor_col = 0;
self.cursor_row += 1;
}
if self.cursor_row == self.buffer.height {
self.buffer.scroll_once(self.bg_color);
self.cursor_row = self.buffer.height - 1;
flush = true;
}
flush
}
fn handle_csi(&mut self, c: u8) -> bool {
match c {
// Move back one character
b'D' => {
if self.cursor_col > 0 {
self.cursor_col -= 1;
}
}
// Manipulate display attributes
b'm' => {
if let Some(arg) = self.esc_args.first() {
match arg {
// Reset
0 => {
self.fg_color = DEFAULT_FG_COLOR;
self.bg_color = DEFAULT_BG_COLOR;
self.attributes = Attributes::empty();
}
// Bold
1 => {
self.attributes |= Attributes::BOLD;
}
// Foreground colors
30..=39 => {
let vt_color = self.esc_args[0] % 10;
if vt_color == 9 {
self.fg_color = DEFAULT_FG_COLOR;
} else {
self.fg_color = ColorAttribute::from_vt100(vt_color as u8);
}
}
// Background colors
40..=49 => {
let vt_color = self.esc_args[0] % 10;
if vt_color == 9 {
self.bg_color = DEFAULT_BG_COLOR;
} else {
self.bg_color = ColorAttribute::from_vt100(vt_color as u8);
}
}
_ => (),
}
}
}
// Move cursor to position
b'f' => {
let row = self.esc_args[0].clamp(1, self.buffer.height) - 1;
let col = self.esc_args[1].clamp(1, CONSOLE_ROW_LEN as u32) - 1;
self.buffer.set_dirty(row);
self.cursor_row = row;
self.cursor_col = col;
}
// Clear rows/columns/screen
b'J' => match self.esc_args[0] {
// Erase lines down
0 => (),
// Erase lines up
1 => (),
// Erase all
2 => {
self.buffer.clear(self.bg_color);
}
_ => (),
},
// Erase in Line
b'K' => match self.esc_args[0] {
// Erase to Right
0 => {
self.buffer
.erase_in_row(self.cursor_row, self.cursor_col as _, self.bg_color);
}
// Erase All
2 => {
self.buffer.clear_row(self.cursor_row, self.bg_color);
}
_ => (),
},
_ => (),
}
self.esc_state = EscapeState::Normal;
false
}
fn handle_csi_byte(&mut self, c: u8) -> bool {
match c {
b'0'..=b'9' => {
let arg = self.esc_args.last_mut().unwrap();
*arg *= 10;
*arg += (c - b'0') as u32;
false
}
b';' => {
self.esc_args.push(0);
false
}
_ => self.handle_csi(c),
}
}
fn putc(&mut self, c: u8) -> bool {
match self.esc_state {
EscapeState::Normal => self.putc_normal(c),
EscapeState::Escape => match c {
b'[' => {
self.esc_state = EscapeState::Csi;
self.esc_args.clear();
self.esc_args.push(0);
false
}
_ => {
self.esc_state = EscapeState::Normal;
self.esc_args.clear();
false
}
},
EscapeState::Csi => self.handle_csi_byte(c),
}
}
}
impl TerminalOutput for ConsoleWrapper {
fn size(&self) -> TerminalSize {
let (rows, columns) = self.0.text_dimensions();
TerminalSize { rows, columns }
}
fn write(&self, byte: u8) -> Result<(), Error> {
self.0.write_char(byte);
Ok(())
}
}
static CONSOLES: IrqSafeRwLock<Vec<Arc<dyn DisplayConsole>>> = IrqSafeRwLock::new(Vec::new());
/// Adds a console device to a auto-flush list
pub fn add_console_autoflush(console: Arc<dyn DisplayConsole>) {
CONSOLES.write().push(console);
}
/// Flushes console buffers to their displays
pub fn flush_consoles() {
for console in CONSOLES.read().iter() {
let mut state = console.state().lock();
console.flush(&mut state);
}
}
/// Periodically flushes data from console buffers onto their displays
pub async fn flush_consoles_task() {
loop {
flush_consoles();
runtime::sleep(Duration::from_millis(20)).await;
}
}

@ -1,19 +1,20 @@
//! Framebuffer console driver
use abi::error::Error;
use libk::device::display::{Color, DisplayDevice};
use alloc::sync::Arc;
use libk_util::sync::IrqSafeSpinlock;
use yggdrasil_abi::error::Error;
use crate::debug::DebugSink;
use super::{
access::KernelFramebufferAccess,
console::{self, Attributes, ConsoleBuffer, ConsoleState, DisplayConsole},
device::KernelFramebufferAccess,
font::PcScreenFont,
Color, DisplayDevice,
};
struct Inner {
display: &'static dyn DisplayDevice,
display: Arc<dyn DisplayDevice>,
font: PcScreenFont<'static>,
char_width: u32,
char_height: u32,
@ -57,7 +58,7 @@ impl DisplayConsole for FramebufferConsole {
fn flush(&self, state: &mut ConsoleState) {
let mut inner = self.inner.lock();
let Ok(mut framebuffer) = KernelFramebufferAccess::acquire(inner.display) else {
let Ok(mut framebuffer) = KernelFramebufferAccess::acquire(inner.display.clone()) else {
return;
};
let font = inner.font;
@ -128,8 +129,7 @@ impl DisplayConsole for FramebufferConsole {
impl FramebufferConsole {
/// Constructs an instance of console from its framebuffer reference.
pub fn from_framebuffer(
// framebuffer: &'static LinearFramebuffer,
display: &'static dyn DisplayDevice,
display: Arc<dyn DisplayDevice>,
font: Option<PcScreenFont<'static>>,
boot: bool,
) -> Result<Self, Error> {
@ -162,7 +162,7 @@ impl FramebufferConsole {
}
/// Changes the display used for this console
pub fn set_display(&self, display: &'static dyn DisplayDevice) -> Result<(), Error> {
pub fn set_display(&self, display: Arc<dyn DisplayDevice>) -> Result<(), Error> {
let mode = display
.active_display_mode()
.ok_or(Error::InvalidOperation)?;
@ -177,7 +177,7 @@ impl FramebufferConsole {
let buffer = ConsoleBuffer::new(mode.height / char_height)?;
let inner = Inner {
display,
display: display.clone(),
font: old_inner.font,
width: mode.width / char_width,
height: mode.height / char_height,

@ -2,9 +2,10 @@
use core::mem::size_of;
use abi::error::Error;
use bytemuck::{Pod, Zeroable};
use libk::AlignedTo;
use yggdrasil_abi::error::Error;
use crate::AlignedTo;
static CONSOLE_FONT: &AlignedTo<u32, [u8]> = &AlignedTo {
align: [],

@ -1,14 +1,9 @@
use core::{
mem::MaybeUninit,
ops::Deref,
sync::atomic::{AtomicU32, Ordering},
};
use core::mem::MaybeUninit;
use alloc::{boxed::Box, collections::BTreeMap};
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use device_api::Device;
use libk_mm::{address::PhysicalAddress, table::MapAttributes, PageProvider};
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
use device_api::device::Device;
use libk_mm::{address::PhysicalAddress, table::MapAttributes, PageProvider, L3_PAGE_SIZE};
use yggdrasil_abi::{
bitflags,
error::Error,
@ -16,7 +11,17 @@ use yggdrasil_abi::{
process::ProcessId,
};
use crate::{task::thread::Thread, vfs::block::BlockDevice};
pub mod access;
pub mod color;
pub mod console;
pub mod fb_console;
pub mod font;
pub use color::Color;
use crate::task::thread::Thread;
use super::block::BlockDevice;
bitflags! {
/// Init-time display driver flags
@ -26,14 +31,6 @@ bitflags! {
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
/// Represents the current lock holder for a display device
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DisplayOwner {
@ -139,49 +136,20 @@ pub trait DisplayDevice: Device + PageProvider {
fn unlock(&self, owner: DisplayOwner) -> Result<(), Error>;
}
/// Convenience wrapper for [DisplayDevice] trait implementors to auto-implement [BlockDevice] as
/// well.
pub struct DisplayDeviceWrapper {
inner: &'static dyn DisplayDevice,
#[derive(Clone)]
pub struct DisplayWrapper {
pub device: Arc<dyn DisplayDevice>,
pub boot: bool,
}
static DEVICES: IrqSafeRwLock<BTreeMap<u32, &'static DisplayDeviceWrapper>> =
IrqSafeRwLock::new(BTreeMap::new());
static DEVICE_CALLBACK: OneTimeInit<fn(&'static DisplayDeviceWrapper)> = OneTimeInit::new();
static LAST_DEVICE_ID: AtomicU32 = AtomicU32::new(1);
pub fn set_display_registration_callback(cb: fn(&'static DisplayDeviceWrapper)) {
DEVICE_CALLBACK.init(cb);
}
/// Adds the display to the global display list
pub fn register_display_device(device: &'static DisplayDeviceWrapper) -> u32 {
let id = LAST_DEVICE_ID.fetch_add(1, Ordering::Relaxed);
{
let mut devices = DEVICES.write();
devices.insert(id, device);
}
if let Some(callback) = DEVICE_CALLBACK.try_get() {
callback(device);
}
id
}
/// Removes the device by its ID
pub fn remove_device(id: u32) -> Option<&'static DisplayDeviceWrapper> {
let mut devices = DEVICES.write();
devices.remove(&id)
}
impl DisplayDeviceWrapper {
/// Wraps the [DisplayDevice] in [DisplayDeviceWrapper].
pub const fn new(inner: &'static dyn DisplayDevice) -> Self {
Self { inner }
impl DisplayWrapper {
pub fn new(device: Arc<dyn DisplayDevice>, boot: bool) -> Result<Self, Error> {
Ok(Self { device, boot })
}
}
#[async_trait]
impl BlockDevice for DisplayDeviceWrapper {
impl BlockDevice for DisplayWrapper {
async fn read(&self, _pos: u64, _buf: &mut [u8]) -> Result<usize, Error> {
Err(Error::InvalidOperation)
}
@ -213,109 +181,41 @@ impl BlockDevice for DisplayDeviceWrapper {
_ => Err(Error::InvalidOperation),
}
}
fn block_size(&self) -> usize {
L3_PAGE_SIZE
}
fn block_count(&self) -> u64 {
0
}
fn max_blocks_per_request(&self) -> usize {
0
}
}
impl PageProvider for DisplayDeviceWrapper {
impl PageProvider for DisplayWrapper {
fn clone_page(
&self,
offset: u64,
src_phys: PhysicalAddress,
src_attrs: MapAttributes,
) -> Result<PhysicalAddress, Error> {
self.inner.clone_page(offset, src_phys, src_attrs)
self.device.clone_page(offset, src_phys, src_attrs)
}
fn get_page(&self, offset: u64) -> Result<PhysicalAddress, Error> {
self.inner.get_page(offset)
self.device.get_page(offset)
}
fn release_page(&self, offset: u64, phys: PhysicalAddress) -> Result<(), Error> {
self.inner.release_page(offset, phys)
self.device.release_page(offset, phys)
}
}
impl Deref for DisplayDeviceWrapper {
type Target = dyn DisplayDevice;
fn deref(&self) -> &Self::Target {
self.inner
impl Device for DisplayWrapper {
fn display_name(&self) -> &str {
self.device.display_name()
}
}
const _0: () = const {
let c1 = Color::from_rgba_u32(0x11223344);
let c2 = Color::from_rgb_u32(0x112233);
if c1.r != 0x11 || c1.g != 0x22 || c1.b != 0x33 || c1.a != 0x44 {
panic!();
}
if c2.r != 0x11 || c2.g != 0x22 || c2.b != 0x33 {
panic!();
}
// if color.to_le_rgba_u32() != 0x11223344 {
// panic!();
// }
// assert_eq!(color.to_rgba_u32(), 0x11223344);
};
impl Color {
pub const BLACK: Self = Self::from_rgb_u32(0x000000);
pub const WHITE: Self = Self::from_rgb_u32(0xFFFFFF);
pub const RED: Self = Self::from_rgb_u32(0xFF0000);
#[inline]
pub const fn from_rgba_u32(v: u32) -> Self {
// 0x11223344 -> [0x44, 0x33, 0x22, 0x11]
// a b g r
let x = v.to_le_bytes();
Self {
a: x[0],
b: x[1],
g: x[2],
r: x[3],
}
}
#[inline]
pub const fn from_rgb_u32(v: u32) -> Self {
let x = v.to_le_bytes();
Self {
a: u8::MAX,
b: x[0],
g: x[1],
r: x[2],
}
}
#[inline]
pub const fn to_rgba_u32(self) -> u32 {
u32::from_ne_bytes([self.r, self.g, self.b, self.a])
}
#[inline]
pub const fn to_bgra_u32(self) -> u32 {
u32::from_ne_bytes([self.b, self.g, self.r, self.a])
}
#[inline]
pub const fn to_pixel_u32(self, format: PixelFormat) -> u32 {
match format {
PixelFormat::B8G8R8A8 => self.to_bgra_u32(),
PixelFormat::R8G8B8A8 => self.to_rgba_u32(),
_ => unimplemented!(),
}
}
pub const fn rgb_mul(self, v: u8) -> Self {
Self {
a: self.a,
r: self.r.saturating_mul(v),
g: self.g.saturating_mul(v),
b: self.b.saturating_mul(v),
}
}
// pub fn rgb_mul_assign(&mut self, v: u8) {
// self.r = self.r.saturating_mul(v);
// self.g = self.g.saturating_mul(v);
// self.b = self.b.saturating_mul(v);
// }
}

@ -0,0 +1,225 @@
use core::{
ops::Deref,
sync::atomic::{AtomicU32, Ordering},
};
use alloc::{collections::BTreeMap, format, sync::Arc, vec::Vec};
use device_api::device::Device;
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
use yggdrasil_abi::error::Error;
use crate::{
debug::{self, DebugSink, LogLevel},
devfs,
vfs::NodeRef,
};
use super::{
block::{
partition::{self, Partition},
BlockDevice,
},
char::CharDevice,
display::{DisplayDevice, DisplayWrapper},
};
struct GenericRegistry<T> {
members: IrqSafeRwLock<BTreeMap<u32, T>>,
last_index: AtomicU32,
}
pub type DisplayRegisterCallback = fn(&Arc<dyn DisplayDevice>, Option<NodeRef>);
// Manages devices: fb<N>
pub struct DisplayDeviceRegistry {
registry: GenericRegistry<Arc<DisplayWrapper>>,
callback: OneTimeInit<DisplayRegisterCallback>,
}
// Manages devices: tty<N>
pub struct TerminalRegistry {
registry: GenericRegistry<Arc<dyn CharDevice>>,
}
// Manages devices: ttyS<N>
pub struct SerialTerminalRegistry {
registry: GenericRegistry<Arc<dyn CharDevice>>,
}
struct PendingDevice {
device: Arc<dyn Device>,
irq_only: bool,
failed: bool,
}
pub struct DeviceRegistry {
pub display: DisplayDeviceRegistry,
pub terminal: TerminalRegistry,
pub serial_terminal: SerialTerminalRegistry,
pending_initialization: IrqSafeRwLock<Vec<PendingDevice>>,
}
pub static DEVICE_REGISTRY: DeviceRegistry = DeviceRegistry {
display: DisplayDeviceRegistry::new(),
terminal: TerminalRegistry::new(),
serial_terminal: SerialTerminalRegistry::new(),
pending_initialization: IrqSafeRwLock::new(Vec::new()),
};
impl TerminalRegistry {
pub const fn new() -> Self {
Self {
registry: GenericRegistry::new(),
}
}
pub fn register(&self, terminal: Arc<dyn CharDevice>) -> Result<u32, Error> {
let id = self.registry.register(terminal.clone())?;
devfs::add_named_char_device(terminal, format!("tty{id}")).ok();
Ok(id)
}
}
impl SerialTerminalRegistry {
pub const fn new() -> Self {
Self {
registry: GenericRegistry::new(),
}
}
pub fn register(
&self,
terminal: Arc<dyn CharDevice>,
sink_level: Option<(Arc<dyn DebugSink>, LogLevel)>,
) -> Result<u32, Error> {
let id = self.registry.register(terminal.clone())?;
devfs::add_named_char_device(terminal, format!("ttyS{id}")).ok();
if let Some((sink, sink_level)) = sink_level {
debug::add_sink(sink, sink_level);
}
Ok(id)
}
}
impl DisplayDeviceRegistry {
pub const fn new() -> Self {
Self {
registry: GenericRegistry::new(),
callback: OneTimeInit::new(),
}
}
pub fn set_callback(&self, callback: DisplayRegisterCallback) {
self.callback.init(callback);
}
pub fn register(&self, display: Arc<dyn DisplayDevice>, boot: bool) -> Result<u32, Error> {
let wrapper = Arc::new(DisplayWrapper::new(display, boot)?);
let id = self.registry.register(wrapper.clone())?;
let node = devfs::add_named_block_device(wrapper.clone(), format!("fb{id}"));
if let Some(callback) = self.callback.try_get() {
callback(&wrapper.device, node.ok());
}
Ok(id)
}
}
impl<T> GenericRegistry<T> {
pub const fn new() -> Self {
Self {
members: IrqSafeRwLock::new(BTreeMap::new()),
last_index: AtomicU32::new(0),
}
}
pub fn register(&self, member: T) -> Result<u32, Error> {
let mut members = self.members.write();
let id = self.last_index.fetch_add(1, Ordering::Release);
members.insert(id, member);
Ok(id)
}
}
impl Deref for DisplayWrapper {
type Target = dyn DisplayDevice;
fn deref(&self) -> &Self::Target {
self.device.as_ref()
}
}
impl DeviceRegistry {
pub fn add_pending_initialization(&self, device: Arc<dyn Device>, irq_only: bool) {
self.pending_initialization.write().push(PendingDevice {
device,
irq_only,
failed: false,
});
}
pub fn run_initialization(&self) {
let mut devices = self.pending_initialization.write();
for pending in devices.iter_mut() {
if pending.irq_only {
continue;
}
log::debug!("Init device: {:?}", pending.device.display_name());
if let Err(error) = unsafe { pending.device.clone().init() } {
log::error!("{:?} init error: {error:?}", pending.device.display_name());
pending.failed = true;
continue;
}
}
for pending in devices.drain(..) {
if pending.failed {
continue;
}
log::debug!("Init IRQ: {:?}", pending.device.display_name());
if let Err(error) = unsafe { pending.device.clone().init_irq() } {
log::error!(
"{:?} IRQ init error: {error:?}",
pending.device.display_name()
);
}
}
}
}
async fn probe_partition_table(
dev: &Arc<dyn BlockDevice>,
) -> Result<Option<Vec<Partition>>, Error> {
if let Some(list) = partition::probe_gpt(dev).await? {
return Ok(Some(list));
}
Ok(None)
}
pub async fn probe_partitions<F: Fn(usize, Partition)>(
dev: Arc<dyn BlockDevice>,
callback: F,
) -> Result<(), Error> {
match probe_partition_table(&dev).await {
Ok(Some(partitions)) => {
for (i, partition) in partitions.into_iter().enumerate() {
callback(i, partition);
}
Ok(())
}
Ok(None) => {
log::warn!("Unrecognized or missing partition table");
Err(Error::DoesNotExist)
}
Err(error) => {
log::warn!("Could not probe partition table: {error:?}");
Err(error)
}
}
}

@ -1,3 +1,50 @@
pub mod display;
// pub mod display;
pub use libk_device::*;
// pub use libk_device::*;
use alloc::sync::Arc;
use device_api::interrupt::{
ExternalInterruptController, InterruptHandler, Irq, IrqOptions, MessageInterruptController,
};
use kernel_arch::{Architecture, ArchitectureImpl};
use libk_util::OneTimeInit;
use yggdrasil_abi::error::Error;
pub mod block;
pub mod char;
pub mod display;
pub mod manager;
// Interrupt controllers don't disappear (or at least in my OS), so it's okay to keep a reference
// inside a static OneTimeInit.
static EXTERNAL_INTC: OneTimeInit<Arc<dyn ExternalInterruptController>> = OneTimeInit::new();
pub fn register_external_interrupt_controller(intc: Arc<dyn ExternalInterruptController>) {
EXTERNAL_INTC.init(intc);
}
pub fn external_interrupt_controller(
) -> Result<&'static Arc<dyn ExternalInterruptController>, Error> {
EXTERNAL_INTC.try_get().ok_or(Error::DoesNotExist)
}
pub fn message_interrupt_controller() -> Result<&'static dyn MessageInterruptController, Error> {
ArchitectureImpl::message_interrupt_controller().ok_or(Error::DoesNotExist)
}
#[inline]
pub fn register_global_interrupt(
irq: u32,
options: IrqOptions,
handler: Arc<dyn InterruptHandler>,
) -> Result<(), Error> {
let intc = external_interrupt_controller()?;
let irq = Irq::External(irq);
intc.register_irq(irq, options, handler)?;
intc.enable_irq(irq)?;
Ok(())
}

@ -23,7 +23,8 @@
arbitrary_self_types_pointers,
result_flattening,
negative_impls,
decl_macro
decl_macro,
optimize_attribute
)]
extern crate alloc;
@ -36,9 +37,12 @@ pub use yggdrasil_abi::error;
pub mod task;
pub mod arch;
pub mod debug;
pub mod devfs;
pub mod device;
pub mod module;
pub mod random;
pub mod time;
pub mod vfs;
#[cfg(any(target_os = "none", rust_analyzer))]

@ -2,7 +2,9 @@
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use crate::device::monotonic_timestamp;
use crate::time::monotonic_time;
// use crate::device::monotonic_timestamp;
const BUFFER_SIZE: usize = 1024;
@ -75,10 +77,10 @@ pub fn range(a: u64, b: u64) -> u64 {
/// Initializes the random generator state
pub fn init() {
let now = monotonic_timestamp().unwrap();
let random_seed = now.subsec_millis();
let now = monotonic_time();
let random_seed = now.nanoseconds.wrapping_add(now.seconds);
let mut state = RandomState::new(random_seed);
let mut state = RandomState::new(random_seed as u32);
state.fill_buf();
RANDOM_STATE.init(IrqSafeSpinlock::new(state));

@ -8,7 +8,9 @@ use core::{
use alloc::vec::Vec;
use futures_util::{Future, FutureExt};
use libk_util::sync::IrqSafeSpinlock;
use yggdrasil_abi::error::Error;
use yggdrasil_abi::{error::Error, time::SystemTime};
use crate::time::monotonic_time;
// 1..32ms, tick every 1ms
static SHORT_TERM_SLEEPS: IrqSafeSpinlock<TimerWheel<32>> =
@ -54,7 +56,12 @@ impl<const STEPS: usize> TimerWheel<STEPS> {
}
pub fn wake_after(&mut self, ticks: u64, waker: &Waker) {
debug_assert!((ticks / self.interval) - self.base <= STEPS as u64,);
debug_assert!(
(ticks / self.interval) - self.base <= STEPS as u64,
"Invalid tick count: ticks={ticks}, interval={}, base={}, STEPS={STEPS}",
self.interval,
self.base
);
let ticks = ticks.max(1);
self.ring[((self.tick + ticks / self.interval - self.base) % (STEPS as u64)) as usize]
.push(waker.clone());
@ -72,13 +79,13 @@ fn register_timeout(duration: Duration, waker: &Waker) {
}
pub struct SleepFuture {
deadline: Duration,
deadline: SystemTime,
}
impl SleepFuture {
pub fn remaining(&self) -> Option<Duration> {
let now = libk_device::monotonic_timestamp().unwrap();
match self.deadline.checked_sub(now) {
let now = monotonic_time();
match self.deadline.checked_sub(&now) {
Some(duration) if !duration.is_zero() => Some(duration),
_ => None,
}
@ -103,14 +110,14 @@ impl Future for SleepFuture {
/// Suspends the task until given duration passes.
pub fn sleep(duration: Duration) -> SleepFuture {
let now = libk_device::monotonic_timestamp().unwrap();
let now = monotonic_time();
let deadline = now + duration;
SleepFuture { deadline }
}
/// Suspends the task until the given deadline comes.
pub fn sleep_until(deadline: Duration) -> SleepFuture {
pub fn sleep_until(deadline: SystemTime) -> SleepFuture {
SleepFuture { deadline }
}
@ -144,7 +151,7 @@ pub async fn maybe_timeout<T, F: Future<Output = T> + Send>(
}
/// Updates the runtime's time
pub fn tick(_now: Duration) {
pub fn tick() {
SHORT_TERM_SLEEPS.lock().tick();
LONG_TERM_SLEEPS.lock().tick();
}

@ -10,10 +10,13 @@ use kernel_arch::{
task::{Scheduler, TaskContext},
Architecture, ArchitectureImpl, CpuImpl,
};
use libk_device::monotonic_timestamp;
use libk_util::{sync::IrqGuard, OneTimeInit};
use yggdrasil_abi::time::SystemTime;
use crate::task::{thread::Thread, TaskContextImpl, ThreadId, ThreadState};
use crate::{
task::{thread::Thread, TaskContextImpl, ThreadId, ThreadState},
time::monotonic_time,
};
/// Per-CPU queue
pub struct CpuQueue {
@ -22,7 +25,7 @@ pub struct CpuQueue {
idle: Cell<TaskContextImpl>,
stats: SchedulerStats,
last_stats_measure: Cell<Option<Duration>>,
last_stats_measure: Cell<Option<SystemTime>>,
}
// TODO sort out Cell<...>
@ -174,9 +177,9 @@ impl Scheduler for CpuQueue {
let current_id = cpu.current_thread_id();
let current = current_id.and_then(Thread::get);
let t = monotonic_timestamp().unwrap();
let t = monotonic_time();
if let Some(t0) = self.last_stats_measure.get() {
let dt = t - t0;
let dt = t.checked_sub(&t0).unwrap();
self.update_stats(dt, current.as_ref());
}
self.last_stats_measure.set(Some(t));

24
kernel/libk/src/time.rs Normal file

@ -0,0 +1,24 @@
use core::sync::atomic::{AtomicU64, Ordering};
use yggdrasil_abi::time::SystemTime;
static MONOTONIC_MILLIS: AtomicU64 = AtomicU64::new(0);
pub fn monotonic_time() -> SystemTime {
let millis = MONOTONIC_MILLIS.load(Ordering::Acquire);
let nanoseconds = (millis % 1000) * 1000000;
let seconds = millis / 1000;
SystemTime {
seconds,
nanoseconds,
}
}
pub fn add_ticks(ticks: u64) -> u64 {
MONOTONIC_MILLIS.fetch_add(ticks, Ordering::Release)
}
pub fn set_ticks(ticks: u64) -> u64 {
MONOTONIC_MILLIS.swap(ticks, Ordering::Release)
}

@ -1,655 +0,0 @@
#![allow(clippy::missing_transmute_annotations)]
use core::{
marker::PhantomData,
ops::{Deref, DerefMut},
};
use alloc::sync::Arc;
use kernel_arch::mem::PhysicalMemoryAllocator;
use libk_mm::{address::PhysicalAddress, phys::GlobalPhysicalAllocator, PageBox};
use libk_util::{lru_hash_table::LruCache, sync::spin_rwlock::IrqSafeRwLock};
use yggdrasil_abi::error::Error;
use crate::task::sync::AsyncMutex;
use super::BlockDevice;
pub struct CachedSegment<
A: PhysicalMemoryAllocator<Address = PhysicalAddress> = GlobalPhysicalAllocator,
> {
data: PageBox<[u8], A>,
dirty: bool,
}
pub struct UncachedCache<
A: PhysicalMemoryAllocator<Address = PhysicalAddress> = GlobalPhysicalAllocator,
> {
device: &'static dyn BlockDevice,
block_size: usize,
_pd: PhantomData<A>,
}
pub enum DeviceMapper<
A: PhysicalMemoryAllocator<Address = PhysicalAddress> = GlobalPhysicalAllocator,
> {
Uncached(UncachedCache<A>),
Cached(BlockCache<A>),
}
pub struct BlockCache<
A: PhysicalMemoryAllocator<Address = PhysicalAddress> = GlobalPhysicalAllocator,
> {
device: &'static dyn BlockDevice,
block_size: usize,
segment_size: usize,
cache: AsyncMutex<LruCache<u64, Arc<IrqSafeRwLock<CachedSegment<A>>>>>,
}
impl DeviceMapper {
pub fn cached_with_capacity(
device: &'static dyn BlockDevice,
block_size: usize,
segment_size: usize,
bucket_capacity: usize,
bucket_count: usize,
) -> DeviceMapper {
DeviceMapper::cached_with_capacity_in(
device,
block_size,
segment_size,
bucket_capacity,
bucket_count,
)
}
pub fn uncached(device: &'static dyn BlockDevice, block_size: usize) -> DeviceMapper {
DeviceMapper::uncached_in(device, block_size)
}
}
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> DeviceMapper<A> {
pub fn cached_with_capacity_in(
device: &'static dyn BlockDevice,
block_size: usize,
segment_size: usize,
bucket_capacity: usize,
bucket_count: usize,
) -> DeviceMapper<A> {
let cache = BlockCache::<A>::with_capacity_in(
device,
block_size,
segment_size,
bucket_capacity,
bucket_count,
);
DeviceMapper::<A>::Cached(cache)
}
pub fn uncached_in(device: &'static dyn BlockDevice, block_size: usize) -> DeviceMapper<A> {
if block_size % device.block_size() != 0 {
panic!("Cache block size is not multiple of device block size");
}
let uncache = UncachedCache::<A> {
device,
block_size,
_pd: PhantomData,
};
Self::Uncached(uncache)
}
pub fn device(&self) -> &'static dyn BlockDevice {
match self {
Self::Uncached(uncache) => uncache.device(),
Self::Cached(cache) => cache.device(),
}
}
pub async fn try_with<T, F: FnOnce(&[u8]) -> Result<T, Error>>(
&self,
pos: u64,
mapper: F,
) -> Result<T, Error> {
match self {
Self::Uncached(uncache) => uncache.try_with(pos, mapper).await,
Self::Cached(cache) => cache.try_with(pos, mapper).await,
}
}
pub async fn try_with_mut<T, F: FnOnce(&mut [u8]) -> Result<T, Error>>(
&self,
pos: u64,
size: usize,
mapper: F,
) -> Result<T, Error> {
match self {
Self::Uncached(uncache) => uncache.try_with_mut(pos, size, mapper).await,
Self::Cached(cache) => cache.try_with_mut(pos, size, mapper).await,
}
}
pub async fn flush(&self) -> Result<(), Error> {
match self {
Self::Uncached(_) => Ok(()),
Self::Cached(cache) => {
cache.flush().await;
Ok(())
}
}
}
}
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> UncachedCache<A> {
pub fn device(&self) -> &'static dyn BlockDevice {
self.device
}
pub async fn try_with<T, F: FnOnce(&[u8]) -> Result<T, Error>>(
&self,
pos: u64,
mapper: F,
) -> Result<T, Error> {
let mut data = PageBox::<_, A>::new_uninit_slice_in(self.block_size)?;
self.device.read_aligned(pos, data.as_slice_mut()).await?;
let result = mapper(unsafe { data.assume_init_slice_ref() })?;
Ok(result)
}
pub async fn try_with_mut<T, F: FnOnce(&mut [u8]) -> Result<T, Error>>(
&self,
pos: u64,
size: usize,
mapper: F,
) -> Result<T, Error> {
let mut data = PageBox::<_, A>::new_uninit_slice_in(self.block_size)?;
// No need to read a block only to then fully rewrite it
if size != self.block_size {
self.device.read_aligned(pos, data.as_slice_mut()).await?;
}
let mut data = unsafe { data.assume_init_slice() };
let result = mapper(&mut data[..])?;
self.device.write_aligned(pos, data.as_slice()).await?;
Ok(result)
}
}
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> BlockCache<A> {
pub fn with_capacity_in(
device: &'static dyn BlockDevice,
block_size: usize,
segment_size: usize,
bucket_capacity: usize,
bucket_count: usize,
) -> BlockCache<A> {
assert_eq!(
block_size % device.block_size(),
0,
"Block size is not a multiple of device block size"
);
assert_eq!(
segment_size % block_size,
0,
"Segment size is not a multiple of block size"
);
assert!(
segment_size >= block_size,
"Segment size is less than block size"
);
log::info!("New block cache: block: {block_size}B, segment: {segment_size}B, geometry: {bucket_capacity}x{bucket_count}");
log::info!(
"Worst-case memory usage: {}K",
(segment_size * bucket_capacity * bucket_count) / 1024
);
BlockCache {
device,
block_size,
segment_size,
cache: AsyncMutex::new(LruCache::with_capacity(bucket_capacity, bucket_count)),
}
}
pub fn device(&self) -> &'static dyn BlockDevice {
self.device
}
async fn evict_block(
&self,
segment_position: u64,
block: Arc<IrqSafeRwLock<CachedSegment<A>>>,
) {
let read = block.read();
if read.dirty {
assert_eq!(segment_position % self.segment_size as u64, 0);
if let Err(err) = self
.device
.write_aligned(segment_position, read.data.as_slice())
.await
{
log::error!("Disk error: flushing block {}: {:?}", segment_position, err);
}
}
}
async fn fetch_block(
&self,
segment_position: u64,
) -> Result<Arc<IrqSafeRwLock<CachedSegment<A>>>, Error> {
// let need_read = write_size.map(|sz| sz != self.block_size).unwrap_or(true);
let mut data = PageBox::new_uninit_slice_in(self.segment_size)?;
// // Don't read a block that's going to be fully rewritten immediately
// if need_read {
self.device
.read_aligned(segment_position, data.as_slice_mut())
.await?;
// }
let data = unsafe { data.assume_init_slice() };
Ok(Arc::new(IrqSafeRwLock::new(CachedSegment {
data,
dirty: false,
})))
}
async fn entry(
&self,
segment_position: u64,
) -> Result<Arc<IrqSafeRwLock<CachedSegment<A>>>, Error> {
assert_eq!(segment_position % self.segment_size as u64, 0);
let mut lock = self.cache.lock().await;
let (value, evicted) = lock
.try_get_or_insert_with_async(segment_position, || self.fetch_block(segment_position))
.await?;
if let Some((segment_position, block)) = evicted {
self.evict_block(segment_position, block).await;
}
Ok(value.clone())
}
pub async fn try_with<T, F: FnOnce(&[u8]) -> Result<T, Error>>(
&self,
block_position: u64,
mapper: F,
) -> Result<T, Error> {
let segment_position = block_position & !(self.segment_size as u64 - 1);
let segment_offset = (block_position & (self.segment_size as u64 - 1)) as usize;
let block = self.entry(segment_position).await?;
let result = mapper(&block.read()[segment_offset..segment_offset + self.block_size])?;
Ok(result)
}
pub async fn try_with_mut<T, F: FnOnce(&mut [u8]) -> Result<T, Error>>(
&self,
block_position: u64,
_size: usize,
mapper: F,
) -> Result<T, Error> {
let segment_position = block_position & !(self.segment_size as u64 - 1);
let segment_offset = (block_position & (self.segment_size as u64 - 1)) as usize;
let block = self.entry(segment_position).await?;
let mut block = block.write();
block.dirty = true;
let result = mapper(&mut block[segment_offset..segment_offset + self.block_size])?;
Ok(result)
}
pub async fn flush(&self) {
todo!()
// for (pos, block) in self.cache.lock().await.flush() {
// self.evict_block(pos, block).await;
// }
}
}
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> CachedSegment<A> {
pub fn set_dirty(&mut self) {
self.dirty = true;
}
}
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> Deref for CachedSegment<A> {
type Target = PageBox<[u8], A>;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<A: PhysicalMemoryAllocator<Address = PhysicalAddress>> DerefMut for CachedSegment<A> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.dirty = true;
&mut self.data
}
}
#[cfg(test)]
mod tests {
use core::{
ffi::c_void,
mem::MaybeUninit,
ptr::null_mut,
sync::atomic::{AtomicBool, Ordering},
};
use std::{io, sync::Mutex};
use async_trait::async_trait;
use kernel_arch::mem::PhysicalMemoryAllocator;
use libk_mm::{address::PhysicalAddress, PageBox, PageSlice};
use yggdrasil_abi::error::Error;
use crate::vfs::block::{BlockDevice, NgBlockDevice, NgBlockDeviceWrapper};
use super::BlockCache;
struct DummyBlock {
block_size: usize,
block_count: usize,
deny_writes: AtomicBool,
data: Mutex<Vec<u8>>,
}
struct PA;
impl DummyBlock {
pub fn new(block_size: usize, block_count: usize) -> Self {
let mut data = vec![0; block_size * block_count];
for i in 0..block_count {
let block = &mut data[block_size * i..block_size * (i + 1)];
block.fill(i as u8);
}
Self {
data: Mutex::new(data),
deny_writes: AtomicBool::new(false),
block_size,
block_count,
}
}
}
#[async_trait::async_trait]
impl NgBlockDevice for DummyBlock {
type Error = Error;
async fn read(
&self,
lba: u64,
buffer: &mut PageSlice<MaybeUninit<u8>>,
) -> Result<(), Error> {
let start = lba as usize * self.block_size;
let end = start + buffer.len();
if end > self.block_count * self.block_size {
return Err(Error::InvalidArgument);
}
let data = self.data.lock().unwrap();
let buffer = unsafe { MaybeUninit::slice_assume_init_mut(&mut buffer[..]) };
buffer.copy_from_slice(&data[start..end]);
Ok(())
}
async fn write(&self, lba: u64, buffer: &PageSlice<u8>) -> Result<(), Error> {
if self.deny_writes.load(Ordering::Acquire) {
panic!("write() with deny_writes = true");
}
let start = lba as usize * self.block_size;
let end = start + buffer.len();
if end > self.block_count * self.block_size {
return Err(Error::InvalidArgument);
}
let mut data = self.data.lock().unwrap();
data[start..end].copy_from_slice(&buffer[..]);
Ok(())
}
fn block_size(&self) -> usize {
self.block_size
}
fn block_count(&self) -> usize {
self.block_count
}
}
impl PhysicalMemoryAllocator for PA {
type Address = PhysicalAddress;
unsafe fn free_page(page: Self::Address) {
let base = page.try_into_usize().unwrap();
let base = core::ptr::with_exposed_provenance_mut::<c_void>(base);
if unsafe { libc::munmap(base, 0x1000) } != 0 {
let err = io::Error::last_os_error();
panic!("free_page: munmap returned {err}");
}
}
fn allocate_page() -> Result<Self::Address, Error> {
Self::allocate_contiguous_pages(1)
}
fn allocate_contiguous_pages(count: usize) -> Result<Self::Address, Error> {
let base = unsafe {
libc::mmap(
null_mut(),
count * 0x1000,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_ANON | libc::MAP_PRIVATE,
-1,
0,
)
};
if base != libc::MAP_FAILED {
let base = base.addr();
Ok(PhysicalAddress::from_usize(base))
} else {
Err(Error::OutOfMemory)
}
}
}
const BS: usize = 1024;
// The test must not crash with denied writes
#[tokio::test]
async fn test_no_modification() {
let device = Box::leak(Box::new(DummyBlock::new(BS, 1024)));
let wrapper = NgBlockDeviceWrapper::new(device);
let cache = BlockCache::<PA>::with_capacity_in(wrapper, BS, BS, 64, 8);
device.deny_writes.store(true, Ordering::Release);
cache
.try_with(1 * BS as u64, |block| {
assert!(block.iter().all(|x| *x == 1));
Ok(())
})
.await
.unwrap();
cache
.try_with(2 * BS as u64, |block| {
assert!(block.iter().all(|x| *x == 2));
Ok(())
})
.await
.unwrap();
cache.flush().await;
}
#[tokio::test]
async fn test_partial_modification() {
let device = Box::leak(Box::new(DummyBlock::new(BS, 1024)));
let wrapper = NgBlockDeviceWrapper::new(device);
// 8 * 8
let cache = BlockCache::<PA>::with_capacity_in(wrapper, BS, BS, 8, 8);
const LBA: u64 = 1;
cache
.try_with_mut(LBA * BS as u64, 16, |block| {
block[0..16].fill(0x12);
Ok(())
})
.await
.unwrap();
cache.flush().await;
{
let mut buffer = PageBox::<_, PA>::new_uninit_slice_in(BS).unwrap();
device.read(LBA, buffer.as_slice_mut()).await.unwrap();
let buffer = unsafe { buffer.assume_init_slice() };
buffer[0..16].iter().for_each(|&x| {
assert_eq!(x, 0x12);
});
buffer[16..].iter().for_each(|&x| {
assert_eq!(x, LBA as u8);
});
}
cache
.try_with_mut(LBA * BS as u64, 16, |block| {
block[16..32].fill(0x23);
Ok(())
})
.await
.unwrap();
cache
.try_with_mut(LBA * BS as u64, 16, |block| {
block[48..64].fill(0x34);
Ok(())
})
.await
.unwrap();
cache
.try_with_mut(LBA * BS as u64, 128, |block| {
block[128..256].fill(0xF1);
Ok(())
})
.await
.unwrap();
cache.flush().await;
{
let mut buffer = PageBox::<_, PA>::new_uninit_slice_in(BS).unwrap();
device.read(LBA, buffer.as_slice_mut()).await.unwrap();
let buffer = unsafe { buffer.assume_init_slice() };
buffer[0..16].iter().for_each(|&x| {
assert_eq!(x, 0x12);
});
buffer[16..32].iter().for_each(|&x| {
assert_eq!(x, 0x23);
});
buffer[48..64].iter().for_each(|&x| {
assert_eq!(x, 0x34);
});
buffer[128..256].iter().for_each(|&x| {
assert_eq!(x, 0xF1);
});
buffer[32..48].iter().for_each(|&x| {
assert_eq!(x, LBA as u8);
});
buffer[64..128].iter().for_each(|&x| {
assert_eq!(x, LBA as u8);
});
buffer[256..].iter().for_each(|&x| {
assert_eq!(x, LBA as u8);
});
}
}
#[tokio::test]
async fn test_implicit_eviction() {
let device = Box::leak(Box::new(DummyBlock::new(BS, 1024)));
let wrapper = NgBlockDeviceWrapper::new(device);
// 8 * 8
let cache = BlockCache::<PA>::with_capacity_in(wrapper, BS, BS, 8, 8);
fn mapper(x: u64) -> u8 {
(x + 3) as u8
}
// Go through all blocks, fill those with some values
for i in 0..1024 {
cache
.try_with_mut(i * BS as u64, BS, |block| {
block.fill(mapper(i));
Ok(())
})
.await
.unwrap();
}
cache.flush().await;
for i in 0..1024 {
let mut buffer = PageBox::<_, PA>::new_uninit_slice_in(BS).unwrap();
device.read(i, buffer.as_slice_mut()).await.unwrap();
let buffer = unsafe { buffer.assume_init_slice() };
assert!(buffer.iter().all(|x| *x == mapper(i)));
}
for i in 0..1023 {
cache
.try_with_mut(i * BS as u64, BS, |block| {
block.fill(0x12);
Ok(())
})
.await
.unwrap();
cache
.try_with_mut((i + 1) * BS as u64, BS, |block| {
block.fill(0x23);
Ok(())
})
.await
.unwrap();
}
for i in 0..1023 {
cache
.try_with_mut(i * BS as u64, BS, |block| {
block.iter_mut().for_each(|x| *x += 1);
Ok(())
})
.await
.unwrap();
cache
.try_with_mut((i + 1) * BS as u64, BS, |block| {
block.iter_mut().for_each(|x| *x += 2);
Ok(())
})
.await
.unwrap();
}
cache.flush().await;
{
let mut buffer = PageBox::<_, PA>::new_uninit_slice_in(BS).unwrap();
device.read(0, buffer.as_slice_mut()).await.unwrap();
let buffer = unsafe { buffer.assume_init_slice() };
buffer.iter().for_each(|&x| {
assert_eq!(x, 0x13, "block 0 mismatch");
});
}
for i in 1..1023 {
let mut buffer = PageBox::<_, PA>::new_uninit_slice_in(BS).unwrap();
device.read(i, buffer.as_slice_mut()).await.unwrap();
let buffer = unsafe { buffer.assume_init_slice() };
buffer.iter().for_each(|&x| {
assert_eq!(x, 0x15, "block {i} mismatch");
});
}
{
let mut buffer = PageBox::<_, PA>::new_uninit_slice_in(BS).unwrap();
device.read(1023, buffer.as_slice_mut()).await.unwrap();
let buffer = unsafe { buffer.assume_init_slice() };
buffer.iter().for_each(|&x| {
assert_eq!(x, 0x25, "block 1023 mismatch");
});
}
}
}

@ -1,235 +0,0 @@
#![allow(unused)]
use core::{
fmt,
mem::MaybeUninit,
ops::Range,
pin::Pin,
task::{Context, Poll},
};
use alloc::boxed::Box;
use async_trait::async_trait;
use futures_util::{task::AtomicWaker, Future};
use libk_mm::{address::PhysicalAddress, table::MapAttributes, PageBox, PageProvider, PageSlice};
use libk_util::{waker::QueueWaker, RangeExt};
use yggdrasil_abi::{error::Error, io::DeviceRequest};
use crate::{
task::debug,
vfs::block::{
request::{IoOperation, IoRequest, IoSubmissionId},
BlockDevice,
},
};
#[async_trait]
pub trait NgBlockDevice: Sync {
type Error: fmt::Debug + Into<Error> = Error;
async fn read(
&self,
lba: u64,
buffer: &mut PageSlice<MaybeUninit<u8>>,
) -> Result<(), Self::Error>;
async fn write(&self, lba: u64, buffer: &PageSlice<u8>) -> Result<(), Self::Error>;
fn block_size(&self) -> usize;
fn block_count(&self) -> usize;
fn max_blocks_per_request(&self) -> usize {
1
}
}
pub struct NgBlockDeviceWrapper<'a, D: NgBlockDevice + 'a> {
device: &'a D,
pub(crate) block_size: usize,
pub(crate) block_count: u64,
#[allow(unused)]
pub(crate) max_blocks_per_request: usize,
}
impl<'a, D: NgBlockDevice + 'a> NgBlockDeviceWrapper<'a, D> {
pub fn new(device: &'a D) -> &'static Self {
Box::leak(Box::new(Self {
device,
block_size: device.block_size(),
block_count: device.block_count() as u64,
max_blocks_per_request: device.max_blocks_per_request(),
}))
}
fn lba_range(&self, byte_range: Range<u64>) -> (Range<u64>, usize) {
let lba_start = byte_range.start / self.block_size as u64;
let lba_end = byte_range.end.div_ceil(self.block_size as u64);
let lba_off = (byte_range.start % self.block_size as u64) as usize;
(lba_start..lba_end, lba_off)
}
fn handle_drive_error(e: D::Error) -> Error {
log::error!("Drive error: {:?}", e);
e.into()
}
}
#[async_trait]
impl<'a, D: NgBlockDevice + 'a> BlockDevice for NgBlockDeviceWrapper<'a, D> {
async fn read_aligned(
&self,
pos: u64,
buf: &mut PageSlice<MaybeUninit<u8>>,
) -> Result<(), Error> {
if pos % self.block_size as u64 != 0 || buf.len() % self.block_size != 0 {
// TODO fallback to unaligned read
todo!()
}
let range = 0..buf.len() / self.block_size;
for chunk in range.chunked_range(self.max_blocks_per_request) {
let lba = chunk.start as u64 + pos / self.block_size as u64;
let buffer_range = chunk.mul(self.block_size);
self.device
.read(lba, buf.subslice_mut(buffer_range))
.await
.map_err(Self::handle_drive_error)?;
}
Ok(())
}
async fn write_aligned(&self, pos: u64, buf: &PageSlice<u8>) -> Result<(), Error> {
if pos % self.block_size as u64 != 0 || buf.len() % self.block_size != 0 {
// TODO fallback to unaligned write
todo!()
}
let range = 0..buf.len() / self.block_size;
for chunk in range.chunked_range(self.max_blocks_per_request) {
let lba = chunk.start as u64 + pos / self.block_size as u64;
let buffer_range = chunk.mul(self.block_size);
self.device
.write(lba, buf.subslice(buffer_range))
.await
.map_err(Self::handle_drive_error)?;
}
Ok(())
}
async fn read(&self, mut pos: u64, mut buf: &mut [u8]) -> Result<usize, Error> {
let start_lba = pos / self.block_size as u64;
let end_lba = (pos + buf.len() as u64)
.div_ceil(self.block_size as u64)
.min(self.block_count as u64);
if start_lba >= end_lba {
return Ok(0);
}
let mut remaining = buf.len();
let mut remaining_lba = (end_lba - start_lba) as usize;
let mut lba = start_lba;
let mut offset = 0;
let max_lba_count = remaining_lba.min(self.max_blocks_per_request);
let mut buffer = PageBox::new_uninit_slice(max_lba_count * self.block_size)?;
while remaining != 0 {
let block_count = remaining_lba.min(self.max_blocks_per_request);
let block_offset = (pos % self.block_size as u64) as usize;
let amount = (block_count * self.block_size - block_offset).min(remaining);
self.device
.read(lba, buffer.as_slice_mut())
.await
.map_err(Self::handle_drive_error)?;
let data = unsafe {
MaybeUninit::slice_assume_init_ref(&buffer[block_offset..block_offset + amount])
};
buf[offset..offset + amount].copy_from_slice(data);
lba += block_count as u64;
remaining_lba -= block_count;
remaining -= amount;
offset += amount;
}
assert_eq!(remaining_lba, 0);
Ok(offset)
}
async fn write(&self, mut pos: u64, mut buf: &[u8]) -> Result<usize, Error> {
let len = buf.len();
let mut remaining = buf.len();
while remaining != 0 {
let block_offset = pos as usize % self.block_size;
let lba = pos / self.block_size as u64;
let amount = core::cmp::min(self.block_size - block_offset, buf.len());
let mut block = PageBox::new_uninit_slice(self.block_size)?;
if amount != self.block_size {
// Need to read the block first -- it's modified partially
self.device
.read(lba, block.as_slice_mut())
.await
.map_err(Self::handle_drive_error)?;
}
let mut block = unsafe { block.assume_init_slice() };
block[block_offset..block_offset + amount].copy_from_slice(&buf[..amount]);
// Write the block back
self.device
.write(lba, block.as_slice())
.await
.map_err(Self::handle_drive_error)?;
buf = &buf[amount..];
remaining -= amount;
pos += amount as u64;
}
Ok(len)
}
fn size(&self) -> Result<u64, Error> {
Ok(self.block_size as u64 * self.block_count)
}
fn block_size(&self) -> usize {
self.block_size
}
}
impl<'a, D: NgBlockDevice + 'a> PageProvider for NgBlockDeviceWrapper<'a, D> {
fn get_page(&self, offset: u64) -> Result<PhysicalAddress, Error> {
todo!()
}
fn clone_page(
&self,
offset: u64,
src_phys: PhysicalAddress,
src_attrs: MapAttributes,
) -> Result<PhysicalAddress, Error> {
todo!()
}
fn release_page(&self, offset: u64, phys: PhysicalAddress) -> Result<(), Error> {
todo!()
}
}

@ -1,133 +0,0 @@
#![allow(missing_docs)]
use core::mem::MaybeUninit;
use alloc::{boxed::Box, vec::Vec};
use async_trait::async_trait;
use libk_mm::{PageProvider, PageSlice};
use partition::Partition;
use yggdrasil_abi::{error::Error, io::DeviceRequest};
pub mod cache;
pub mod device;
pub mod partition;
pub mod request;
pub use device::{NgBlockDevice, NgBlockDeviceWrapper};
use crate::task::runtime;
pub async fn probe_partitions_async<
D: NgBlockDevice + 'static,
F: Fn(usize, &'static dyn BlockDevice) -> Result<(), Error> + Send + 'static,
>(
dev: &'static NgBlockDeviceWrapper<'static, D>,
callback: F,
) -> Result<(), Error> {
async fn probe_table<D: NgBlockDevice + 'static>(
dev: &'static NgBlockDeviceWrapper<'static, D>,
) -> Result<Option<Vec<Partition<'static, D>>>, Error> {
if let Some(partitions) = partition::probe_gpt(dev).await? {
return Ok(Some(partitions));
}
Ok(None)
}
match probe_table(dev).await {
Ok(Some(partitions)) => {
// Create block devices for the partitions
for (i, partition) in partitions.into_iter().enumerate() {
let partition_blkdev: &Partition<'static, D> = Box::leak(Box::new(partition));
if let Err(error) = callback(i, partition_blkdev) {
log::warn!("Could not add partition {}: {:?}", i, error);
}
}
}
Ok(None) => {
log::warn!("Unknown or missing partition table");
}
Err(error) => {
log::warn!("Could not probe partition table: {:?}", error);
}
}
Ok(())
}
pub fn probe_partitions<
D: NgBlockDevice + 'static,
F: Fn(usize, &'static dyn BlockDevice) -> Result<(), Error> + Send + 'static,
>(
dev: &'static NgBlockDeviceWrapper<'static, D>,
callback: F,
) -> Result<(), Error> {
runtime::spawn(probe_partitions_async(dev, callback))
}
/// Block device interface
#[allow(unused)]
#[async_trait]
pub trait BlockDevice: PageProvider + Sync {
async fn read_aligned(
&self,
pos: u64,
buf: &mut PageSlice<MaybeUninit<u8>>,
) -> Result<(), Error> {
Err(Error::NotImplemented)
}
async fn write_aligned(&self, pos: u64, buf: &PageSlice<u8>) -> Result<(), Error> {
Err(Error::NotImplemented)
}
async fn read(&self, pos: u64, buf: &mut [u8]) -> Result<usize, Error> {
Err(Error::NotImplemented)
}
async fn write(&self, pos: u64, buf: &[u8]) -> Result<usize, Error> {
Err(Error::NotImplemented)
}
async fn read_exact(&self, pos: u64, buf: &mut [u8]) -> Result<(), Error> {
let count = self.read(pos, buf).await?;
if count == buf.len() {
Ok(())
} else {
Err(Error::InvalidOperation)
}
}
async fn write_exact(&self, pos: u64, buf: &[u8]) -> Result<(), Error> {
let count = self.write(pos, buf).await?;
if count == buf.len() {
Ok(())
} else {
Err(Error::InvalidOperation)
}
}
/// Returns the size of the block device in bytes
fn size(&self) -> Result<u64, Error> {
Err(Error::NotImplemented)
}
/// Returns `true` if the device can be read from
fn is_readable(&self) -> bool {
true
}
/// Returns `true` if the device can be written to
fn is_writable(&self) -> bool {
true
}
fn block_size(&self) -> usize {
512
}
/// Performs a device-specific function
fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> {
Err(Error::NotImplemented)
}
}

@ -1,202 +0,0 @@
use core::mem::{size_of, MaybeUninit};
use alloc::{boxed::Box, vec, vec::Vec};
use async_trait::async_trait;
use bytemuck::{Pod, Zeroable};
use libk_mm::{address::PhysicalAddress, table::MapAttributes, PageBox, PageProvider, PageSlice};
use static_assertions::const_assert_eq;
use uuid::Uuid;
use yggdrasil_abi::{error::Error, io::DeviceRequest};
use super::{BlockDevice, NgBlockDevice, NgBlockDeviceWrapper};
pub struct Partition<'a, D: NgBlockDevice + 'a> {
pub device: &'a NgBlockDeviceWrapper<'a, D>,
pub lba_start: u64,
pub lba_end: u64,
}
#[derive(Clone, Copy)]
#[repr(C)]
struct GptHeader {
signature: [u8; 8],
revision: u32,
header_size: u32,
crc32: u32,
_0: u32,
header_lba: u64,
alternate_header_lba: u64,
first_usable_lba: u64,
last_usable_lba: u64,
guid: [u8; 16],
partition_table_lba: u64,
partition_table_len: u32,
partition_table_entry_size: u32,
partition_table_crc32: u32,
_1: [u8; 420],
}
#[derive(Clone, Copy, Zeroable, Pod)]
#[repr(C)]
struct GptEntry {
type_guid: Uuid,
part_guid: Uuid,
lba_start: u64,
lba_end: u64,
attrs: u64,
}
const_assert_eq!(size_of::<GptHeader>(), 512);
impl<'a, D: NgBlockDevice + 'a> Partition<'a, D> {
fn end_byte(&self) -> u64 {
self.lba_end * self.device.block_size as u64
}
fn start_byte(&self) -> u64 {
self.lba_start * self.device.block_size as u64
}
}
impl<'a, D: NgBlockDevice + 'a> PageProvider for Partition<'a, D> {
fn get_page(&self, _offset: u64) -> Result<PhysicalAddress, Error> {
todo!()
}
fn clone_page(
&self,
_offset: u64,
_src_phys: PhysicalAddress,
_src_attrs: MapAttributes,
) -> Result<PhysicalAddress, Error> {
todo!()
}
fn release_page(&self, _offset: u64, _phys: PhysicalAddress) -> Result<(), Error> {
todo!()
}
}
#[async_trait]
impl<'a, D: NgBlockDevice + 'a> BlockDevice for Partition<'a, D> {
async fn read_aligned(
&self,
pos: u64,
buf: &mut PageSlice<MaybeUninit<u8>>,
) -> Result<(), Error> {
let start = self.start_byte() + pos;
if start % self.device.block_size as u64 != 0
|| start + buf.len() as u64 >= self.end_byte()
|| buf.len() % self.device.block_size != 0
{
return Err(Error::InvalidArgument);
}
self.device.read_aligned(start, buf).await
}
async fn write_aligned(&self, pos: u64, buf: &PageSlice<u8>) -> Result<(), Error> {
let start = self.start_byte() + pos;
if start % self.device.block_size as u64 != 0
|| start + buf.len() as u64 >= self.end_byte()
|| buf.len() % self.device.block_size != 0
{
return Err(Error::InvalidArgument);
}
self.device.write_aligned(start, buf).await
}
async fn read(&self, pos: u64, buf: &mut [u8]) -> Result<usize, Error> {
if pos >= self.end_byte() {
return Ok(0);
}
let start = self.start_byte() + pos;
let end = core::cmp::min(start + buf.len() as u64, self.end_byte());
let count = (end - start) as usize;
if count == 0 {
todo!()
}
self.device.read(start, &mut buf[..count]).await
}
async fn write(&self, pos: u64, buf: &[u8]) -> Result<usize, Error> {
if pos >= self.end_byte() {
return Ok(0);
}
let start = self.start_byte() + pos;
let end = core::cmp::min(start + buf.len() as u64, self.end_byte());
let count = (end - start) as usize;
self.device.write(start, &buf[..count]).await
}
fn size(&self) -> Result<u64, Error> {
Ok((self.lba_end - self.lba_start) * self.device.block_size as u64)
}
fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> {
self.device.device_request(req)
}
}
async unsafe fn read_struct_lba<T>(
dev: &'static dyn BlockDevice,
lba: u64,
) -> Result<PageBox<T>, Error> {
assert_eq!(size_of::<T>(), 512);
let mut data = PageBox::new_uninit()?;
dev.read_aligned(lba * 512, PageBox::as_bytes_mut(&mut data))
.await?;
Ok(data.assume_init())
}
pub(crate) async fn probe_gpt<D: NgBlockDevice + 'static>(
dev: &'static NgBlockDeviceWrapper<'static, D>,
) -> Result<Option<Vec<Partition<'static, D>>>, Error> {
let header = unsafe { read_struct_lba::<GptHeader>(dev, 1) }.await?;
if &header.signature != b"EFI PART" {
// Not a GPT partition table
return Ok(None);
}
let pt_entsize = header.partition_table_entry_size as usize;
let pt_len = header.partition_table_len as usize;
let mut pt_data = PageBox::new_slice(0, pt_len * pt_entsize)?;
log::info!("pt_entsize = {}, pt_len = {}", pt_entsize, pt_len);
assert!(size_of::<GptEntry>() <= pt_entsize);
dev.read_exact(header.partition_table_lba * 512, &mut pt_data)
.await?;
let mut partitions = vec![];
for i in 0..pt_len {
let pt_entry_data = &pt_data[i * pt_entsize..i * pt_entsize + size_of::<GptEntry>()];
let pt_entry: &GptEntry = bytemuck::from_bytes(pt_entry_data);
if pt_entry.type_guid.is_nil() {
break;
}
log::debug!(
"Found partition: guid={}, start={}, end={}",
pt_entry.type_guid,
pt_entry.lba_start,
pt_entry.lba_end
);
partitions.push(Partition {
device: dev,
lba_start: pt_entry.lba_start,
lba_end: pt_entry.lba_end,
});
}
Ok(Some(partitions))
}

@ -1,19 +0,0 @@
use core::mem::MaybeUninit;
use libk_mm::PageBox;
pub enum IoOperation {
Read { lba: u64, count: usize },
Write { lba: u64, count: usize },
}
pub struct IoRequest<'a> {
pub operation: IoOperation,
pub data: &'a mut PageBox<[MaybeUninit<u8>]>,
}
#[derive(Clone, Copy, Debug)]
pub struct IoSubmissionId {
pub queue_id: usize,
pub command_id: usize,
}

@ -1,93 +0,0 @@
use alloc::boxed::Box;
use async_trait::async_trait;
use yggdrasil_abi::{error::Error, io::DeviceRequest};
use crate::vfs::{
block::BlockDevice,
node::{CommonImpl, NodeRef},
traits::FileReadiness,
};
/// Character device interface
#[allow(unused)]
#[async_trait]
pub trait CharDevice: FileReadiness + Sync {
/// Reads data from the device, blocking until operation has completed
async fn read(&'static self, buf: &mut [u8]) -> Result<usize, Error> {
Err(Error::NotImplemented)
}
/// Writes data to the devices, blocking until operation has completed
async fn write(&'static self, buf: &[u8]) -> Result<usize, Error> {
Err(Error::NotImplemented)
}
/// Reads data from the device, returns [Error::WouldBlock] if no data is available
fn read_nonblocking(&'static self, buf: &mut [u8]) -> Result<usize, Error> {
Err(Error::WouldBlock)
}
/// Writes data to the device, returns [Error::WouldBlock] if data cannot yet be written
fn write_nonblocking(&'static self, buf: &[u8]) -> Result<usize, Error> {
Err(Error::WouldBlock)
}
/// Returns `true` if the device can be read from
fn is_readable(&self) -> bool {
true
}
/// Returns `true` if the device can be written to
fn is_writable(&self) -> bool {
true
}
/// Returns `true` if the given device is a terminal
fn is_terminal(&self) -> bool {
false
}
/// Performs a device-specific function
fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> {
Err(Error::NotImplemented)
}
}
#[derive(Clone)]
pub(crate) struct BlockDeviceWrapper(pub(crate) &'static dyn BlockDevice);
#[derive(Clone)]
pub(crate) struct CharDeviceWrapper(pub(crate) &'static dyn CharDevice);
impl BlockDeviceWrapper {
pub fn is_readable(&self) -> bool {
self.0.is_readable()
}
pub fn is_writable(&self) -> bool {
self.0.is_writable()
}
}
impl CommonImpl for BlockDeviceWrapper {
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
self.0.size()
}
}
impl CharDeviceWrapper {
pub fn is_terminal(&self) -> bool {
self.0.is_terminal()
}
pub fn is_readable(&self) -> bool {
self.0.is_readable()
}
pub fn is_writable(&self) -> bool {
self.0.is_writable()
}
}
impl CommonImpl for CharDeviceWrapper {
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
Ok(0)
}
}

@ -3,13 +3,18 @@ use core::sync::atomic::{AtomicBool, Ordering};
use libk_util::sync::IrqSafeSpinlock;
use yggdrasil_abi::{error::Error, io::SeekFrom};
use crate::vfs::{
device::{BlockDeviceWrapper, CharDeviceWrapper},
node::NodeRef,
use crate::{
device::{block::BlockDeviceFile, char::CharDeviceFile},
vfs::{CommonImpl, NodeRef},
};
// use crate::vfs::{
// device::{BlockDeviceWrapper, CharDeviceWrapper},
// node::NodeRef,
// };
pub struct BlockFile {
pub(super) device: BlockDeviceWrapper,
pub(super) device: BlockDeviceFile,
pub(super) node: NodeRef,
pub(super) position: IrqSafeSpinlock<u64>,
pub(super) read: bool,
@ -17,7 +22,7 @@ pub struct BlockFile {
}
pub struct CharFile {
pub(super) device: CharDeviceWrapper,
pub(super) device: CharDeviceFile,
pub(super) node: NodeRef,
pub(super) read: bool,
pub(super) write: bool,
@ -31,7 +36,7 @@ impl BlockFile {
async fn read_async(&self, buf: &mut [u8]) -> Result<usize, Error> {
let mut position = self.position.lock();
let count = self.device.0.read(*position, buf).await?;
let count = self.device.read(*position, buf).await?;
*position += count as u64;
Ok(count)
}
@ -45,10 +50,6 @@ impl BlockFile {
pub fn write(&self, buf: &[u8]) -> Result<usize, Error> {
block!(self.write_async(buf).await)?
// let mut position = self.position.lock();
// let count = self.device.0.write(*position, buf)?;
// *position += count as u64;
// Ok(count)
}
pub fn seek(&self, from: SeekFrom) -> Result<u64, Error> {
@ -64,7 +65,7 @@ impl BlockFile {
}
SeekFrom::Start(pos) => pos,
SeekFrom::End(off) => {
let size = i64::try_from(self.device.0.size()?).unwrap();
let size = i64::try_from(CommonImpl::size(&self.device, &self.node)?).unwrap();
let newpos = size + off;
if newpos < 0 {

@ -12,6 +12,7 @@ use alloc::{
sync::Arc,
};
use async_trait::async_trait;
use device::{BlockFile, CharFile};
use libk_mm::{address::PhysicalAddress, table::MapAttributes, PageProvider};
use libk_util::sync::IrqSafeSpinlock;
use yggdrasil_abi::{
@ -24,25 +25,35 @@ use yggdrasil_abi::{
};
use crate::{
device::{block::BlockDeviceFile, char::CharDeviceFile},
task::process::Process,
vfs::{
channel::ChannelDescriptor,
device::{BlockDeviceWrapper, CharDeviceWrapper},
// device::{BlockDeviceWrapper, CharDeviceWrapper},
node::NodeRef,
traits::{Read, Seek, Write},
FdPoll, FileReadiness, Node, PseudoTerminalMaster, PseudoTerminalSlave, SharedMemory,
FdPoll,
FileReadiness,
Node,
// PseudoTerminalMaster,
// PseudoTerminalSlave,
SharedMemory,
TimerFile,
},
};
use self::{
device::{BlockFile, CharFile},
// device::{BlockFile, CharFile},
directory::DirectoryFile,
pipe::PipeEnd,
regular::RegularFile,
};
use super::{pid::PidFile, pty, socket::SocketWrapper};
use super::{
pid::PidFile,
pty::{self, PseudoTerminalMaster, PseudoTerminalSlave},
socket::SocketWrapper,
};
mod device;
mod directory;
@ -205,7 +216,7 @@ impl File {
}
pub(crate) fn block(
device: BlockDeviceWrapper,
device: BlockDeviceFile,
node: NodeRef,
opts: OpenOptions,
) -> Result<Arc<Self>, Error> {
@ -215,7 +226,7 @@ impl File {
if read && !device.is_readable() {
return Err(Error::InvalidOperation);
}
if write && !device.is_writable() {
if write && !device.is_writeable() {
return Err(Error::ReadOnly);
}
@ -229,7 +240,7 @@ impl File {
}
pub(crate) fn char(
device: CharDeviceWrapper,
device: CharDeviceFile,
node: NodeRef,
opts: OpenOptions,
) -> Result<Arc<Self>, Error> {
@ -239,7 +250,7 @@ impl File {
if read && !device.is_readable() {
return Err(Error::InvalidOperation);
}
if write && !device.is_writable() {
if write && !device.is_writeable() {
return Err(Error::ReadOnly);
}
@ -263,6 +274,7 @@ impl File {
Self::PtySlave(half) => Ok(Arc::new(Self::PtySlave(half.clone()))),
Self::PtyMaster(half) => Ok(Arc::new(Self::PtyMaster(half.clone()))),
_ => {
log::info!("Invalid file send(): {:?}", self);
Err(Error::InvalidOperation)
@ -294,7 +306,7 @@ impl File {
/// Polls a file for "read-readiness"
pub fn poll_read(&self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
match self {
Self::Char(f) => f.device.0.poll_read(cx),
Self::Char(f) => f.device.poll_read(cx),
Self::Channel(ch) => ch.poll_read(cx),
Self::Poll(ch) => ch.poll_read(cx),
Self::PtyMaster(half) => half.half.poll_read(cx),
@ -311,8 +323,8 @@ impl File {
/// Performs a device-specific request
pub fn device_request(&self, req: &mut DeviceRequest) -> Result<(), Error> {
match self {
Self::Char(f) => f.device.0.device_request(req),
Self::Block(f) => f.device.0.device_request(req),
Self::Char(f) => f.device.device_request(req),
Self::Block(f) => f.device.device_request(req),
Self::PtySlave(half) => half.half.device_request(req),
Self::PtyMaster(half) => half.half.device_request(req),
_ => Err(Error::InvalidOperation),
@ -349,7 +361,7 @@ impl File {
impl PageProvider for File {
fn get_page(&self, offset: u64) -> Result<PhysicalAddress, Error> {
match self {
Self::Block(f) => f.device.0.get_page(offset),
Self::Block(f) => f.device.get_page(offset),
Self::SharedMemory(f) => f.get_page(offset),
_ => Err(Error::InvalidOperation),
}
@ -357,7 +369,7 @@ impl PageProvider for File {
fn release_page(&self, offset: u64, phys: PhysicalAddress) -> Result<(), Error> {
match self {
Self::Block(f) => f.device.0.release_page(offset, phys),
Self::Block(f) => f.device.release_page(offset, phys),
Self::SharedMemory(f) => f.release_page(offset, phys),
_ => Err(Error::InvalidOperation),
}

@ -2,12 +2,10 @@
extern crate alloc;
pub mod block;
pub mod pty;
pub(crate) mod asyncio;
pub(crate) mod channel;
pub(crate) mod device;
pub(crate) mod file;
pub(crate) mod filesystem;
pub(crate) mod ioctx;
@ -22,7 +20,7 @@ pub(crate) mod timer;
pub(crate) mod traits;
pub use channel::{Channel, ChannelDescriptor, Message, MessagePayload, Subscription};
pub use device::CharDevice;
// pub use device::CharDevice;
pub use file::{DirectoryOpenPosition, File, FileRef, FileSet, InstanceData};
pub use filesystem::{
parse_mount_options, register_root, roots as filesystem_roots, Filesystem,

@ -24,13 +24,15 @@ mod tree;
pub use access::AccessToken;
pub use traits::{CommonImpl, DirectoryImpl, RegularImpl, SymlinkImpl};
use crate::vfs::{
block::BlockDevice,
device::{BlockDeviceWrapper, CharDevice, CharDeviceWrapper},
PseudoTerminalMaster, PseudoTerminalSlave,
use crate::device::{
block::{BlockDevice, BlockDeviceFile},
char::{CharDevice, CharDeviceFile},
};
use super::Filesystem;
use super::{
pty::{PseudoTerminalMaster, PseudoTerminalSlave},
Filesystem,
};
/// Wrapper type for a [Node] shared reference
pub type NodeRef = Arc<Node>;
@ -77,10 +79,9 @@ pub(crate) struct SymlinkData {
enum NodeImpl {
Regular(Box<dyn RegularImpl>),
Directory(DirectoryData),
Block(BlockDeviceWrapper),
Char(CharDeviceWrapper),
Block(BlockDeviceFile),
Char(CharDeviceFile),
Symlink(SymlinkData),
// These map transparently to other types of nodes
// TODO: implement open operation on these
#[allow(unused)]
@ -194,18 +195,18 @@ impl Node {
}
/// Creates a new block device node with given [BlockDevice]
pub fn block(device: &'static dyn BlockDevice, flags: NodeFlags) -> NodeRef {
pub fn block(device: Arc<dyn BlockDevice>, flags: NodeFlags) -> NodeRef {
Self::new(
NodeImpl::Block(BlockDeviceWrapper(device)),
NodeImpl::Block(BlockDeviceFile(device)),
flags,
Metadata::default_file(),
)
}
/// Creates a new character device node with given [CharDevice]
pub fn char(device: &'static dyn CharDevice, flags: NodeFlags) -> NodeRef {
pub fn char(device: Arc<dyn CharDevice>, flags: NodeFlags) -> NodeRef {
Self::new(
NodeImpl::Char(CharDeviceWrapper(device)),
NodeImpl::Char(CharDeviceFile(device)),
flags,
Metadata::default_file(),
)
@ -277,10 +278,11 @@ impl Node {
/// Returns `true` if the node represents a character device and the character device is a
/// terminal
pub fn is_terminal(&self) -> bool {
if let NodeImpl::Char(dev) = &self.data {
dev.is_terminal()
} else {
false
match &self.data {
NodeImpl::Char(dev) => dev.is_terminal(),
NodeImpl::PseudoTerminalSlave(_) => true,
NodeImpl::PseudoTerminalMaster(_) => true,
_ => false,
}
}
@ -358,9 +360,9 @@ impl Node {
symlink.imp.read_to_string()
}
pub fn as_block_device(&self) -> Result<&'static dyn BlockDevice, Error> {
pub fn as_block_device(&self) -> Result<&Arc<dyn BlockDevice>, Error> {
match &self.data {
NodeImpl::Block(dev) => Ok(dev.0),
NodeImpl::Block(dev) => Ok(&dev.0),
_ => Err(Error::InvalidFile),
}
}

@ -15,8 +15,8 @@ impl Node {
/// Performs a device-specific function on a the device node
pub fn device_request(self: &NodeRef, req: &mut DeviceRequest) -> Result<(), Error> {
match &self.data {
NodeImpl::Block(dev) => dev.0.device_request(req),
NodeImpl::Char(dev) => dev.0.device_request(req),
NodeImpl::Block(dev) => dev.device_request(req),
NodeImpl::Char(dev) => dev.device_request(req),
_ => Err(Error::InvalidOperation),
}
}

@ -4,8 +4,9 @@ use core::{
task::{Context, Poll},
};
use alloc::boxed::Box;
use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait;
use device_api::device::Device;
use libk_util::{
ring::{LossyRingQueue, RingBuffer},
sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock},
@ -19,9 +20,9 @@ use yggdrasil_abi::{
process::{ProcessGroupId, Signal},
};
use crate::task::process::Process;
use crate::{device::char::CharDevice, task::process::Process};
use super::{CharDevice, FileReadiness};
use super::FileReadiness;
pub struct Terminal<O: TerminalOutput> {
input: TerminalInput,
@ -37,7 +38,7 @@ pub struct TerminalInput {
}
// Program -> screen, PTY master, terminal, etc.
pub trait TerminalOutput: Sync {
pub trait TerminalOutput: Sync + Send {
fn write(&self, byte: u8) -> Result<(), Error>;
fn notify_readers(&self) {}
@ -205,9 +206,9 @@ impl<O: TerminalOutput> Terminal<O> {
}
}
pub async fn consume_keyboard<D: CharDevice>(
&'static self,
keyboard: &'static D,
pub async fn consume_keyboard(
self: Arc<Self>,
keyboard: Arc<dyn CharDevice>,
stop: Option<&AtomicBool>,
) {
let mut buf = [0; 4];
@ -350,19 +351,19 @@ impl InputBuffer {
#[async_trait]
impl<O: TerminalOutput> CharDevice for Terminal<O> {
async fn write(&'static self, buf: &[u8]) -> Result<usize, Error> {
async fn write(&self, buf: &[u8]) -> Result<usize, Error> {
self.write_to_output(buf)
}
fn write_nonblocking(&'static self, buf: &[u8]) -> Result<usize, Error> {
fn write_nonblocking(&self, buf: &[u8]) -> Result<usize, Error> {
self.write_to_output(buf)
}
async fn read(&'static self, buf: &mut [u8]) -> Result<usize, Error> {
async fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {
self.read_from_input(buf).await
}
fn read_nonblocking(&'static self, buf: &mut [u8]) -> Result<usize, Error> {
fn read_nonblocking(&self, buf: &mut [u8]) -> Result<usize, Error> {
self.read_from_input_nonblocking(buf)
}
@ -370,7 +371,7 @@ impl<O: TerminalOutput> CharDevice for Terminal<O> {
true
}
fn is_writable(&self) -> bool {
fn is_writeable(&self) -> bool {
true
}
@ -389,6 +390,12 @@ impl<O: TerminalOutput> FileReadiness for Terminal<O> {
}
}
impl<O: TerminalOutput> Device for Terminal<O> {
fn display_name(&self) -> &str {
"Terminal"
}
}
pub fn read_all(source: &mut RingBuffer<u8>, target: &mut [u8], eof: Option<u8>) -> usize {
let mut pos = 0;
while pos < target.len()

@ -320,6 +320,7 @@ dependencies = [
"abi-lib",
"async-trait",
"atomic_enum",
"bitflags",
"bytemuck",
"cfg-if",
"crossbeam-queue",
@ -327,7 +328,6 @@ dependencies = [
"elf",
"futures-util",
"kernel-arch",
"libk-device",
"libk-mm",
"libk-util",
"log",
@ -339,16 +339,6 @@ dependencies = [
"yggdrasil-abi",
]
[[package]]
name = "libk-device"
version = "0.1.0"
dependencies = [
"device-api",
"kernel-arch",
"libk-util",
"yggdrasil-abi",
]
[[package]]
name = "libk-mm"
version = "0.1.0"

@ -7,8 +7,7 @@ use aarch64_cpu::{
};
use kernel_arch::{absolute_address, Architecture, ArchitectureImpl};
use kernel_arch_aarch64::CPU_COUNT;
use kernel_fs::devfs;
use libk::task::runtime;
use libk::{devfs, task::runtime};
use libk_mm::{
address::{PhysicalAddress, Virtualize},
phys,

@ -251,7 +251,9 @@ fn el0_sync_inner(frame: &mut ExceptionFrame) {
}
fn irq_common() {
external_interrupt_controller().handle_pending_irqs();
external_interrupt_controller()
.unwrap()
.handle_pending_irqs();
}
global_asm!(include_str!("vectors.S"));

@ -4,14 +4,14 @@ use core::sync::atomic::Ordering;
use aarch64_cpu::asm::barrier;
use abi::error::Error;
use alloc::{boxed::Box, sync::Arc};
use alloc::sync::Arc;
use device_api::{
device::Device,
interrupt::{
ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable,
IpiDeliveryTarget, IpiMessage, Irq, IrqOptions, LocalInterruptController,
MessageInterruptController, MsiInfo,
},
Device,
};
use device_tree::{device_tree_driver, dt::DevTreeIndexPropExt};
use kernel_arch_aarch64::{GicInterface, CPU_COUNT};
@ -20,7 +20,7 @@ use libk_mm::{
address::PhysicalAddress,
device::{DeviceMemoryIo, RawDeviceMemoryMapping},
};
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
use self::{gicc::Gicc, gicd::Gicd};
@ -38,7 +38,7 @@ pub struct Gic {
gicd: OneTimeInit<Gicd>,
gicd_base: PhysicalAddress,
gicc_base: PhysicalAddress,
table: IrqSafeSpinlock<FixedInterruptTable<MAX_IRQ>>,
table: IrqSafeRwLock<FixedInterruptTable<MAX_IRQ>>,
}
/// Per-CPU GIC information
@ -47,11 +47,11 @@ pub struct GicPerCpu {}
impl GicInterface for Gic {}
impl Device for Gic {
fn display_name(&self) -> &'static str {
fn display_name(&self) -> &str {
"ARM Generic Interrupt Controller v2"
}
unsafe fn init(&'static self) -> Result<(), Error> {
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
let gicd_mmio = Arc::new(RawDeviceMemoryMapping::map(
self.gicd_base.into_u64(),
0x1000,
@ -70,8 +70,8 @@ impl Device for Gic {
self.gicd.init(gicd);
self.gicc.init(gicc);
register_external_interrupt_controller(self);
AArch64::set_gic(self);
register_external_interrupt_controller(self.clone());
AArch64::set_gic(self.clone());
Ok(())
}
@ -82,9 +82,9 @@ impl ExternalInterruptController for Gic {
&self,
irq: Irq,
options: IrqOptions,
handler: &'static dyn InterruptHandler,
handler: Arc<dyn InterruptHandler>,
) -> Result<(), Error> {
let mut table = self.table.lock();
let mut table = self.table.write();
let gicd = self.gicd.get();
let index = match irq {
@ -133,14 +133,16 @@ impl ExternalInterruptController for Gic {
gicc.clear_irq(irq_number);
{
let table = self.table.lock();
match table.handler(irq_number) {
Some(handler) => {
drop(table);
handler.handle_irq(None);
let table = self.table.read();
let entry = match table.handler(irq_number) {
Some(handler) => handler.clone(),
None => {
log::warn!("No handler for irq{}", irq_number);
return;
}
None => log::warn!("No handler for irq{}", irq_number),
}
};
entry.handle_irq(None);
}
}
}
@ -153,7 +155,7 @@ impl MessageInterruptController for Gic {
fn register_msi_range(
&self,
_range: &mut [MsiInfo],
_handler: &'static dyn InterruptHandler,
_handler: Arc<dyn InterruptHandler>,
) -> Result<(), Error> {
unimplemented!()
}
@ -203,7 +205,7 @@ impl Gic {
gicd: OneTimeInit::new(),
gicd_base,
gicc_base,
table: IrqSafeSpinlock::new(FixedInterruptTable::new()),
table: IrqSafeRwLock::new(FixedInterruptTable::new()),
}
}
}
@ -223,7 +225,7 @@ device_tree_driver! {
let (gicc_base, _) = reg.cell2_array_item(0, dt.address_cells, dt.size_cells)?;
let (gicd_base, _) = reg.cell2_array_item(1, dt.address_cells, dt.size_cells)?;
Some(Box::new(unsafe { Gic::new(
Some(Arc::new(unsafe { Gic::new(
PhysicalAddress::from_u64(gicc_base),
PhysicalAddress::from_u64(gicd_base),
)}))

@ -4,6 +4,7 @@ use core::sync::atomic::Ordering;
use aarch64_cpu::registers::{CNTP_CTL_EL0, CNTP_TVAL_EL0};
use abi::error::Error;
use alloc::sync::Arc;
use device_api::{
interrupt::{Irq, LocalInterruptController},
ResetDevice,
@ -17,10 +18,10 @@ use kernel_arch_aarch64::{
},
ArchitectureImpl, PerCpuData,
};
use kernel_fs::devfs::{self, BlockDeviceType};
use libk::{
arch::Cpu,
device::{display::set_display_registration_callback, external_interrupt_controller},
debug,
device::{external_interrupt_controller, manager::DEVICE_REGISTRY},
};
use libk_mm::{
address::PhysicalAddress,
@ -34,8 +35,7 @@ use tock_registers::interfaces::Writeable;
use ygg_driver_pci::PciBusManager;
use crate::{
debug,
device::{self, power::arm_psci::Psci},
device::power::arm_psci::Psci,
fs::{Initrd, INITRD_DATA},
};
@ -62,9 +62,8 @@ pub struct AArch64 {
dt: OneTimeInit<DeviceTree<'static>>,
/// Optional instance of PSCI on this platform
pub psci: OneTimeInit<&'static Psci>,
reset: OneTimeInit<&'static dyn ResetDevice>,
pub psci: OneTimeInit<Arc<Psci>>,
reset: OneTimeInit<Arc<dyn ResetDevice>>,
initrd: OneTimeInit<PhysicalRef<'static, [u8]>>,
}
@ -90,7 +89,7 @@ impl Platform for AArch64 {
}
}
fn register_reset_device(&self, reset: &'static dyn ResetDevice) -> Result<(), Error> {
fn register_reset_device(&self, reset: Arc<dyn ResetDevice>) -> Result<(), Error> {
self.reset.init(reset);
Ok(())
}
@ -105,10 +104,10 @@ impl Platform for AArch64 {
}
}
static GIC: OneTimeInit<&'static Gic> = OneTimeInit::new();
static GIC: OneTimeInit<Arc<Gic>> = OneTimeInit::new();
impl AArch64 {
fn set_gic(gic: &'static Gic) {
fn set_gic(gic: Arc<Gic>) {
GIC.init(gic);
}
@ -273,20 +272,14 @@ impl AArch64 {
node,
};
if let Some((device, _)) =
device_tree::driver::probe_dt_node(&probe, device::register_device)
{
device.init()?;
if let Some(device) = device_tree::driver::probe_dt_node(&probe) {
device.clone().init()?;
DEVICE_REGISTRY.add_pending_initialization(device, true);
}
}
debug::init();
set_display_registration_callback(|device| {
log::info!("Display registered: {:?}", device.display_name());
devfs::add_block_device(device, BlockDeviceType::Framebuffer).ok();
});
log::info!(
"Yggdrasil v{} ({})",
env!("CARGO_PKG_VERSION"),
@ -304,10 +297,8 @@ impl AArch64 {
return Ok(());
}
if let Some((device, _)) =
device_tree::driver::probe_dt_node(&probe, device::register_device)
{
device.init()?;
if let Some(device) = device_tree::driver::probe_dt_node(&probe) {
DEVICE_REGISTRY.add_pending_initialization(device, false);
}
Ok(())
@ -319,21 +310,7 @@ impl AArch64 {
);
}
// Initialize IRQs for the devices
device::manager_lock().devices().for_each(|dev| unsafe {
if let Err(error) = dev.init_irq() {
log::warn!(
"Could not init IRQs for {:?}: {:?}",
dev.display_name(),
error
);
}
});
log::info!("Enumerated devices:");
device::manager_lock().devices().for_each(|dev| {
log::info!("* {:?}", dev.display_name());
});
DEVICE_REGISTRY.run_initialization();
PciBusManager::setup_bus_devices()?;
} else {
@ -349,13 +326,14 @@ impl AArch64 {
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::CLEAR);
CNTP_TVAL_EL0.set(10000000);
external_interrupt_controller()
.unwrap()
.enable_irq(Irq::Private(14))
.unwrap();
}
if let Some(gic) = GIC.try_get() {
let cpu_data = ArchitectureImpl::local_cpu_data().unwrap();
cpu_data.gic.init(*gic);
cpu_data.gic.init(gic.clone());
}
Ok(())

@ -54,6 +54,7 @@ fn enumerate_cpus<'a>(dt: &'a DeviceTree) -> impl Iterator<Item = CpuInfo<'a>> {
impl CpuEnableMethod {
unsafe fn start_cpu(&self, id: usize, ip: usize, sp: usize) -> Result<(), Error> {
log::info!("Start CPU #{id}");
match self {
Self::Psci => {
let psci = PLATFORM.psci.try_get().ok_or_else(|| {

@ -1,22 +1,15 @@
//! AArch64 Generic Timer
use core::time::Duration;
use aarch64_cpu::registers::{CNTFRQ_EL0, CNTPCT_EL0, CNTP_CTL_EL0, CNTP_TVAL_EL0};
use abi::error::Error;
use alloc::boxed::Box;
use alloc::sync::Arc;
use device_api::{
device::Device,
interrupt::{InterruptHandler, Irq},
timer::MonotonicTimestampProviderDevice,
Device,
};
use device_tree::device_tree_driver;
use kernel_arch::task::Scheduler;
use libk::{
arch::Cpu,
device::{external_interrupt_controller, register_monotonic_timestamp_provider},
task::runtime,
};
use libk::{arch::Cpu, device::external_interrupt_controller, task::runtime, time};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
/// ARM Generic Timer driver
@ -28,11 +21,14 @@ pub struct ArmTimer {
pub const TICK_INTERVAL: u64 = 250000;
impl InterruptHandler for ArmTimer {
fn handle_irq(&self, _vector: Option<usize>) -> bool {
CNTP_TVAL_EL0.set(TICK_INTERVAL);
let now = self.monotonic_timestamp().unwrap();
fn handle_irq(self: Arc<Self>, _vector: Option<usize>) -> bool {
let count = CNTPCT_EL0.get() * 1_000;
let freq = CNTFRQ_EL0.get();
runtime::tick(now);
CNTP_TVAL_EL0.set(TICK_INTERVAL);
time::set_ticks(count / freq);
runtime::tick();
unsafe {
Cpu::local().scheduler().yield_cpu();
@ -40,32 +36,26 @@ impl InterruptHandler for ArmTimer {
true
}
}
impl MonotonicTimestampProviderDevice for ArmTimer {
fn monotonic_timestamp(&self) -> Result<Duration, Error> {
let count = CNTPCT_EL0.get() * 1_000_000;
let freq = CNTFRQ_EL0.get();
Ok(Duration::from_nanos((count / freq) * 1_000))
fn display_name(&self) -> &str {
"ARM Generic Timer"
}
}
impl Device for ArmTimer {
fn display_name(&self) -> &'static str {
fn display_name(&self) -> &str {
"ARM Generic Timer"
}
unsafe fn init(&'static self) -> Result<(), Error> {
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET);
register_monotonic_timestamp_provider(self);
Ok(())
}
unsafe fn init_irq(&'static self) -> Result<(), Error> {
let intc = external_interrupt_controller();
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
let intc = external_interrupt_controller()?;
intc.register_irq(self.irq, Default::default(), self)?;
intc.register_irq(self.irq, Default::default(), self.clone())?;
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::IMASK::CLEAR);
CNTP_TVAL_EL0.set(TICK_INTERVAL);
@ -91,6 +81,6 @@ device_tree_driver! {
compatible: ["arm,armv8-timer"],
probe(_dt) => {
// TODO actually get info from the dt
Some(Box::new(unsafe { ArmTimer::new(Irq::Private(14)) }))
Some(Arc::new(unsafe { ArmTimer::new(Irq::Private(14)) }))
}
}

@ -2,31 +2,37 @@
use core::sync::atomic::{AtomicBool, Ordering};
use abi::error::Error;
use alloc::sync::Arc;
use boot::multiboot::MultibootInfo;
use device_api::{
interrupt::{IpiDeliveryTarget, IpiMessage, Irq},
Device, ResetDevice,
ResetDevice,
};
use git_version::git_version;
use kernel_arch::{Architecture, ArchitectureImpl};
use kernel_arch_i686::{gdt, mem::table::L3, PerCpuData};
use kernel_arch_x86::cpuid::{self, CpuFeatures, EcxFeatures, EdxFeatures};
use kernel_fs::devfs::{self, BlockDeviceType, CharDeviceType};
use libk::{
arch::Cpu,
device::display::{set_display_registration_callback, DriverFlags},
debug::{self, LogLevel},
devfs,
device::{
display::{
console::{add_console_autoflush, ConsoleWrapper},
DriverFlags,
},
manager::DEVICE_REGISTRY,
},
task::runtime,
vfs::{Terminal, TerminalInput},
};
use libk_device::{register_external_interrupt_controller, register_monotonic_timestamp_provider};
use libk_mm::{
address::{PhysicalAddress, Virtualize},
phys::{self, reserved::reserve_region, PhysicalMemoryRegion},
table::EntryLevelExt,
};
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use libk_util::sync::IrqSafeSpinlock;
use peripherals::textfb::TextFramebuffer;
use ygg_driver_input::KEYBOARD_DEVICE;
use ygg_driver_pci::{LegacyPciAccess, PciBusManager};
mod boot;
@ -36,12 +42,8 @@ mod peripherals;
use crate::{
arch::x86::{
intrinsics::IoPortAccess,
peripherals::{i8253::I8253, ps2::PS2},
},
debug::{self, LogLevel},
device::{
self,
display::console::{add_console_autoflush, ConsoleWrapper},
peripherals::{i8253::I8253, ps2::PS2Controller},
ISA_IRQ_OFFSET,
},
fs::{Initrd, INITRD_DATA},
};
@ -63,11 +65,7 @@ struct LegacyPci {
inner: IrqSafeSpinlock<LegacyPciInner>,
}
pub struct I686 {
com1_3: OneTimeInit<ComPort>,
textfb: OneTimeInit<TextFramebuffer>,
textfb_console: OneTimeInit<Terminal<ConsoleWrapper<'static>>>,
}
pub struct I686;
static SWITCH_TO_GRAPHIC: AtomicBool = AtomicBool::new(false);
@ -78,11 +76,7 @@ static PCI: LegacyPci = LegacyPci {
}),
};
pub static PLATFORM: I686 = I686 {
com1_3: OneTimeInit::new(),
textfb: OneTimeInit::new(),
textfb_console: OneTimeInit::new(),
};
pub static PLATFORM: I686 = I686;
impl Platform for I686 {
const KERNEL_VIRT_OFFSET: usize = 0xC0000000;
@ -100,7 +94,7 @@ impl Platform for I686 {
// No APs in i686, go get a better CPU
}
fn register_reset_device(&self, _reset: &'static dyn ResetDevice) -> Result<(), Error> {
fn register_reset_device(&self, _reset: Arc<dyn ResetDevice>) -> Result<(), Error> {
unimplemented!()
}
}
@ -132,12 +126,6 @@ impl I686 {
}
unsafe fn init_platform(&'static self, multiboot_info: &MultibootInfo) -> Result<(), Error> {
// Init serial output
let com1_3 = self
.com1_3
.init(ComPort::new(0x3F8, 0x3E8, Irq::External(4)));
debug::add_sink(com1_3.port_a(), LogLevel::Debug);
reserve_region(
"lowmem",
PhysicalMemoryRegion {
@ -172,6 +160,14 @@ impl I686 {
debug::init();
let com1_3 = Arc::new(ComPort::new(
0x3F8,
0x3E8,
Irq::External(ISA_IRQ_OFFSET + 4),
));
debug::add_sink(com1_3.port_a().clone(), LogLevel::Debug);
unsafe { init_gdt() };
unsafe { exception::init_exceptions() };
@ -227,7 +223,7 @@ impl I686 {
ygg_driver_virtio_net::probe,
);
set_display_registration_callback(|device| {
DEVICE_REGISTRY.display.set_callback(|device, _| {
log::info!("Display registered: {:?}", device.display_name());
if device
.driver_flags()
@ -235,36 +231,41 @@ impl I686 {
{
SWITCH_TO_GRAPHIC.store(true, Ordering::Release);
}
devfs::add_block_device(device, BlockDeviceType::Framebuffer).ok();
});
I8259.init().expect("Could not initialize i8259 PIC");
register_external_interrupt_controller(&I8259);
I8253.init().expect("Could not initialize i8253 Timer");
I8253.init_irq().expect("Could not setup timer IRQ");
register_monotonic_timestamp_provider(&I8253);
PS2.init()?;
PS2.init_irq()?;
let i8259 = I8259::setup().expect("Could not initialize i8259 PIC");
let i8253 = I8253::setup().expect("Could not initialize i8253 Timer");
i8259.set_i8253(i8253);
if let Err(error) = PS2Controller::setup() {
log::error!("Could not initialize PS/2 Controller: {error:?}");
}
// Setup text framebuffer
// TODO check if video mode is set from boot info
let textfb = TextFramebuffer::new(PhysicalAddress::from_u32(0xB8000), 80, 25)?;
let textfb = self.textfb.init(textfb);
debug::add_sink(textfb, LogLevel::Info);
add_console_autoflush(textfb);
let textfb = Arc::new(TextFramebuffer::new(
PhysicalAddress::from_u32(0xB8000),
80,
25,
)?);
debug::add_sink(textfb.clone(), LogLevel::Info);
add_console_autoflush(textfb.clone());
let textfb_console = Terminal::from_parts(
let textfb_console = Arc::new(Terminal::from_parts(
Default::default(),
TerminalInput::with_capacity(256).unwrap(),
ConsoleWrapper(textfb),
);
let textfb_console = self.textfb_console.init(textfb_console);
));
let keyboard_input = ygg_driver_input::setup();
runtime::spawn(textfb_console.consume_keyboard(&KEYBOARD_DEVICE, Some(&SWITCH_TO_GRAPHIC)))
.ok();
devfs::add_char_device(textfb_console, CharDeviceType::TtyRegular).ok();
device::register_device(&PS2);
runtime::spawn(
textfb_console
.clone()
.consume_keyboard(keyboard_input, Some(&SWITCH_TO_GRAPHIC)),
)
.ok();
DEVICE_REGISTRY.terminal.register(textfb_console).ok();
log::info!(
"Yggdrasil v{} ({})",

@ -1,17 +1,17 @@
use abi::error::Error;
use libk::{
debug::DebugSink,
device::display::console::{
Attributes, ColorAttribute, ConsoleBuffer, ConsoleChar, ConsoleState, DisplayConsole,
},
};
use libk_mm::{
address::PhysicalAddress,
device::{DeviceMemoryAttributes, DeviceMemoryIoMut},
};
use libk_util::sync::IrqSafeSpinlock;
use crate::{
arch::x86::intrinsics::{io_wait, IoPort, IoPortAccess},
debug::DebugSink,
device::display::console::{
Attributes, ColorAttribute, ConsoleBuffer, ConsoleChar, ConsoleState, DisplayConsole,
},
};
use crate::arch::x86::intrinsics::{io_wait, IoPort, IoPortAccess};
#[derive(Clone, Copy)]
#[repr(C)]
@ -152,3 +152,4 @@ fn convert_color(c: ColorAttribute) -> u8 {
}
unsafe impl Sync for TextFramebuffer {}
unsafe impl Send for TextFramebuffer {}

@ -2,10 +2,15 @@
use abi::error::Error;
use alloc::sync::Arc;
use device_api::{
interrupt::{IpiDeliveryTarget, IpiMessage},
ResetDevice,
};
// use device_api::{
// interrupt::{IpiDeliveryTarget, IpiMessage},
// ResetDevice,
// };
use kernel_arch::{Architecture, ArchitectureImpl};
use libk_mm::table::EntryLevel;
@ -54,7 +59,7 @@ pub trait Platform {
// Architecture intrinsics
/// Adds a reset device to the system
fn register_reset_device(&self, reset: &'static dyn ResetDevice) -> Result<(), Error> {
fn register_reset_device(&self, reset: Arc<dyn ResetDevice>) -> Result<(), Error> {
Err(Error::NotImplemented)
}

@ -1,21 +1,15 @@
use core::time::Duration;
use abi::error::Error;
use alloc::sync::Arc;
use device_api::{
device::Device,
interrupt::{InterruptHandler, Irq},
timer::MonotonicTimestampProviderDevice,
Device,
};
use kernel_arch::task::Scheduler;
use libk::{arch::Cpu, device::external_interrupt_controller, task::runtime};
use libk_util::sync::IrqSafeSpinlock;
use libk::{device::external_interrupt_controller, task::runtime, time};
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use crate::{
arch::x86::{
intrinsics::{IoPort, IoPortAccess},
ISA_IRQ_OFFSET,
},
device::timer::GLOBAL_TIME,
use crate::arch::x86::{
intrinsics::{IoPort, IoPortAccess},
ISA_IRQ_OFFSET,
};
const FREQUENCY: u32 = 1193180;
@ -24,8 +18,6 @@ const CMD_CH0: u8 = 0 << 6;
const CMD_ACC_WORD: u8 = 3 << 4;
const CMD_MODE_RATE: u8 = 2 << 1;
pub static I8253: I8253 = I8253::new();
struct Inner {
ch0_data: IoPort<u8>,
#[allow(unused)]
@ -33,33 +25,19 @@ struct Inner {
#[allow(unused)]
ch2_data: IoPort<u8>,
cmd: IoPort<u8>,
tick: u64,
}
pub struct I8253 {
inner: IrqSafeSpinlock<Inner>,
}
impl MonotonicTimestampProviderDevice for I8253 {
fn monotonic_timestamp(&self) -> Result<Duration, Error> {
let tick = self.inner.lock().tick;
Ok(Duration::from_millis(tick))
}
}
impl InterruptHandler for I8253 {
fn handle_irq(&self, _vector: Option<usize>) -> bool {
let mut inner = self.inner.lock();
inner.tick += 1;
let now = Duration::from_millis(inner.tick);
GLOBAL_TIME.add_time(Duration::from_millis(1));
drop(inner);
runtime::tick(now);
fn display_name(&self) -> &str {
"i8253 PIT"
}
fn handle_irq(self: Arc<Self>, _vector: Option<usize>) -> bool {
self.irq_handler_fastpath();
true
}
}
@ -69,8 +47,8 @@ impl Device for I8253 {
"i8253 PIT"
}
unsafe fn init_irq(&'static self) -> Result<(), Error> {
let intc = external_interrupt_controller();
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
let intc = external_interrupt_controller()?;
let inner = self.inner.lock();
let div: u16 = (FREQUENCY / 1000).try_into().unwrap();
@ -80,7 +58,7 @@ impl Device for I8253 {
inner.ch0_data.write((div >> 8) as u8);
let irq = Irq::External(ISA_IRQ_OFFSET);
intc.register_irq(irq, Default::default(), self)?;
intc.register_irq(irq, Default::default(), self.clone())?;
intc.enable_irq(irq)?;
Ok(())
@ -95,25 +73,33 @@ impl I8253 {
ch1_data: IoPort::new(0x41),
ch2_data: IoPort::new(0x42),
cmd: IoPort::new(0x43),
tick: 0,
}),
}
}
pub fn setup() -> Result<Arc<Self>, Error> {
let this = Arc::new(Self::new());
unsafe { this.clone().init_irq() }?;
INSTANCE.init(this.clone());
Ok(this)
}
pub fn irq_handler_fastpath(&self) {
let mut inner = self.inner.lock();
inner.tick += 1;
time::add_ticks(1);
runtime::tick();
let now = Duration::from_millis(inner.tick);
drop(inner);
#[cfg(any(target_arch = "x86", rust_analyzer))]
{
use kernel_arch::task::Scheduler;
use libk::arch::Cpu;
runtime::tick(now);
let cpu = Cpu::local();
let cpu = Cpu::local();
if let Some(queue) = cpu.try_get_scheduler() {
unsafe { queue.yield_cpu() };
if let Some(queue) = cpu.try_get_scheduler() {
unsafe { queue.yield_cpu() };
}
}
}
}
static INSTANCE: OneTimeInit<Arc<I8253>> = OneTimeInit::new();

@ -1,14 +1,16 @@
//! Intel 8259 interrupt controller
use abi::error::Error;
use alloc::sync::Arc;
use device_api::{
device::Device,
interrupt::{
ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable, Irq,
IrqOptions,
},
Device,
};
use libk_util::sync::IrqSafeSpinlock;
use libk::device::register_external_interrupt_controller;
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use crate::arch::x86::intrinsics::{IoPort, IoPortAccess};
@ -34,10 +36,11 @@ struct Inner {
pub struct I8259 {
inner: IrqSafeSpinlock<Inner>,
table: IrqSafeSpinlock<FixedInterruptTable<15>>,
i8253: OneTimeInit<Arc<I8253>>,
}
impl Device for I8259 {
unsafe fn init(&'static self) -> Result<(), Error> {
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
self.enable();
Ok(())
}
@ -66,7 +69,7 @@ impl ExternalInterruptController for I8259 {
&self,
irq: Irq,
_options: IrqOptions,
handler: &'static dyn InterruptHandler,
handler: Arc<dyn InterruptHandler>,
) -> Result<(), Error> {
let Irq::External(irq) = irq else {
return Err(Error::InvalidArgument);
@ -87,7 +90,7 @@ impl ExternalInterruptController for I8259 {
if index == 0 {
// Fastpath for timer
self.eoi(index);
I8253.irq_handler_fastpath();
self.i8253.get().clone().irq_handler_fastpath();
} else if index < 16 {
// Rest of IRQs
self.eoi(index);
@ -95,29 +98,45 @@ impl ExternalInterruptController for I8259 {
let table = self.table.lock();
if let Some(handler) = table.handler(index - 1) {
handler.handle_irq(None);
handler.clone().handle_irq(None);
}
}
}
}
impl I8259 {
const fn new() -> Self {
let inner = Inner {
const fn new_inner() -> Inner {
Inner {
master_cmd: IoPort::new(0x20),
master_data: IoPort::new(0x21),
slave_cmd: IoPort::new(0xA0),
slave_data: IoPort::new(0xA1),
};
Self {
inner: IrqSafeSpinlock::new(inner),
table: IrqSafeSpinlock::new(FixedInterruptTable::new()),
}
}
pub fn disable(&self) {
let inner = self.inner.lock();
const fn new() -> Self {
Self {
inner: IrqSafeSpinlock::new(Self::new_inner()),
table: IrqSafeSpinlock::new(FixedInterruptTable::new()),
i8253: OneTimeInit::new(),
}
}
pub fn setup() -> Result<Arc<Self>, Error> {
let this = Arc::new(Self::new());
unsafe { this.clone().init() }?;
register_external_interrupt_controller(this.clone());
I8259.init(this.clone());
Ok(this)
}
pub fn set_i8253(&self, i8253: Arc<I8253>) {
self.i8253.init(i8253);
}
pub fn disable() {
let inner = Self::new_inner();
log::info!("Disabling i8259 PIC");
// Remap PIC IRQ vectors to 32..
@ -171,7 +190,7 @@ impl I8259 {
}
}
pub static I8259: I8259 = I8259::new();
static I8259: OneTimeInit<Arc<I8259>> = OneTimeInit::new();
#[cfg(any(target_arch = "x86", rust_analyzer))]
pub fn setup_vectors(idt: &mut [exception::Entry]) {
@ -195,7 +214,7 @@ extern "C" fn irq_handler(frame: *mut InterruptFrame) {
let frame = unsafe { &mut *frame };
I8259.handle_specific_irq(frame.irq_number as _);
I8259.get().handle_specific_irq(frame.irq_number as _);
if let Some(thread) = Thread::get_current() {
unsafe { thread.handle_pending_signals(frame) };

@ -1,5 +1,5 @@
pub mod i8253;
pub mod i8259;
pub mod ps2;
pub mod rtc;
// pub mod rtc;
pub mod serial;

@ -3,9 +3,10 @@ use abi::{
error::Error,
io::{KeyboardKey, KeyboardKeyEvent},
};
use alloc::sync::Arc;
use device_api::{
device::Device,
interrupt::{InterruptHandler, Irq},
Device,
};
use libk::device::external_interrupt_controller;
use libk_util::sync::IrqSafeSpinlock;
@ -75,7 +76,7 @@ impl Inner {
}
impl InterruptHandler for PS2Controller {
fn handle_irq(&self, _vector: Option<usize>) -> bool {
fn handle_irq(self: Arc<Self>, _vector: Option<usize>) -> bool {
let mut count = 0;
let mut inner = self.inner.lock();
@ -105,6 +106,10 @@ impl InterruptHandler for PS2Controller {
count != 0
}
fn display_name(&self) -> &str {
"PS/2 Keyboard Interrupt"
}
}
impl Device for PS2Controller {
@ -112,16 +117,17 @@ impl Device for PS2Controller {
"PS/2 Controller"
}
unsafe fn init(&'static self) -> Result<(), Error> {
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
Ok(())
}
unsafe fn init_irq(&'static self) -> Result<(), Error> {
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
let intc = external_interrupt_controller()?;
let mut inner = self.inner.lock();
// let intc = PLATFORM.interrupt_controller();
let intc = external_interrupt_controller();
intc.register_irq(self.primary_irq, Default::default(), self)?;
intc.register_irq(self.primary_irq, Default::default(), self.clone())?;
// Disable PS/2 devices from sending any further data
inner.send_command(0xAD);
@ -156,11 +162,22 @@ impl PS2Controller {
inner: IrqSafeSpinlock::new(inner),
}
}
pub fn setup() -> Result<Arc<Self>, Error> {
let this = Arc::new(PS2Controller::new(
Irq::External(ISA_IRQ_OFFSET + 1),
Irq::External(ISA_IRQ_OFFSET + 12),
0x64,
0x60,
));
unsafe { this.clone().init_irq() }?;
Ok(this)
}
}
pub static PS2: PS2Controller = PS2Controller::new(
Irq::External(ISA_IRQ_OFFSET + 1),
Irq::External(ISA_IRQ_OFFSET + 12),
0x64,
0x60,
);
// pub static PS2: PS2Controller = PS2Controller::new(
// Irq::External(ISA_IRQ_OFFSET + 1),
// Irq::External(ISA_IRQ_OFFSET + 12),
// 0x64,
// 0x60,
// );

@ -1,12 +1,11 @@
//! Driver for x86 COM ports
use abi::error::Error;
use device_api::{interrupt::Irq, serial::SerialDevice, Device};
use alloc::sync::Arc;
use device_api::{device::Device, interrupt::Irq, serial::SerialDevice};
use libk::debug::DebugSink;
use libk_util::sync::IrqSafeSpinlock;
use crate::{
arch::x86::intrinsics::{IoPort, IoPortAccess},
debug::DebugSink,
};
use crate::arch::x86::intrinsics::{IoPort, IoPortAccess};
// Single port
struct Inner {
@ -22,14 +21,14 @@ pub struct Port {
/// COM port pair
#[allow(unused)]
pub struct ComPort {
port_a: Port,
port_b: Port,
port_a: Arc<Port>,
port_b: Arc<Port>,
irq: Irq,
}
impl DebugSink for Port {
fn putc(&self, c: u8) -> Result<(), Error> {
self.send(c)
self.send_byte(c)
}
fn supports_control_sequences(&self) -> bool {
@ -38,7 +37,7 @@ impl DebugSink for Port {
}
impl SerialDevice for Port {
fn send(&self, byte: u8) -> Result<(), Error> {
fn send_byte(&self, byte: u8) -> Result<(), Error> {
let inner = self.inner.lock();
while inner.lsr.read() & Self::LSR_THRE == 0 {
@ -48,6 +47,10 @@ impl SerialDevice for Port {
inner.dr.write(byte);
Ok(())
}
fn is_terminal(&self) -> bool {
false
}
}
impl Device for Port {
@ -55,7 +58,7 @@ impl Device for Port {
"COM port"
}
unsafe fn init(&'static self) -> Result<(), Error> {
unsafe fn init(self: Arc<Self>) -> Result<(), Error> {
Ok(())
}
}
@ -75,16 +78,16 @@ impl Port {
impl ComPort {
/// Constructs a COM port pair
pub const fn new(port_a: u16, port_b: u16, irq: Irq) -> Self {
pub fn new(port_a: u16, port_b: u16, irq: Irq) -> Self {
Self {
port_a: Port::new(port_a),
port_b: Port::new(port_b),
port_a: Arc::new(Port::new(port_a)),
port_b: Arc::new(Port::new(port_b)),
irq,
}
}
/// Returns a reference to the A port of this COM pair
pub fn port_a(&self) -> &Port {
pub fn port_a(&self) -> &Arc<Port> {
&self.port_a
}
}

Some files were not shown because too many files have changed in this diff Show More