dev: rework device management (&'static -> Arc)
This commit is contained in:
parent
18bfeaf917
commit
56fbcefa80
Cargo.lock
kernel
Cargo.toml
arch
aarch64/src
i686/src
interface/src
x86_64/src
driver
block
bus
fs
input
net
usb/xhci/src
virtio
lib
libk
Cargo.toml
libk-device
libk-util/src
src
modules/test_mod
src/arch
18
Cargo.lock
generated
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
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)
|
||||
}
|
650
kernel/libk/src/device/block/cache.rs
Normal file
650
kernel/libk/src/device/block/cache.rs
Normal file
@ -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");
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
222
kernel/libk/src/device/block/mod.rs
Normal file
222
kernel/libk/src/device/block/mod.rs
Normal file
@ -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()
|
||||
}
|
||||
}
|
94
kernel/libk/src/device/block/partition/gpt.rs
Normal file
94
kernel/libk/src/device/block/partition/gpt.rs
Normal file
@ -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))
|
||||
}
|
118
kernel/libk/src/device/block/partition/mod.rs
Normal file
118
kernel/libk/src/device/block/partition/mod.rs
Normal file
@ -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()
|
||||
}
|
||||
}
|
64
kernel/libk/src/device/char.rs
Normal file
64
kernel/libk/src/device/char.rs
Normal file
@ -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();
|
||||
}
|
72
kernel/libk/src/device/display/color.rs
Normal file
72
kernel/libk/src/device/display/color.rs
Normal file
@ -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);
|
||||
// }
|
||||
}
|
555
kernel/libk/src/device/display/console.rs
Normal file
555
kernel/libk/src/device/display/console.rs
Normal file
@ -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);
|
||||
// }
|
||||
}
|
225
kernel/libk/src/device/manager.rs
Normal file
225
kernel/libk/src/device/manager.rs
Normal file
@ -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
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()
|
||||
|
12
kernel/modules/test_mod/Cargo.lock
generated
12
kernel/modules/test_mod/Cargo.lock
generated
@ -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
Loading…
x
Reference in New Issue
Block a user