Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aba41cb5b2 | |||
| 475bf90eb9 | |||
| 33474c10d3 | |||
| e934b4d696 | |||
| 6d8d97d492 | |||
| ca01f57873 | |||
| 9be467d5d5 | |||
| 0c2ebbf7b3 | |||
| cb4c0bc4b0 | |||
| c1b62aef1d | |||
| 6469914be1 | |||
| 131e6adc3d | |||
| 322cb0a958 | |||
| 6552fa8059 | |||
| 0ff48fd520 | |||
| 9c32c11b0b | |||
| 3be32b7b8f |
Generated
+88
@@ -2638,6 +2638,70 @@ dependencies = [
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_bsp_arm"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"device-tree",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_bsp_bcm283x"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"device-tree",
|
||||
"futures-util",
|
||||
"kernel-arch-aarch64",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_bsp_jh7110"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"device-tree",
|
||||
"futures-util",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_bsp_riscv"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"device-tree",
|
||||
"kernel-arch-riscv64",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_fat32"
|
||||
version = "0.1.0"
|
||||
@@ -2808,6 +2872,25 @@ dependencies = [
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_usb_dwc2"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"atomic_enum",
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"device-tree",
|
||||
"futures-util",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"ygg_driver_usb",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_usb_xhci"
|
||||
version = "0.1.0"
|
||||
@@ -2951,6 +3034,10 @@ dependencies = [
|
||||
"yboot-proto",
|
||||
"ygg_driver_acpi",
|
||||
"ygg_driver_ahci",
|
||||
"ygg_driver_bsp_arm",
|
||||
"ygg_driver_bsp_bcm283x",
|
||||
"ygg_driver_bsp_jh7110",
|
||||
"ygg_driver_bsp_riscv",
|
||||
"ygg_driver_fat32",
|
||||
"ygg_driver_input",
|
||||
"ygg_driver_net_core",
|
||||
@@ -2961,6 +3048,7 @@ dependencies = [
|
||||
"ygg_driver_nvme",
|
||||
"ygg_driver_pci",
|
||||
"ygg_driver_usb",
|
||||
"ygg_driver_usb_dwc2",
|
||||
"ygg_driver_usb_xhci",
|
||||
"ygg_driver_virtio_blk",
|
||||
"ygg_driver_virtio_gpu",
|
||||
|
||||
@@ -7,6 +7,7 @@ exclude = [
|
||||
"toolchain",
|
||||
"userspace/dynload-program",
|
||||
"userspace/lib/ygglibc",
|
||||
"userspace",
|
||||
"toolchain-c"
|
||||
]
|
||||
members = [
|
||||
|
||||
Binary file not shown.
+513
-1326
File diff suppressed because it is too large
Load Diff
@@ -56,11 +56,17 @@ git-version = "0.3.9"
|
||||
aarch64-cpu.workspace = true
|
||||
device-tree.workspace = true
|
||||
kernel-arch-aarch64.workspace = true
|
||||
ygg_driver_bsp_arm.path = "driver/bsp/arm"
|
||||
ygg_driver_bsp_bcm283x.path = "driver/bsp/bcm283x"
|
||||
ygg_driver_usb_dwc2.path = "driver/usb/dwc2"
|
||||
|
||||
[target.'cfg(target_arch = "riscv64")'.dependencies]
|
||||
device-tree.workspace = true
|
||||
kernel-arch-riscv64.workspace = true
|
||||
ygg_driver_bsp_arm.path = "driver/bsp/arm" # PrimeCell components
|
||||
ygg_driver_bsp_riscv.path = "driver/bsp/riscv"
|
||||
ygg_driver_net_stmmac.path = "driver/net/stmmac"
|
||||
ygg_driver_bsp_jh7110.path = "driver/bsp/jh7110"
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||
yboot-proto.workspace = true
|
||||
@@ -87,7 +93,11 @@ kernel-arch-aarch64.workspace = true
|
||||
kernel-arch-riscv64.workspace = true
|
||||
|
||||
ygg_driver_acpi.path = "driver/acpi"
|
||||
ygg_driver_bsp_arm.path = "driver/bsp/arm"
|
||||
ygg_driver_bsp_riscv.path = "driver/bsp/riscv"
|
||||
ygg_driver_net_stmmac.path = "driver/net/stmmac"
|
||||
ygg_driver_bsp_bcm283x.path = "driver/bsp/bcm283x"
|
||||
ygg_driver_bsp_jh7110.path = "driver/bsp/jh7110"
|
||||
|
||||
[features]
|
||||
default = ["fb_console"]
|
||||
|
||||
@@ -44,6 +44,7 @@ pub struct PerCpuData {
|
||||
}
|
||||
|
||||
pub static CPU_COUNT: AtomicUsize = AtomicUsize::new(1);
|
||||
pub static mut BOOT_HART_ID: u64 = 0;
|
||||
static IPI_QUEUES: OneTimeInit<Vec<IpiQueue<ArchitectureImpl>>> = OneTimeInit::new();
|
||||
static HART_TO_QUEUE: IrqSafeSpinlock<ArchitectureImpl, BTreeMap<u32, usize>> =
|
||||
IrqSafeSpinlock::new(BTreeMap::new());
|
||||
@@ -60,6 +61,11 @@ impl CpuData for PerCpuData {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the ID of the bootstrap HART
|
||||
pub fn boot_hart_id() -> u64 {
|
||||
unsafe { BOOT_HART_ID }
|
||||
}
|
||||
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn idle_task(_: usize) -> ! {
|
||||
core::arch::naked_asm!("1: nop; j 1b");
|
||||
|
||||
@@ -4,6 +4,8 @@ const EXT_HSM: u64 = 0x48534D;
|
||||
const EXT_TIME: u64 = 0x54494D45;
|
||||
const EXT_DBCN: u64 = 0x4442434E;
|
||||
const EXT_SPI: u64 = 0x735049;
|
||||
const EXT_SYSTEM_SHUTDOWN: u64 = 0x53525354;
|
||||
const EXT_SYSTEM_SHUTDOWN_LEGACY: u64 = 0x08;
|
||||
|
||||
primitive_enum! {
|
||||
pub enum Status: i64 {
|
||||
@@ -108,3 +110,9 @@ pub fn sbi_debug_console_write_byte(byte: u8) {
|
||||
pub fn sbi_set_timer(next_event: u64) {
|
||||
unsafe { sbi_do_call(EXT_TIME, 0x00, next_event, 0, 0, 0, 0, 0) }.ok();
|
||||
}
|
||||
|
||||
pub fn sbi_system_shutdown() -> ! {
|
||||
unsafe { sbi_do_call(EXT_SYSTEM_SHUTDOWN, 0x00, 0, 0, 0, 0, 0, 0) }.ok();
|
||||
unsafe { sbi_do_call(EXT_SYSTEM_SHUTDOWN_LEGACY, 0x00, 0, 0, 0, 0, 0, 0) }.ok();
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#![feature(allocator_api)]
|
||||
#![feature(allocator_api, never_type)]
|
||||
#![no_std]
|
||||
|
||||
use acpi::AcpiTables;
|
||||
use acpi_system::{AcpiInterruptMethod, AcpiSystem};
|
||||
use acpi_system::{AcpiInterruptMethod, AcpiSleepState, AcpiSystem};
|
||||
use alloc::boxed::Box;
|
||||
use libk::error::Error;
|
||||
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
|
||||
@@ -45,6 +45,17 @@ pub fn get_pci_route(
|
||||
.ok()
|
||||
}
|
||||
|
||||
pub fn power_off() -> Result<!, Error> {
|
||||
let system = ACPI_SYSTEM.get();
|
||||
unsafe {
|
||||
system.lock().enter_sleep_state(AcpiSleepState::S5).ok();
|
||||
|
||||
loop {
|
||||
core::arch::asm!("cli; hlt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes ACPI management
|
||||
pub fn switch_to_acpi(tables: &'static AcpiTables<AcpiHandlerImpl>) -> Result<(), Error> {
|
||||
// NOTE mostly broken for real HW
|
||||
@@ -67,12 +78,6 @@ pub fn switch_to_acpi(tables: &'static AcpiTables<AcpiHandlerImpl>) -> Result<()
|
||||
// // 6. Do something with the devices
|
||||
// // 7. Actually enter the S5 state
|
||||
|
||||
// unsafe {
|
||||
// PLATFORM
|
||||
// .send_ipi(IpiDeliveryTarget::OtherCpus, IpiMessage::Shutdown)
|
||||
// .unwrap();
|
||||
// }
|
||||
|
||||
// SHUTDOWN_FENCE.signal();
|
||||
// SHUTDOWN_FENCE.wait_all(CPU_COUNT.load(Ordering::Acquire));
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "ygg_driver_bsp_arm"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
device-tree.workspace = true
|
||||
device-api.workspace = true
|
||||
yggdrasil-abi.workspace = true
|
||||
libk-mm.workspace = true
|
||||
libk-util.workspace = true
|
||||
libk.workspace = true
|
||||
|
||||
tock-registers.workspace = true
|
||||
log.workspace = true
|
||||
bytemuck.workspace = true
|
||||
@@ -0,0 +1,8 @@
|
||||
#![allow(unsafe_op_in_unsafe_fn)]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod pl011;
|
||||
mod pl031;
|
||||
mod pl061;
|
||||
@@ -1,14 +1,12 @@
|
||||
//! ARM PL011 driver
|
||||
use abi::{error::Error, io::TerminalOptions};
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{FullIrq, InterruptHandler, IrqVector},
|
||||
interrupt::{InterruptHandler, IrqHandle, IrqVector},
|
||||
};
|
||||
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
|
||||
use libk::{
|
||||
debug::DebugSink,
|
||||
device::{external_interrupt_controller, manager::DEVICE_REGISTRY},
|
||||
device::manager::DEVICE_REGISTRY,
|
||||
vfs::{Terminal, TerminalInput, TerminalOutput},
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
||||
@@ -18,6 +16,7 @@ use tock_registers::{
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
use yggdrasil_abi::{error::Error, io::TerminalOptions};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
@@ -69,7 +68,7 @@ struct Pl011Inner {
|
||||
pub struct Pl011 {
|
||||
inner: OneTimeInit<Arc<Terminal<Pl011Inner>>>,
|
||||
base: PhysicalAddress,
|
||||
irq: FullIrq,
|
||||
irq: IrqHandle,
|
||||
}
|
||||
|
||||
impl Io {
|
||||
@@ -165,14 +164,12 @@ impl Device for Pl011 {
|
||||
}
|
||||
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
let intc = external_interrupt_controller()?;
|
||||
|
||||
intc.register_irq(self.irq.irq, self.irq.options, self.clone())?;
|
||||
self.irq.register(self.clone())?;
|
||||
{
|
||||
let io = self.inner.get().output().io.lock();
|
||||
io.regs.IMSC.modify(IMSC::RXIM::SET);
|
||||
}
|
||||
intc.enable_irq(self.irq.irq)?;
|
||||
self.irq.enable()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
use alloc::sync::Arc;
|
||||
use device_api::device::{Device, DeviceInitContext};
|
||||
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
|
||||
use libk::{
|
||||
error::Error,
|
||||
fs::sysfs::{self, nodes::SysfsRtcNode},
|
||||
};
|
||||
use libk_mm::device::DeviceMemoryIo;
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
use tock_registers::{
|
||||
interfaces::Readable,
|
||||
register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
(0x00 => RTCDR: ReadOnly<u32>),
|
||||
(0x04 => RTCMR: ReadWrite<u32>),
|
||||
(0x08 => RTCLR: ReadWrite<u32>),
|
||||
(0x0C => RTCCR: ReadWrite<u32>),
|
||||
(0x10 => RTCIMSC: ReadWrite<u32>),
|
||||
(0x14 => RTCRIS: ReadOnly<u32>),
|
||||
(0x18 => RTCMIS: ReadOnly<u32>),
|
||||
(0x1C => RTCICR: WriteOnly<u32>),
|
||||
(0x20 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
struct Pl031 {
|
||||
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
|
||||
}
|
||||
|
||||
impl SysfsRtcNode for Pl031 {
|
||||
fn read(&self) -> Result<u64, Error> {
|
||||
let regs = self.regs.lock();
|
||||
Ok(regs.RTCDR.get() as _)
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Pl031 {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
sysfs::nodes::add_rtc_node(self.clone());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
"ARM PL031 RTC"
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["arm,pl031"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let base = node.map_base(context, 0)?;
|
||||
|
||||
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
|
||||
|
||||
let rtc = Arc::new(Pl031 { regs: IrqSafeSpinlock::new(regs) });
|
||||
|
||||
Some(rtc)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
use core::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
use abi::error::Error;
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use device_api::{
|
||||
clock::{ClockHandle, ResetHandle},
|
||||
@@ -9,7 +8,7 @@ use device_api::{
|
||||
GpioController, GpioInterruptEvent, GpioInterruptMode, GpioPinLevel, PinHandle,
|
||||
SinglePinDirection,
|
||||
},
|
||||
interrupt::{FullIrq, InterruptHandler, IrqVector},
|
||||
interrupt::{InterruptHandler, IrqHandle, IrqVector},
|
||||
};
|
||||
use device_tree::{
|
||||
driver::{
|
||||
@@ -18,7 +17,7 @@ use device_tree::{
|
||||
},
|
||||
DeviceTreePropertyRead, TProp,
|
||||
};
|
||||
use libk::{device::external_interrupt_controller, event::signal_gpio_event};
|
||||
use libk::event::signal_gpio_event;
|
||||
use libk_mm::device::DeviceMemoryIo;
|
||||
use libk_util::{bit::BitField, sync::IrqSafeSpinlock};
|
||||
use tock_registers::{
|
||||
@@ -26,6 +25,7 @@ use tock_registers::{
|
||||
register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
@@ -50,7 +50,7 @@ register_structs! {
|
||||
struct Pl061 {
|
||||
#[allow(unused)]
|
||||
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
|
||||
irq: FullIrq,
|
||||
irq: IrqHandle,
|
||||
clocks: Vec<ClockHandle>,
|
||||
resets: Vec<ResetHandle>,
|
||||
gpio_events: [AtomicU64; 8],
|
||||
@@ -69,9 +69,8 @@ impl Device for Pl061 {
|
||||
}
|
||||
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
let intc = external_interrupt_controller()?;
|
||||
intc.register_irq(self.irq.irq, self.irq.options, self.clone())?;
|
||||
intc.enable_irq(self.irq.irq)?;
|
||||
self.irq.register(self.clone())?;
|
||||
self.irq.enable()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "ygg_driver_bsp_bcm283x"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
device-tree.workspace = true
|
||||
device-api.workspace = true
|
||||
yggdrasil-abi.workspace = true
|
||||
libk-mm.workspace = true
|
||||
libk-util.workspace = true
|
||||
libk.workspace = true
|
||||
kernel-arch-aarch64.workspace = true
|
||||
|
||||
tock-registers.workspace = true
|
||||
log.workspace = true
|
||||
bytemuck.workspace = true
|
||||
futures-util.workspace = true
|
||||
@@ -1,6 +1,3 @@
|
||||
//! BCM283x AUX peripheral
|
||||
use aarch64_cpu::registers::ReadWriteable;
|
||||
use abi::error::Error;
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::{ClockController, ClockHandle},
|
||||
@@ -13,9 +10,11 @@ use device_tree::{
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
||||
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
|
||||
use tock_registers::{
|
||||
interfaces::ReadWriteable,
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite},
|
||||
};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
+9
-12
@@ -1,19 +1,13 @@
|
||||
//! Broadcom BCM2835 mini-UART driver
|
||||
|
||||
use abi::{
|
||||
error::Error,
|
||||
io::{TerminalOptions, TerminalOutputOptions},
|
||||
};
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::ClockHandle,
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{FullIrq, InterruptHandler, IrqVector},
|
||||
interrupt::{InterruptHandler, IrqHandle, IrqVector},
|
||||
};
|
||||
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
|
||||
use libk::{
|
||||
debug::DebugSink,
|
||||
device::{external_interrupt_controller, manager::DEVICE_REGISTRY},
|
||||
device::manager::DEVICE_REGISTRY,
|
||||
vfs::{Terminal, TerminalInput, TerminalOutput},
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
||||
@@ -23,6 +17,10 @@ use tock_registers::{
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite},
|
||||
};
|
||||
use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{TerminalOptions, TerminalOutputOptions},
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
@@ -63,7 +61,7 @@ struct Inner {
|
||||
/// Broadcom 283x mini-UART driver
|
||||
pub struct Bcm2835AuxUart {
|
||||
base: PhysicalAddress,
|
||||
irq: FullIrq,
|
||||
irq: IrqHandle,
|
||||
clock: ClockHandle,
|
||||
inner: OneTimeInit<Arc<Terminal<Inner>>>,
|
||||
}
|
||||
@@ -173,9 +171,8 @@ impl Device for Bcm2835AuxUart {
|
||||
}
|
||||
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
let intc = external_interrupt_controller()?;
|
||||
intc.register_irq(self.irq.irq, self.irq.options, self.clone())?;
|
||||
intc.enable_irq(self.irq.irq)?;
|
||||
self.irq.register(self.clone())?;
|
||||
self.irq.enable()?;
|
||||
|
||||
let inner = self.inner.get().output();
|
||||
let regs = inner.regs.lock();
|
||||
@@ -1,9 +1,8 @@
|
||||
use abi::error::Error;
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
gpio::{GpioController, GpioInterruptEvent, PinHandle},
|
||||
interrupt::{FullIrq, InterruptHandler, IrqVector},
|
||||
interrupt::{InterruptHandler, IrqHandle, IrqVector},
|
||||
};
|
||||
use device_tree::{
|
||||
driver::{
|
||||
@@ -12,7 +11,6 @@ use device_tree::{
|
||||
},
|
||||
DeviceTreePropertyRead, TProp,
|
||||
};
|
||||
use libk::device::external_interrupt_controller;
|
||||
use libk_mm::device::DeviceMemoryIo;
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
use tock_registers::{
|
||||
@@ -20,6 +18,7 @@ use tock_registers::{
|
||||
register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
const PUPD_NONE: u32 = 0b00;
|
||||
|
||||
@@ -69,7 +68,7 @@ register_structs! {
|
||||
struct Bcm2711Gpio {
|
||||
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
|
||||
#[allow(unused)]
|
||||
irqs: [FullIrq; 2],
|
||||
irqs: [IrqHandle; 2],
|
||||
}
|
||||
|
||||
impl Regs {
|
||||
@@ -142,10 +141,9 @@ impl Device for Bcm2711Gpio {
|
||||
regs.configure_pin_interrupts(pin, false, false, false, false);
|
||||
}
|
||||
|
||||
let intc = external_interrupt_controller()?;
|
||||
for irq in self.irqs.iter() {
|
||||
intc.register_irq(irq.irq, irq.options, self.clone())?;
|
||||
intc.enable_irq(irq.irq)?;
|
||||
irq.register(self.clone())?;
|
||||
irq.enable()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -0,0 +1,8 @@
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod aux;
|
||||
mod aux_uart;
|
||||
mod gpio;
|
||||
mod mbox;
|
||||
@@ -1,8 +1,5 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use core::{cell::UnsafeCell, mem::MaybeUninit};
|
||||
|
||||
use abi::error::Error;
|
||||
use alloc::sync::Arc;
|
||||
use device_api::device::{Device, DeviceInitContext};
|
||||
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
|
||||
@@ -25,6 +22,7 @@ use tock_registers::{
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite, WriteOnly},
|
||||
};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "ygg_driver_bsp_jh7110"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
device-tree.workspace = true
|
||||
device-api.workspace = true
|
||||
yggdrasil-abi.workspace = true
|
||||
libk-mm.workspace = true
|
||||
libk-util.workspace = true
|
||||
libk.workspace = true
|
||||
|
||||
tock-registers.workspace = true
|
||||
log.workspace = true
|
||||
bytemuck.workspace = true
|
||||
futures-util.workspace = true
|
||||
@@ -1,8 +1,5 @@
|
||||
//! StarFive JH7110 clock drivers
|
||||
|
||||
use core::{marker::PhantomData, ops::Index};
|
||||
|
||||
use abi::error::Error;
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::{ClockController, ClockHandle, Hertz, ResetController, ResetHandle},
|
||||
@@ -17,6 +14,7 @@ use device_tree::{
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
// SYSCRG clocks
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#![allow(unsafe_op_in_unsafe_fn)]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod clocks;
|
||||
mod pinctrl;
|
||||
+1
-1
@@ -1,4 +1,3 @@
|
||||
use abi::error::Error;
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use device_api::{
|
||||
clock::{ClockHandle, ResetHandle},
|
||||
@@ -18,6 +17,7 @@ use device_tree::{
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
|
||||
use libk_util::{bit::BitField, sync::IrqSafeSpinlock, OneTimeInit};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PinMuxConfig {
|
||||
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "ygg_driver_bsp_riscv"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
device-tree.workspace = true
|
||||
device-api.workspace = true
|
||||
yggdrasil-abi.workspace = true
|
||||
libk-mm.workspace = true
|
||||
libk-util.workspace = true
|
||||
libk.workspace = true
|
||||
kernel-arch-riscv64.workspace = true
|
||||
|
||||
tock-registers.workspace = true
|
||||
log.workspace = true
|
||||
bytemuck.workspace = true
|
||||
@@ -0,0 +1,70 @@
|
||||
use alloc::sync::Arc;
|
||||
use device_api::device::{Device, DeviceInitContext};
|
||||
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
|
||||
use libk::{
|
||||
error::Error,
|
||||
fs::sysfs::{self, nodes::SysfsRtcNode},
|
||||
};
|
||||
use libk_mm::device::DeviceMemoryIo;
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
use tock_registers::{
|
||||
interfaces::Readable,
|
||||
register_structs,
|
||||
registers::{ReadOnly, ReadWrite},
|
||||
};
|
||||
use yggdrasil_abi::time::NANOSECONDS_IN_SECOND;
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
(0x00 => TIME_LOW: ReadOnly<u32>),
|
||||
(0x04 => TIME_HIGH: ReadOnly<u32>),
|
||||
(0x08 => ALARM_LOW: ReadWrite<u32>),
|
||||
(0x0C => ALARM_HIGH: ReadWrite<u32>),
|
||||
(0x10 => IRQ_ENABLED: ReadWrite<u32>),
|
||||
(0x14 => CLEAR_ALARM: ReadWrite<u32>),
|
||||
(0x18 => ALARM_STATUS: ReadOnly<u32>),
|
||||
(0x1C => CLEAR_INTERRUPT: ReadWrite<u32>),
|
||||
(0x20 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
struct Rtc {
|
||||
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
|
||||
}
|
||||
|
||||
impl SysfsRtcNode for Rtc {
|
||||
fn read(&self) -> Result<u64, Error> {
|
||||
let regs = self.regs.lock();
|
||||
let low = regs.TIME_LOW.get();
|
||||
let high = regs.TIME_HIGH.get();
|
||||
let t = ((high as u64) << 32) | (low as u64);
|
||||
Ok(t / NANOSECONDS_IN_SECOND)
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Rtc {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
sysfs::nodes::add_rtc_node(self.clone());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
"Google Goldfish RTC"
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["google,goldfish-rtc"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let base = node.map_base(context, 0)?;
|
||||
|
||||
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
|
||||
|
||||
let rtc = Arc::new(Rtc { regs: IrqSafeSpinlock::new(regs) });
|
||||
|
||||
Some(rtc)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#![allow(unsafe_op_in_unsafe_fn)]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod goldfish_rtc;
|
||||
mod plic;
|
||||
@@ -1,12 +1,9 @@
|
||||
//! RISC-V PLIC driver
|
||||
|
||||
use abi::{error::Error, primitive_enum};
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{
|
||||
ExternalInterruptController, FixedInterruptTable, FullIrq, InterruptHandler,
|
||||
InterruptTable, Irq, IrqOptions, IrqVector,
|
||||
ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable, Irq,
|
||||
IrqHandle, IrqOptions, IrqVector,
|
||||
},
|
||||
};
|
||||
use device_tree::{
|
||||
@@ -15,6 +12,7 @@ use device_tree::{
|
||||
},
|
||||
DeviceTreePropertyRead, TProp,
|
||||
};
|
||||
use kernel_arch_riscv64::boot_hart_id;
|
||||
use libk::{arch::Cpu, device::register_external_interrupt_controller};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
||||
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
|
||||
@@ -23,8 +21,7 @@ use tock_registers::{
|
||||
register_structs,
|
||||
registers::{ReadOnly, ReadWrite},
|
||||
};
|
||||
|
||||
use crate::arch::riscv64::boot::boot_hart_id;
|
||||
use yggdrasil_abi::{error::Error, primitive_enum};
|
||||
|
||||
const MAX_IRQS: usize = 1024;
|
||||
|
||||
@@ -264,11 +261,12 @@ impl Device for Plic {
|
||||
}
|
||||
|
||||
impl DeviceTreeInterruptController for Plic {
|
||||
fn map_interrupt(&self, property: &TProp, offset: usize) -> Option<FullIrq> {
|
||||
fn map_interrupt(self: Arc<Self>, property: &TProp, offset: usize) -> Option<IrqHandle> {
|
||||
let num = property.read_cell(offset, 1)?;
|
||||
Some(FullIrq {
|
||||
Some(IrqHandle {
|
||||
irq: Irq::External(num as _),
|
||||
options: IrqOptions::default(),
|
||||
intc: self.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
use core::{any::Any, fmt, mem::MaybeUninit};
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use alloc::{sync::Arc, vec};
|
||||
use libk::{
|
||||
block,
|
||||
error::Error,
|
||||
task::sync::AsyncMutex,
|
||||
vfs::{
|
||||
CommonImpl, CreateInfo, DirectoryImpl, DirectoryOpenPosition, Filename, Metadata, Node,
|
||||
NodeFlags, NodeRef,
|
||||
@@ -281,10 +282,10 @@ impl DirectoryNode {
|
||||
} else {
|
||||
let file = FileNode {
|
||||
fs: self.fs.clone(),
|
||||
cluster,
|
||||
size_bytes: size,
|
||||
parent: Some(self.cluster),
|
||||
metadata,
|
||||
cluster_chain_cache: AsyncMutex::new(vec![cluster]),
|
||||
};
|
||||
|
||||
Ok(Node::regular(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use core::any::Any;
|
||||
|
||||
use alloc::{sync::Arc, vec, vec::Vec};
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use libk::{
|
||||
block,
|
||||
error::Error,
|
||||
@@ -13,28 +13,22 @@ use crate::{data::ClusterNumber, Fat32Fs};
|
||||
|
||||
pub struct FileNode {
|
||||
pub(crate) fs: Arc<Fat32Fs>,
|
||||
pub(crate) cluster: ClusterNumber,
|
||||
pub(crate) size_bytes: u32,
|
||||
pub(crate) metadata: Metadata,
|
||||
// Will be used when metadata needs to be updated
|
||||
#[allow(unused)]
|
||||
pub(crate) parent: Option<ClusterNumber>,
|
||||
|
||||
pub(crate) cluster_chain_cache: AsyncMutex<Vec<ClusterNumber>>,
|
||||
}
|
||||
|
||||
// TODO use a "sliding window" to minimize memory usage when working with large files?
|
||||
struct OpenedFile {
|
||||
cluster_chain: AsyncMutex<Vec<ClusterNumber>>,
|
||||
}
|
||||
|
||||
impl OpenedFile {
|
||||
fn new(first_cluster: ClusterNumber) -> Self {
|
||||
Self {
|
||||
cluster_chain: AsyncMutex::new(vec![first_cluster]),
|
||||
}
|
||||
}
|
||||
|
||||
async fn seek(&self, file: &FileNode, cluster_index: usize) -> Result<ClusterNumber, Error> {
|
||||
let mut chain = self.cluster_chain.lock().await;
|
||||
impl FileNode {
|
||||
async fn fetch_cluster_number(
|
||||
&self,
|
||||
file: &FileNode,
|
||||
cluster_index: usize,
|
||||
) -> Result<ClusterNumber, Error> {
|
||||
let mut chain = self.cluster_chain_cache.lock().await;
|
||||
if cluster_index >= chain.len() {
|
||||
let last = *chain.last().unwrap();
|
||||
file.fs
|
||||
@@ -47,15 +41,8 @@ impl OpenedFile {
|
||||
|
||||
Ok(chain[cluster_index])
|
||||
}
|
||||
}
|
||||
|
||||
impl FileNode {
|
||||
async fn read_inner(
|
||||
&self,
|
||||
instance: &OpenedFile,
|
||||
mut pos: u64,
|
||||
buffer: &mut [u8],
|
||||
) -> Result<usize, Error> {
|
||||
async fn read_inner(&self, mut pos: u64, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
if pos >= self.size_bytes as u64 {
|
||||
return Ok(0);
|
||||
}
|
||||
@@ -73,7 +60,9 @@ impl FileNode {
|
||||
let offset_in_sector = (pos % bps) as usize;
|
||||
let amount = rem.min(bps as usize - offset_in_sector);
|
||||
|
||||
let cluster = instance.seek(self, cluster_index as usize).await?;
|
||||
let cluster = self
|
||||
.fetch_cluster_number(self, cluster_index as usize)
|
||||
.await?;
|
||||
|
||||
self.fs
|
||||
.with_cluster_sector(cluster, sector_in_cluster, |data| {
|
||||
@@ -119,8 +108,7 @@ impl RegularImpl for FileNode {
|
||||
if opts.contains_any(OpenOptions::TRUNCATE | OpenOptions::APPEND | OpenOptions::WRITE) {
|
||||
return Err(Error::ReadOnly);
|
||||
}
|
||||
let instance = Arc::new(OpenedFile::new(self.cluster));
|
||||
Ok((0, Some(instance)))
|
||||
Ok((0, None))
|
||||
}
|
||||
|
||||
fn close(&self, _node: &NodeRef, _instance: Option<&InstanceData>) -> Result<(), Error> {
|
||||
@@ -130,14 +118,11 @@ impl RegularImpl for FileNode {
|
||||
fn read(
|
||||
&self,
|
||||
_node: &NodeRef,
|
||||
instance: Option<&InstanceData>,
|
||||
_instance: Option<&InstanceData>,
|
||||
pos: u64,
|
||||
buf: &mut [u8],
|
||||
) -> Result<usize, Error> {
|
||||
let instance = instance
|
||||
.and_then(|p| p.downcast_ref::<OpenedFile>())
|
||||
.ok_or(Error::InvalidFile)?;
|
||||
block!(self.read_inner(instance, pos, buf).await)?
|
||||
block!(self.read_inner(pos, buf).await)?
|
||||
}
|
||||
|
||||
fn write(
|
||||
|
||||
@@ -20,6 +20,7 @@ use yggdrasil_abi::{
|
||||
error::Error,
|
||||
io::{FileMode, FileType, GroupId, UserId},
|
||||
path::Path,
|
||||
time::SystemTime,
|
||||
};
|
||||
|
||||
use crate::tar::TarIterator;
|
||||
@@ -75,6 +76,7 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
|
||||
path: &Path,
|
||||
create: bool,
|
||||
mode: FileMode,
|
||||
mtime: SystemTime,
|
||||
) -> Result<NodeRef, Error> {
|
||||
let access = unsafe { AccessToken::authorized() };
|
||||
if path.is_empty() {
|
||||
@@ -97,7 +99,18 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
|
||||
}
|
||||
|
||||
let ino = INO_COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||
let node = DirectoryNode::<A>::new(self.clone(), Metadata::now_root(mode, ino));
|
||||
let metadata = Metadata {
|
||||
uid: UserId::root(),
|
||||
gid: GroupId::root(),
|
||||
atime: mtime,
|
||||
ctime: mtime,
|
||||
mode,
|
||||
mtime,
|
||||
inode: Some(ino),
|
||||
block_size: 512,
|
||||
block_count: 0,
|
||||
};
|
||||
let node = DirectoryNode::<A>::new(self.clone(), metadata);
|
||||
at.add_child(filename, node.clone())?;
|
||||
|
||||
node
|
||||
@@ -112,7 +125,7 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
|
||||
Ok(node)
|
||||
} else {
|
||||
assert!(node.is_directory());
|
||||
self.make_path(&node, rest, create, mode)
|
||||
self.make_path(&node, rest, create, mode, mtime)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,16 +133,22 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
|
||||
let kind = hdr.node_kind();
|
||||
let mode = usize::from(&hdr.mode);
|
||||
let mode = FileMode::new(0o777 & (mode as u32));
|
||||
let mtime = SystemTime::new(usize::from(&hdr.mtime) as u64, 0);
|
||||
let ino = INO_COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||
let metadata = Metadata {
|
||||
uid: UserId::root(),
|
||||
gid: GroupId::root(),
|
||||
atime: mtime,
|
||||
ctime: mtime,
|
||||
mode,
|
||||
mtime,
|
||||
inode: Some(ino),
|
||||
block_size: 512,
|
||||
block_count: usize::from(&hdr.size).div_ceil(512) as u64,
|
||||
};
|
||||
match kind {
|
||||
FileType::File => Ok(FileNode::<A>::new(
|
||||
self.clone(),
|
||||
Metadata::now_root(mode, ino),
|
||||
)),
|
||||
FileType::Directory => Ok(DirectoryNode::<A>::new(
|
||||
self.clone(),
|
||||
Metadata::now_root(mode, ino),
|
||||
)),
|
||||
FileType::File => Ok(FileNode::<A>::new(self.clone(), metadata)),
|
||||
FileType::Directory => Ok(DirectoryNode::<A>::new(self.clone(), metadata)),
|
||||
FileType::Symlink => {
|
||||
let target = hdr.symlink_target()?;
|
||||
Ok(fixed_path_symlink(target))
|
||||
@@ -149,9 +168,10 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
|
||||
};
|
||||
|
||||
let path = Path::from_str(hdr.name.as_str()?.trim_matches('/'));
|
||||
let mtime = SystemTime::new(usize::from(&hdr.mtime) as u64, 0);
|
||||
|
||||
let (dirname, filename) = path.split_right();
|
||||
let parent = self.make_path(&root, dirname, true, FileMode::new(0o755))?;
|
||||
let parent = self.make_path(&root, dirname, true, FileMode::new(0o755), mtime)?;
|
||||
let node = self.create_node_initial(hdr)?;
|
||||
let filename = Filename::new(filename)?;
|
||||
|
||||
@@ -164,8 +184,9 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
|
||||
panic!("Unreachable");
|
||||
};
|
||||
|
||||
let mtime = SystemTime::new(usize::from(&hdr.mtime) as u64, 0);
|
||||
let path = Path::from_str(hdr.name.as_str()?.trim_matches('/'));
|
||||
let node = self.make_path(&root, path, false, FileMode::empty())?;
|
||||
let node = self.make_path(&root, path, false, FileMode::empty(), mtime)?;
|
||||
assert_eq!(node.ty(), hdr.node_kind());
|
||||
|
||||
let uid = unsafe { UserId::from_raw(usize::from(&hdr.uid) as u32) };
|
||||
|
||||
@@ -23,7 +23,7 @@ pub(crate) struct TarEntry {
|
||||
pub uid: OctalField<8>,
|
||||
pub gid: OctalField<8>,
|
||||
pub size: OctalField<12>,
|
||||
_mtime: OctalField<12>,
|
||||
pub mtime: OctalField<12>,
|
||||
_checksum: OctalField<8>,
|
||||
type_: u8,
|
||||
link_name: TarString<100>,
|
||||
|
||||
@@ -8,14 +8,13 @@ use device_api::{
|
||||
clock::{ClockHandle, ResetHandle},
|
||||
device::{Device, DeviceInitContext},
|
||||
dma::DmaAllocator,
|
||||
interrupt::{FullIrq, InterruptHandler, IrqVector},
|
||||
interrupt::{InterruptHandler, IrqHandle, IrqVector},
|
||||
};
|
||||
use device_tree::driver::{
|
||||
device_tree_driver, util::read_mac_address, InitSequence, Node, ProbeContext,
|
||||
};
|
||||
use futures_util::task::AtomicWaker;
|
||||
use libk::{
|
||||
device::external_interrupt_controller,
|
||||
dma::DmaBuffer,
|
||||
error::Error,
|
||||
task::runtime::{self, psleep, pwait},
|
||||
@@ -68,7 +67,7 @@ struct Stmmac {
|
||||
rst_stmmaceth: ResetHandle,
|
||||
rst_ahb: ResetHandle,
|
||||
|
||||
irq: FullIrq,
|
||||
irq: IrqHandle,
|
||||
softirq_events: BitmapEvent<AtomicWaker>,
|
||||
|
||||
inner: OneTimeInit<Inner>,
|
||||
@@ -184,9 +183,7 @@ impl Device for Stmmac {
|
||||
}
|
||||
let dma = self.dma.init(cx.dma_allocator.clone());
|
||||
|
||||
let intc = external_interrupt_controller()?;
|
||||
|
||||
intc.register_irq(self.irq.irq, self.irq.options, self.clone())?;
|
||||
self.irq.register(self.clone())?;
|
||||
|
||||
let tx_ring_capacity = 32;
|
||||
let rx_ring_capacity = 32;
|
||||
@@ -350,7 +347,7 @@ impl Device for Stmmac {
|
||||
ygg_driver_net_core::register_interface(NetworkInterfaceType::Ethernet, self.clone());
|
||||
self.iface_id.init(iface.id());
|
||||
|
||||
intc.enable_irq(self.irq.irq)?;
|
||||
self.irq.enable()?;
|
||||
|
||||
let p = self.clone();
|
||||
runtime::spawn(async move { p.softirq().await })?;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "ygg_driver_usb_dwc2"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
yggdrasil-abi = { path = "../../../../lib/abi" }
|
||||
device-api = { path = "../../../lib/device-api", features = ["derive"] }
|
||||
ygg_driver_usb = { path = "../../bus/usb" }
|
||||
device-tree = { path = "../../../lib/device-tree" }
|
||||
|
||||
libk-util = { path = "../../../libk/libk-util" }
|
||||
libk-mm = { path = "../../../libk/libk-mm" }
|
||||
libk = { path = "../../../libk" }
|
||||
|
||||
async-trait.workspace = true
|
||||
log.workspace = true
|
||||
atomic_enum.workspace = true
|
||||
tock-registers.workspace = true
|
||||
bytemuck.workspace = true
|
||||
futures-util.workspace = true
|
||||
@@ -0,0 +1,222 @@
|
||||
#![no_std]
|
||||
|
||||
use core::{
|
||||
sync::atomic::{AtomicBool, AtomicU32},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{InterruptHandler, IrqVector},
|
||||
};
|
||||
use futures_util::task::AtomicWaker;
|
||||
use libk::{error::Error, task::runtime};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
||||
use libk_util::{event::BitmapEvent, sync::IrqSafeSpinlock};
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
LocalRegisterCopy,
|
||||
};
|
||||
use yggdrasil_abi::bitflags;
|
||||
|
||||
use crate::{
|
||||
regs::{PortSpeed, Regs},
|
||||
vendor::Dwc2Impl,
|
||||
};
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod regs;
|
||||
mod vendor;
|
||||
|
||||
pub struct Dwc2<H: Dwc2Impl> {
|
||||
imp: H,
|
||||
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
|
||||
name: Arc<str>,
|
||||
|
||||
channel_bitmask: AtomicU32,
|
||||
port_event_mask: BitmapEvent<AtomicWaker>,
|
||||
}
|
||||
|
||||
impl<H: Dwc2Impl + 'static> Dwc2<H> {
|
||||
pub unsafe fn new<N: Into<Arc<str>>>(
|
||||
imp: H,
|
||||
base: PhysicalAddress,
|
||||
name: N,
|
||||
) -> Result<Self, Error> {
|
||||
let name = name.into();
|
||||
let regs = IrqSafeSpinlock::new(unsafe { DeviceMemoryIo::map(base, Default::default()) }?);
|
||||
Ok(Self {
|
||||
imp,
|
||||
regs,
|
||||
name,
|
||||
channel_bitmask: AtomicU32::new(0),
|
||||
port_event_mask: BitmapEvent::new(AtomicWaker::new()),
|
||||
})
|
||||
}
|
||||
|
||||
fn signal_events(&self, channels: u16, ports: u32) {
|
||||
self.port_event_mask
|
||||
.signal(((channels as u64) << 32) | (ports as u64));
|
||||
}
|
||||
|
||||
async fn setup_port(self: Arc<Self>, speed: PortSpeed) -> Result<(), Error> {
|
||||
log::info!("{}: setup port", self.name);
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn wait_for_port_event(&self) -> LocalRegisterCopy<u32, regs::HPRT::Register> {
|
||||
LocalRegisterCopy::new(self.port_event_mask.wait_mask(0x2A).await as u32)
|
||||
}
|
||||
|
||||
async fn wait_for_channel_events(&self) -> u16 {
|
||||
(self.port_event_mask.wait_mask(0xFFFF00000000).await >> 32) as u16
|
||||
}
|
||||
|
||||
async fn wait_for_device(&self) -> PortSpeed {
|
||||
let hprt = self.regs.lock().HPRT.extract();
|
||||
// If already connected, begin init, if not, wait for connect change
|
||||
if !hprt.matches_all(regs::HPRT::PCSTS::SET) {
|
||||
loop {
|
||||
let event = self.wait_for_port_event().await;
|
||||
if event.matches_all(regs::HPRT::PCDET::SET) {
|
||||
log::info!("{}: port connection detected", self.name);
|
||||
break;
|
||||
} else {
|
||||
log::warn!("{}: unhandled port event {:#x}", self.name, event.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("{}: port connected", self.name);
|
||||
// Reset the port
|
||||
{
|
||||
let regs = self.regs.lock();
|
||||
regs.HPRT.modify(regs::HPRT::PRST::SET);
|
||||
}
|
||||
runtime::sleep(Duration::from_millis(10)).await;
|
||||
// Clear the reset
|
||||
{
|
||||
let regs = self.regs.lock();
|
||||
regs.HPRT.modify(regs::HPRT::PRST::CLEAR);
|
||||
}
|
||||
|
||||
// Wait for port enable status
|
||||
loop {
|
||||
let event = self.wait_for_port_event().await;
|
||||
|
||||
if event.matches_all(regs::HPRT::PENA::SET) {
|
||||
log::info!("{}: port enabled", self.name);
|
||||
break;
|
||||
}
|
||||
if event.matches_all(regs::HPRT::PCSTS::CLEAR) {
|
||||
todo!("Handle port disconnect");
|
||||
}
|
||||
}
|
||||
|
||||
let regs = self.regs.lock();
|
||||
match regs.HPRT.read_as_enum(regs::HPRT::PSPD) {
|
||||
Some(regs::HPRT::PSPD::Value::LowSpeed) => PortSpeed::LowSpeed,
|
||||
Some(regs::HPRT::PSPD::Value::FullSpeed) => PortSpeed::FullSpeed,
|
||||
Some(regs::HPRT::PSPD::Value::HighSpeed) => PortSpeed::HighSpeed,
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn softirq(self: Arc<Self>) -> Result<(), Error> {
|
||||
loop {
|
||||
// 4-9
|
||||
let speed = self.wait_for_device().await;
|
||||
|
||||
// 10. HFIR setup for PHY clk TODO
|
||||
// 11. Configure FSLPCS based on step 9 TODO
|
||||
// 12. GRXFSIZ to configure Rx FIFO size
|
||||
// 13. HNPTXFSIZ to configure non-periodic Tx FIFO
|
||||
// 14. HPTXFSIZ to configure periodic Tx FIFO
|
||||
if let Err(error) = self.clone().setup_port(speed).await {
|
||||
log::error!("{}: port error {:?}", self.name, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn setup(self: Arc<Self>) -> Result<(), Error> {
|
||||
log::info!("{}: init", self.name);
|
||||
let regs = self.regs.lock();
|
||||
|
||||
regs.soft_reset(Duration::from_secs(1)).await?;
|
||||
|
||||
// Common setup
|
||||
regs.GAHBCFG
|
||||
.write(regs::GAHBCFG::GINTMSK::SET + regs::GAHBCFG::DMAEN::SET);
|
||||
|
||||
// GUSBCTL
|
||||
// TODO HNP/SRP capability?
|
||||
// TODO OTG_FS/OTG_HS timeout calibration
|
||||
// TODO USB turnaround time
|
||||
|
||||
regs.GINTMSK.write(
|
||||
regs::GINTMSK::OTGINT::SET
|
||||
+ regs::GINTMSK::MMISM::SET
|
||||
+ regs::GINTMSK::USBRST::SET
|
||||
+ regs::GINTMSK::ENUMDNEM::SET
|
||||
+ regs::GINTMSK::OEPINT::SET
|
||||
+ regs::GINTMSK::IEPINT::SET
|
||||
// + regs::GINTMSK::PRTIM::SET
|
||||
+ regs::GINTMSK::DISCINT::SET,
|
||||
);
|
||||
|
||||
let mode = if regs.GINTSTS.matches_all(regs::GINTSTS::CMOD::Host) {
|
||||
"host"
|
||||
} else {
|
||||
log::warn!("TODO: device mode setup");
|
||||
"device"
|
||||
};
|
||||
log::info!("{}: operating in {} mode", self.name, mode);
|
||||
|
||||
// Host mode setup
|
||||
// 1. Enable HPRTINT
|
||||
regs.GINTMSK.modify(regs::GINTMSK::PRTIM::SET);
|
||||
// 2. Setup FS host
|
||||
regs.HCFG
|
||||
.write(regs::HCFG::FSLSS::CLEAR + regs::HCFG::FSLSPCS.val(1));
|
||||
// 3. Drive Vbus on the USB
|
||||
regs.HPRT.modify(regs::HPRT::PPWR::SET);
|
||||
|
||||
// 4. Wait for PCDET interrupt on HPRT0
|
||||
runtime::spawn(self.clone().softirq())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Dwc2Impl + 'static> Device for Dwc2<H> {
|
||||
unsafe fn init(self: Arc<Self>, cx: DeviceInitContext) -> Result<(), Error> {
|
||||
unsafe { self.imp.initialize_host_early(self.clone()) }?;
|
||||
runtime::spawn(self.clone().setup())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: Dwc2Impl + 'static> InterruptHandler for Dwc2<H> {
|
||||
fn handle_irq(self: Arc<Self>, vector: IrqVector) -> bool {
|
||||
let regs = self.regs.lock();
|
||||
let gintsts = regs.take_interrupt();
|
||||
|
||||
let hprt = if gintsts.matches_all(regs::GINTSTS::HPRTINT::SET) {
|
||||
regs.take_port_status().get()
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
if hprt != 0 {
|
||||
self.signal_events(0, hprt);
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
use core::time::Duration;
|
||||
|
||||
use libk::{error::Error, task::runtime, time};
|
||||
use tock_registers::{
|
||||
interfaces::{ReadWriteable, Readable, Writeable},
|
||||
register_bitfields, register_structs,
|
||||
registers::ReadWrite,
|
||||
LocalRegisterCopy,
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
pub GAHBCFG [
|
||||
PTXFELVL OFFSET(8) NUMBITS(1) [],
|
||||
TXFELVL OFFSET(7) NUMBITS(1) [],
|
||||
DMAEN OFFSET(5) NUMBITS(1) [],
|
||||
HBSTLEN OFFSET(1) NUMBITS(4) [],
|
||||
GINTMSK OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
pub GUSBCFG [
|
||||
FDMOD OFFSET(30) NUMBITS(1) [],
|
||||
FHMOD OFFSET(29) NUMBITS(1) [],
|
||||
ULPIIPD OFFSET(25) NUMBITS(1) [],
|
||||
PTCI OFFSET(24) NUMBITS(1) [],
|
||||
PCCI OFFSET(23) NUMBITS(1) [],
|
||||
TSDPS OFFSET(22) NUMBITS(1) [],
|
||||
ULPIEVBUSI OFFSET(21) NUMBITS(1) [],
|
||||
ULPIEVBUSD OFFSET(20) NUMBITS(1) [],
|
||||
ULPICSM OFFSET(19) NUMBITS(1) [],
|
||||
ULPIAR OFFSET(18) NUMBITS(1) [],
|
||||
ULPIFSLS OFFSET(17) NUMBITS(1) [],
|
||||
PHYLPC OFFSET(15) NUMBITS(1) [],
|
||||
TRDT OFFSET(10) NUMBITS(4) [],
|
||||
HNPCAP OFFSET(9) NUMBITS(1) [],
|
||||
SRPCAP OFFSET(8) NUMBITS(1) [],
|
||||
PHYSEL OFFSET(6) NUMBITS(1) [],
|
||||
TOCAL OFFSET(0) NUMBITS(3) [],
|
||||
],
|
||||
pub GRSTCTL [
|
||||
AHBIDL OFFSET(31) NUMBITS(1) [],
|
||||
DMAREQ OFFSET(30) NUMBITS(1) [],
|
||||
TXFNUM OFFSET(6) NUMBITS(5) [],
|
||||
TXFFLSH OFFSET(5) NUMBITS(1) [],
|
||||
RXFFLSH OFFSET(4) NUMBITS(1) [],
|
||||
FCRST OFFSET(2) NUMBITS(1) [],
|
||||
PSRST OFFSET(1) NUMBITS(1) [],
|
||||
CSRST OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
pub GINTSTS [
|
||||
WKUPINT OFFSET(31) NUMBITS(1) [],
|
||||
SRQINT OFFSET(30) NUMBITS(1) [],
|
||||
DISCINT OFFSET(29) NUMBITS(1) [],
|
||||
CIDSCHG OFFSET(28) NUMBITS(1) [],
|
||||
LPMINT OFFSET(27) NUMBITS(1) [],
|
||||
PTXFE OFFSET(26) NUMBITS(1) [],
|
||||
HCINT OFFSET(25) NUMBITS(1) [],
|
||||
HPRTINT OFFSET(24) NUMBITS(1) [],
|
||||
RSTDET OFFSET(23) NUMBITS(1) [],
|
||||
DATAFSUSP OFFSET(22) NUMBITS(1) [],
|
||||
IPXFR OFFSET(21) NUMBITS(1) [],
|
||||
IISOIXFR OFFSET(20) NUMBITS(1) [],
|
||||
OEPINT OFFSET(19) NUMBITS(1) [],
|
||||
IEPINT OFFSET(18) NUMBITS(1) [],
|
||||
EOPF OFFSET(15) NUMBITS(1) [],
|
||||
ISOODRP OFFSET(14) NUMBITS(1) [],
|
||||
ENUMDNE OFFSET(13) NUMBITS(1) [],
|
||||
USBRST OFFSET(12) NUMBITS(1) [],
|
||||
USBSUSP OFFSET(11) NUMBITS(1) [],
|
||||
ESUSP OFFSET(10) NUMBITS(1) [],
|
||||
GONAKEFF OFFSET(7) NUMBITS(1) [],
|
||||
GINAKEFF OFFSET(6) NUMBITS(1) [],
|
||||
NPTXFE OFFSET(5) NUMBITS(1) [],
|
||||
RXFLVL OFFSET(4) NUMBITS(1) [],
|
||||
SOF OFFSET(3) NUMBITS(1) [],
|
||||
OTGINT OFFSET(2) NUMBITS(1) [],
|
||||
MMIS OFFSET(1) NUMBITS(1) [],
|
||||
CMOD OFFSET(0) NUMBITS(1) [
|
||||
Device = 0,
|
||||
Host = 1
|
||||
],
|
||||
],
|
||||
pub GINTMSK [
|
||||
WUIM OFFSET(31) NUMBITS(1) [],
|
||||
SRQIM OFFSET(30) NUMBITS(1) [],
|
||||
DISCINT OFFSET(29) NUMBITS(1) [],
|
||||
CIDSCHGM OFFSET(28) NUMBITS(1) [],
|
||||
LPMINTM OFFSET(27) NUMBITS(1) [],
|
||||
PTXFEM OFFSET(26) NUMBITS(1) [],
|
||||
HCIM OFFSET(25) NUMBITS(1) [],
|
||||
PRTIM OFFSET(24) NUMBITS(1) [],
|
||||
RSTDETM OFFSET(23) NUMBITS(1) [],
|
||||
FSUSPM OFFSET(22) NUMBITS(1) [],
|
||||
IPXFRM OFFSET(21) NUMBITS(1) [],
|
||||
IISOIXFRM OFFSET(20) NUMBITS(1) [],
|
||||
OEPINT OFFSET(19) NUMBITS(1) [],
|
||||
IEPINT OFFSET(18) NUMBITS(1) [],
|
||||
EOPFM OFFSET(15) NUMBITS(1) [],
|
||||
ISOODRPM OFFSET(14) NUMBITS(1) [],
|
||||
ENUMDNEM OFFSET(13) NUMBITS(1) [],
|
||||
USBRST OFFSET(12) NUMBITS(1) [],
|
||||
USBSUSPM OFFSET(11) NUMBITS(1) [],
|
||||
ESUSPM OFFSET(10) NUMBITS(1) [],
|
||||
GONAKEFFM OFFSET(7) NUMBITS(1) [],
|
||||
GINAKEFFM OFFSET(6) NUMBITS(1) [],
|
||||
NPTXFEM OFFSET(5) NUMBITS(1) [],
|
||||
RXFLVLM OFFSET(4) NUMBITS(1) [],
|
||||
SOFM OFFSET(3) NUMBITS(1) [],
|
||||
OTGINT OFFSET(2) NUMBITS(1) [],
|
||||
MMISM OFFSET(1) NUMBITS(1) [],
|
||||
],
|
||||
pub HCFG [
|
||||
FSLSS OFFSET(2) NUMBITS(1) [],
|
||||
FSLSPCS OFFSET(0) NUMBITS(2) [],
|
||||
],
|
||||
pub HPRT [
|
||||
PSPD OFFSET(17) NUMBITS(2) [
|
||||
HighSpeed = 0,
|
||||
FullSpeed = 1,
|
||||
LowSpeed = 2,
|
||||
],
|
||||
PTCTL OFFSET(13) NUMBITS(4) [],
|
||||
PPWR OFFSET(12) NUMBITS(1) [],
|
||||
PLSTS OFFSET(10) NUMBITS(2) [],
|
||||
PRST OFFSET(8) NUMBITS(1) [],
|
||||
PSUSP OFFSET(7) NUMBITS(1) [],
|
||||
PRES OFFSET(6) NUMBITS(1) [],
|
||||
POCCHNG OFFSET(5) NUMBITS(1) [],
|
||||
POCA OFFSET(4) NUMBITS(1) [],
|
||||
PENCHNG OFFSET(3) NUMBITS(1) [],
|
||||
PENA OFFSET(2) NUMBITS(1) [],
|
||||
PCDET OFFSET(1) NUMBITS(1) [],
|
||||
PCSTS OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
|
||||
pub HCCHARx [
|
||||
CHENA OFFSET(31) NUMBITS(1) [],
|
||||
CHDIS OFFSET(30) NUMBITS(1) [],
|
||||
ODDFRM OFFSET(29) NUMBITS(1) [],
|
||||
DAD OFFSET(22) NUMBITS(7) [],
|
||||
MCNT OFFSET(20) NUMBITS(2) [],
|
||||
EPTYP OFFSET(18) NUMBITS(2) [
|
||||
Control = 0,
|
||||
Isochronous = 1,
|
||||
Bulk = 2,
|
||||
Interrupt = 3
|
||||
],
|
||||
LSDEV OFFSET(17) NUMBITS(1) [],
|
||||
EPDIR OFFSET(15) NUMBITS(1) [
|
||||
Out = 0,
|
||||
In = 1
|
||||
],
|
||||
EPNUM OFFSET(11) NUMBITS(4) [],
|
||||
MPSIZ OFFSET(0) NUMBITS(11) [],
|
||||
],
|
||||
pub HCSPLTx [
|
||||
SPLITEN OFFSET(31) NUMBITS(1) [],
|
||||
COMPLSPLT OFFSET(16) NUMBITS(1) [],
|
||||
XACTPOS OFFSET(14) NUMBITS(2) [
|
||||
Mid = 0,
|
||||
End = 1,
|
||||
Begin = 2,
|
||||
All = 3,
|
||||
],
|
||||
HUBADDR OFFSET(7) NUMBITS(7) [],
|
||||
PRTADDR OFFSET(0) NUMBITS(7) [],
|
||||
],
|
||||
pub HCINTx [
|
||||
DTERR OFFSET(10) NUMBITS(1) [],
|
||||
FRMOR OFFSET(9) NUMBITS(1) [],
|
||||
BBERR OFFSET(8) NUMBITS(1) [],
|
||||
TXERR OFFSET(7) NUMBITS(1) [],
|
||||
NYET OFFSET(6) NUMBITS(1) [],
|
||||
ACK OFFSET(5) NUMBITS(1) [],
|
||||
NAK OFFSET(4) NUMBITS(1) [],
|
||||
STALL OFFSET(3) NUMBITS(1) [],
|
||||
AHBERR OFFSET(2) NUMBITS(1) [],
|
||||
CHH OFFSET(1) NUMBITS(1) [],
|
||||
XFRC OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
pub HCTSIZx [
|
||||
DPID OFFSET(29) NUMBITS(2) [
|
||||
DATA0 = 0,
|
||||
DATA2 = 1,
|
||||
DATA1 = 2,
|
||||
SETUP = 3
|
||||
],
|
||||
PKTCNT OFFSET(19) NUMBITS(10) [],
|
||||
XFRSIZ OFFSET(0) NUMBITS(19) []
|
||||
],
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
pub HostChannelRegs {
|
||||
(0x00 => pub HCCHARx: ReadWrite<u32, HCCHARx::Register>),
|
||||
(0x04 => pub HCSPLTx: ReadWrite<u32, HCSPLTx::Register>),
|
||||
(0x08 => pub HCINTx: ReadWrite<u32, HCINTx::Register>),
|
||||
(0x0C => pub HCINTMSKx: ReadWrite<u32, HCINTx::Register>),
|
||||
(0x10 => pub HCTSIZx: ReadWrite<u32, HCTSIZx::Register>),
|
||||
(0x14 => pub HCDMAx: ReadWrite<u32>),
|
||||
(0x18 => _0),
|
||||
(0x20 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
pub Regs {
|
||||
(0x000 => pub GOTGCTL: ReadWrite<u32>),
|
||||
(0x004 => pub GOTGINT: ReadWrite<u32>),
|
||||
(0x008 => pub GAHBCFG: ReadWrite<u32, GAHBCFG::Register>),
|
||||
(0x00C => pub GUSBCFG: ReadWrite<u32, GUSBCFG::Register>),
|
||||
(0x010 => pub GRSTCTL: ReadWrite<u32, GRSTCTL::Register>),
|
||||
(0x014 => pub GINTSTS: ReadWrite<u32, GINTSTS::Register>),
|
||||
(0x018 => pub GINTMSK: ReadWrite<u32, GINTMSK::Register>),
|
||||
(0x01C => pub GRXSTSR: ReadWrite<u32>),
|
||||
(0x020 => pub GRXSTSP: ReadWrite<u32>),
|
||||
(0x024 => pub GRXFSIZ: ReadWrite<u32>),
|
||||
(0x028 => pub HNPTXFSIZ: ReadWrite<u32>),
|
||||
(0x02C => pub HNPTXSTS: ReadWrite<u32>),
|
||||
(0x030 => _0),
|
||||
(0x038 => pub GCCFG: ReadWrite<u32>),
|
||||
(0x03C => pub CID: ReadWrite<u32>),
|
||||
(0x040 => _1),
|
||||
(0x054 => pub GLPMCFG: ReadWrite<u32>),
|
||||
(0x058 => _2),
|
||||
(0x100 => pub HPTXFSIZ: ReadWrite<u32>),
|
||||
(0x104 => pub DIEPTXF_N: [ReadWrite<u32>; 8]),
|
||||
(0x124 => _3),
|
||||
(0x400 => pub HCFG: ReadWrite<u32, HCFG::Register>),
|
||||
(0x404 => pub HFIR: ReadWrite<u32>),
|
||||
(0x408 => pub HFNUM: ReadWrite<u32>),
|
||||
(0x40C => _4),
|
||||
(0x410 => pub HPTXSTS: ReadWrite<u32>),
|
||||
(0x414 => pub HAINT: ReadWrite<u32>),
|
||||
(0x418 => pub HAINTMSK: ReadWrite<u32>),
|
||||
(0x41C => _5),
|
||||
(0x440 => pub HPRT: ReadWrite<u32, HPRT::Register>),
|
||||
(0x444 => _6),
|
||||
(0x500 => pub CHANNELx: [HostChannelRegs; 16]),
|
||||
(0x700 => _7),
|
||||
(0x800 => _8),
|
||||
(0xF00 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Regs {}
|
||||
unsafe impl Sync for Regs {}
|
||||
|
||||
pub enum PortSpeed {
|
||||
LowSpeed,
|
||||
FullSpeed,
|
||||
HighSpeed,
|
||||
}
|
||||
|
||||
impl Regs {
|
||||
pub async fn soft_reset(&self, timeout: Duration) -> Result<(), Error> {
|
||||
self.GRSTCTL.modify(GRSTCTL::CSRST::SET);
|
||||
|
||||
let deadline = time::monotonic_time() + timeout;
|
||||
let mut timeout = true;
|
||||
while time::monotonic_time() < deadline {
|
||||
if self.GRSTCTL.matches_all(GRSTCTL::CSRST::CLEAR) {
|
||||
timeout = false;
|
||||
break;
|
||||
}
|
||||
runtime::sleep(Duration::from_millis(20)).await;
|
||||
}
|
||||
if timeout {
|
||||
return Err(Error::TimedOut);
|
||||
}
|
||||
|
||||
// The spec wants the OS to wait for at least 3 PHY clocks
|
||||
runtime::sleep(Duration::from_millis(100)).await;
|
||||
|
||||
assert!(self.GRSTCTL.matches_all(GRSTCTL::AHBIDL::SET));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn take_interrupt(&self) -> LocalRegisterCopy<u32, GINTSTS::Register> {
|
||||
let intsts = self.GINTSTS.extract();
|
||||
self.GINTSTS.set(intsts.get());
|
||||
intsts
|
||||
}
|
||||
|
||||
pub fn take_port_status(&self) -> LocalRegisterCopy<u32, HPRT::Register> {
|
||||
let hprt = self.HPRT.extract();
|
||||
self.HPRT.set(hprt.get());
|
||||
hprt
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{clock::ClockHandle, device::Device, interrupt::IrqHandle};
|
||||
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
|
||||
use libk::error::Error;
|
||||
use libk_mm::device::DeviceMemoryIo;
|
||||
|
||||
use crate::{regs::Regs, Dwc2};
|
||||
|
||||
pub trait Dwc2Impl: Send + Sync + Sized {
|
||||
unsafe fn initialize_host_early(&self, dwc2: Arc<Dwc2<Self>>) -> Result<(), Error>;
|
||||
async unsafe fn initialize_host(
|
||||
&self,
|
||||
dwc2: Arc<Dwc2<Self>>,
|
||||
regs: &DeviceMemoryIo<'_, Regs>,
|
||||
) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
pub struct Bcm2835Usb {
|
||||
clk_otg: ClockHandle,
|
||||
irq: IrqHandle,
|
||||
}
|
||||
|
||||
impl Dwc2Impl for Bcm2835Usb {
|
||||
unsafe fn initialize_host_early(&self, dwc2: Arc<Dwc2<Self>>) -> Result<(), Error> {
|
||||
// TODO PHY setup
|
||||
self.irq.register(dwc2)?;
|
||||
self.irq.enable()?;
|
||||
|
||||
self.clk_otg.enable()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async unsafe fn initialize_host(
|
||||
&self,
|
||||
dwc2: Arc<Dwc2<Self>>,
|
||||
regs: &DeviceMemoryIo<'_, Regs>,
|
||||
) -> Result<(), Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["brcm,bcm2835-usb"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let base = node.map_base(context, 0).unwrap();
|
||||
let clk_otg = node.named_clock("otg").unwrap();
|
||||
let irq = node.interrupt(0).unwrap();
|
||||
let name = node.name()?;
|
||||
|
||||
node.dump();
|
||||
|
||||
let imp = Bcm2835Usb {
|
||||
clk_otg,
|
||||
irq
|
||||
};
|
||||
let dwc2 = unsafe { Dwc2::new(imp, base, name) }
|
||||
.inspect_err(|e| log::error!("{name}: setup error {e:?}"))
|
||||
.ok()?;
|
||||
|
||||
Some(Arc::new(dwc2))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,11 @@ use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::device::Device;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct FullIrq {
|
||||
#[derive(Clone)]
|
||||
pub struct IrqHandle {
|
||||
pub irq: Irq,
|
||||
pub options: IrqOptions,
|
||||
pub intc: Arc<dyn ExternalInterruptController>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
@@ -154,6 +155,16 @@ pub struct FixedInterruptTable<const N: usize> {
|
||||
rows: [Option<Arc<dyn InterruptHandler>>; N],
|
||||
}
|
||||
|
||||
impl IrqHandle {
|
||||
pub fn register(&self, handler: Arc<dyn InterruptHandler>) -> Result<(), Error> {
|
||||
self.intc.register_irq(self.irq, self.options, handler)
|
||||
}
|
||||
|
||||
pub fn enable(&self) -> Result<(), Error> {
|
||||
self.intc.enable_irq(self.irq)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> FixedInterruptTable<N> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
|
||||
@@ -3,7 +3,7 @@ use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::{ClockHandle, ResetHandle},
|
||||
gpio::PinHandle,
|
||||
interrupt::FullIrq,
|
||||
interrupt::IrqHandle,
|
||||
};
|
||||
use fdt_rs::spec::Phandle;
|
||||
|
||||
@@ -83,14 +83,14 @@ pub fn map_interrupt_at(
|
||||
interrupt_controller: &Arc<Node>,
|
||||
property: &TProp,
|
||||
offset: usize,
|
||||
) -> Option<FullIrq> {
|
||||
) -> Option<IrqHandle> {
|
||||
let interrupt_controller = interrupt_controller.interrupt_controller.try_get()?;
|
||||
interrupt_controller.map_interrupt(property, offset)
|
||||
interrupt_controller.clone().map_interrupt(property, offset)
|
||||
}
|
||||
|
||||
/// Same as [map_interrupt_at], but uses a phandle to address the `interrupt-controller` node
|
||||
/// and a scaled `index`.
|
||||
pub fn map_interrupt(phandle: Phandle, property: &TProp, index: usize) -> Option<FullIrq> {
|
||||
pub fn map_interrupt(phandle: Phandle, property: &TProp, index: usize) -> Option<IrqHandle> {
|
||||
let interrupt_controller = lookup_phandle(phandle, true)?;
|
||||
let interrupt_cells = interrupt_controller.self_interrupt_cells()?;
|
||||
map_interrupt_at(&interrupt_controller, property, index * interrupt_cells)
|
||||
|
||||
@@ -7,7 +7,7 @@ use device_api::{
|
||||
clock::{ClockHandle, ResetHandle},
|
||||
device::Device,
|
||||
gpio::PinHandle,
|
||||
interrupt::{ExternalInterruptController, FullIrq},
|
||||
interrupt::{ExternalInterruptController, IrqHandle},
|
||||
};
|
||||
use libk::error::Error;
|
||||
|
||||
@@ -25,7 +25,7 @@ pub trait Driver: Sync {
|
||||
pub trait DeviceTreeInterruptController {
|
||||
/// Reads interrupt information from `property` at given `offset` and maps it to
|
||||
/// the interrupt used by the controller
|
||||
fn map_interrupt(&self, property: &TProp, offset: usize) -> Option<FullIrq>;
|
||||
fn map_interrupt(self: Arc<Self>, property: &TProp, offset: usize) -> Option<IrqHandle>;
|
||||
|
||||
/// Returns the [ExternalInterruptController] implementor of this node
|
||||
fn as_interrupt_controller(self: Arc<Self>) -> Arc<dyn ExternalInterruptController>;
|
||||
|
||||
@@ -10,7 +10,7 @@ use device_api::{
|
||||
clock::{ClockController, ClockHandle, ResetHandle},
|
||||
device::{Device, DeviceInitContext},
|
||||
gpio::PinHandle,
|
||||
interrupt::{ExternalInterruptController, FullIrq, MessageInterruptController},
|
||||
interrupt::{ExternalInterruptController, IrqHandle, MessageInterruptController},
|
||||
};
|
||||
use fdt_rs::spec::Phandle;
|
||||
use libk::dma::DummyDmaAllocator;
|
||||
@@ -410,7 +410,7 @@ impl Node {
|
||||
|
||||
/// Reads interrupt information from `interrupts[index]` property, mapped by the node's
|
||||
/// `interrupt-parent`.
|
||||
pub fn interrupt(&self, index: usize) -> Option<FullIrq> {
|
||||
pub fn interrupt(&self, index: usize) -> Option<IrqHandle> {
|
||||
let interrupts = self.property("interrupts")?;
|
||||
let phandle = self.interrupt_parent?;
|
||||
map_interrupt(phandle, &interrupts, index)
|
||||
@@ -418,7 +418,7 @@ impl Node {
|
||||
|
||||
/// Same as [Node::interrupt], but allows specifying other property to read the information
|
||||
/// from.
|
||||
pub fn interrupt_from(&self, property: &TProp, index: usize) -> Option<FullIrq> {
|
||||
pub fn interrupt_from(&self, property: &TProp, index: usize) -> Option<IrqHandle> {
|
||||
let phandle = self.interrupt_parent?;
|
||||
map_interrupt(phandle, property, index)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
gpio::{GpioPinConfig, GpioPinLevel, InputPinBias, OutputPinBias},
|
||||
interrupt::{FullIrq, MessageInterruptController},
|
||||
interrupt::{IrqHandle, MessageInterruptController},
|
||||
};
|
||||
use fdt_rs::prelude::PropReader;
|
||||
use yggdrasil_abi::net::MacAddress;
|
||||
@@ -44,7 +44,6 @@ pub struct GenericPinctrlConfig {
|
||||
}
|
||||
|
||||
/// Represents an entry in a PCIe-controller's `interrupt-map` field
|
||||
#[derive(Debug)]
|
||||
pub struct PcieInterruptEntry {
|
||||
/// PCI address bus
|
||||
pub bus: u8,
|
||||
@@ -57,7 +56,7 @@ pub struct PcieInterruptEntry {
|
||||
/// Destination interrupt controller
|
||||
pub interrupt_controller: Arc<Node>,
|
||||
/// Destination IRQ options
|
||||
pub irq: FullIrq,
|
||||
pub irq: IrqHandle,
|
||||
}
|
||||
|
||||
/// Represents a single PCI address (Requester ID for a MSI(-x))
|
||||
|
||||
@@ -204,6 +204,21 @@ impl<N: EventNotify> BitmapEvent<N> {
|
||||
self.notify.notify_all();
|
||||
}
|
||||
|
||||
pub async fn wait_mask(&self, mask: u64) -> u64 {
|
||||
let not_mask = !mask;
|
||||
poll_fn(|cx| {
|
||||
let state = self.value.fetch_and(not_mask, Ordering::AcqRel);
|
||||
if state & mask != 0 {
|
||||
self.notify.unsubscribe(cx.waker());
|
||||
Poll::Ready(state)
|
||||
} else {
|
||||
self.notify.subscribe(cx.waker());
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn wait(&self) -> u64 {
|
||||
poll_fn(|cx| {
|
||||
let state = self.value.swap(0, Ordering::AcqRel);
|
||||
|
||||
@@ -5,6 +5,7 @@ use object::KObject;
|
||||
use crate::vfs::NodeRef;
|
||||
|
||||
pub mod attribute;
|
||||
pub mod nodes;
|
||||
pub mod object;
|
||||
|
||||
static ROOT: OneTimeInit<NodeRef> = OneTimeInit::new();
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
|
||||
use alloc::{format, string::String, sync::Arc};
|
||||
use libk_util::OneTimeInit;
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::fs::sysfs::{
|
||||
attribute::{StringAttribute, StringAttributeOps},
|
||||
device,
|
||||
object::KObject,
|
||||
};
|
||||
|
||||
pub trait SysfsRtcNode: Send + Sync + 'static {
|
||||
fn read(&self) -> Result<u64, Error>;
|
||||
}
|
||||
|
||||
static RTC_NODES: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
pub fn add_rtc_node<N: SysfsRtcNode>(node: Arc<N>) {
|
||||
struct Time<N>(PhantomData<N>);
|
||||
|
||||
impl<N: SysfsRtcNode> StringAttributeOps for Time<N> {
|
||||
type Data = Arc<N>;
|
||||
const NAME: &'static str = "time";
|
||||
|
||||
fn read(state: &Self::Data) -> Result<String, Error> {
|
||||
let time = state.read()?;
|
||||
Ok(format!("{time}"))
|
||||
}
|
||||
}
|
||||
|
||||
static RTC_OBJECT: OneTimeInit<Arc<KObject<()>>> = OneTimeInit::new();
|
||||
let rtc_object = RTC_OBJECT.or_init_with(|| {
|
||||
let device_object = device().unwrap();
|
||||
let rtc_object = KObject::new(());
|
||||
device_object.add_object("rtc", rtc_object.clone()).ok();
|
||||
rtc_object
|
||||
});
|
||||
|
||||
let rtc_node = KObject::new(node);
|
||||
|
||||
rtc_node
|
||||
.add_attribute(StringAttribute::from(Time(PhantomData)))
|
||||
.ok();
|
||||
|
||||
let index = RTC_NODES.fetch_add(1, Ordering::AcqRel);
|
||||
let name = format!("rtc{index}");
|
||||
rtc_object.add_object(name, rtc_node).ok();
|
||||
}
|
||||
@@ -310,6 +310,7 @@ fn el0_sync_inner(frame: &mut ExceptionFrame) {
|
||||
}
|
||||
|
||||
fn irq_common() {
|
||||
// TODO some concept of "root" interrupt controller
|
||||
external_interrupt_controller()
|
||||
.unwrap()
|
||||
.handle_pending_irqs();
|
||||
|
||||
@@ -8,9 +8,9 @@ use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{
|
||||
ExternalInterruptController, FixedInterruptTable, FullIrq, InterruptHandler,
|
||||
InterruptTable, IpiDeliveryTarget, IpiMessage, Irq, IrqLevel, IrqOptions, IrqTrigger,
|
||||
IrqVector, LocalInterruptController,
|
||||
ExternalInterruptController, FixedInterruptTable, InterruptHandler, InterruptTable,
|
||||
IpiDeliveryTarget, IpiMessage, Irq, IrqHandle, IrqLevel, IrqOptions, IrqTrigger, IrqVector,
|
||||
LocalInterruptController,
|
||||
},
|
||||
};
|
||||
use device_tree::{
|
||||
@@ -23,7 +23,7 @@ use libk_mm::{
|
||||
address::PhysicalAddress,
|
||||
device::{DeviceMemoryIo, RawDeviceMemoryMapping},
|
||||
};
|
||||
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
|
||||
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
|
||||
|
||||
use self::{gicc::Gicc, gicd::Gicd};
|
||||
|
||||
@@ -41,13 +41,13 @@ pub mod gicv2m;
|
||||
|
||||
/// ARM Generic Interrupt Controller v2
|
||||
pub struct Gic {
|
||||
gicc: OneTimeInit<Gicc>,
|
||||
gicd: OneTimeInit<Gicd>,
|
||||
gicd_base: PhysicalAddress,
|
||||
gicc_base: PhysicalAddress,
|
||||
gicc: Gicc,
|
||||
gicd: Gicd,
|
||||
table: IrqSafeRwLock<FixedInterruptTable<MAX_IRQ>>,
|
||||
}
|
||||
|
||||
unsafe impl Sync for Gic {}
|
||||
|
||||
/// Per-CPU GIC information
|
||||
pub struct GicPerCpu {}
|
||||
|
||||
@@ -59,32 +59,8 @@ impl Device for Gic {
|
||||
}
|
||||
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
log::debug!(
|
||||
"Init GIC: gicd={:#x}, gicc={:#x}",
|
||||
self.gicd_base,
|
||||
self.gicc_base
|
||||
);
|
||||
let gicd_mmio = Arc::new(RawDeviceMemoryMapping::map(
|
||||
self.gicd_base.into_u64(),
|
||||
0x1000,
|
||||
Default::default(),
|
||||
)?);
|
||||
let gicd_mmio_shared = DeviceMemoryIo::from_raw(gicd_mmio.clone())?;
|
||||
let gicd_mmio_banked = DeviceMemoryIo::from_raw(gicd_mmio)?;
|
||||
let gicc_mmio = DeviceMemoryIo::map(self.gicc_base, Default::default())?;
|
||||
|
||||
let gicd = Gicd::new(gicd_mmio_shared, gicd_mmio_banked);
|
||||
let gicc = Gicc::new(gicc_mmio);
|
||||
|
||||
gicd.init();
|
||||
gicc.init();
|
||||
|
||||
self.gicd.init(gicd);
|
||||
self.gicc.init(gicc);
|
||||
|
||||
register_external_interrupt_controller(self.clone());
|
||||
AArch64::set_gic(self.clone());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -97,7 +73,6 @@ impl ExternalInterruptController for Gic {
|
||||
handler: Arc<dyn InterruptHandler>,
|
||||
) -> Result<(), Error> {
|
||||
let mut table = self.table.write();
|
||||
let gicd = self.gicd.get();
|
||||
|
||||
let index = match irq {
|
||||
Irq::External(i) => i + GIC_SPI_START,
|
||||
@@ -112,7 +87,7 @@ impl ExternalInterruptController for Gic {
|
||||
options.level
|
||||
);
|
||||
if index >= GIC_SPI_START as usize {
|
||||
gicd.configure_irq(index, options);
|
||||
self.gicd.configure_irq(index, options);
|
||||
}
|
||||
table.insert(index, handler)?;
|
||||
|
||||
@@ -120,29 +95,27 @@ impl ExternalInterruptController for Gic {
|
||||
}
|
||||
|
||||
fn enable_irq(&self, irq: Irq) -> Result<(), Error> {
|
||||
let gicd = self.gicd.get();
|
||||
let index = match irq {
|
||||
Irq::External(i) => i + GIC_SPI_START,
|
||||
Irq::Private(i) => i + GIC_PPI_START,
|
||||
} as usize;
|
||||
log::debug!("Enable irq{index} ({irq:?})");
|
||||
gicd.enable_irq(index);
|
||||
self.gicd.enable_irq(index);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_pending_irqs(&self) {
|
||||
let gicc = self.gicc.get();
|
||||
|
||||
let irq_number = gicc.pending_irq_number();
|
||||
let irq_number = self.gicc.pending_irq_number();
|
||||
if irq_number >= MAX_IRQ {
|
||||
return;
|
||||
}
|
||||
|
||||
gicc.clear_irq(irq_number);
|
||||
self.gicc.clear_irq(irq_number);
|
||||
|
||||
if irq_number == IPI_VECTOR as usize {
|
||||
// TODO pop entry
|
||||
crate::panic::panic_secondary();
|
||||
let ipi = Cpu::local().get_ipi();
|
||||
AArch64::handle_ipi(ipi);
|
||||
return;
|
||||
}
|
||||
|
||||
if irq_number < GIC_PPI_START as usize {
|
||||
@@ -190,20 +163,20 @@ impl LocalInterruptController for Gic {
|
||||
barrier::isb(barrier::SY);
|
||||
|
||||
unsafe {
|
||||
self.gicd.get().set_sgir(target, IPI_VECTOR);
|
||||
self.gicd.set_sgir(target, IPI_VECTOR);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn init_ap(&self) -> Result<(), Error> {
|
||||
self.gicc.get().init();
|
||||
self.gicc.init();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceTreeInterruptController for Gic {
|
||||
fn map_interrupt(&self, property: &TProp, offset: usize) -> Option<FullIrq> {
|
||||
fn map_interrupt(self: Arc<Self>, property: &TProp, offset: usize) -> Option<IrqHandle> {
|
||||
// IRQ_TYPE_NONE - 0
|
||||
// IRQ_TYPE_EDGE_RISING - 1
|
||||
// IRQ_TYPE_EDGE_FALLING - 2
|
||||
@@ -229,9 +202,10 @@ impl DeviceTreeInterruptController for Gic {
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(FullIrq {
|
||||
Some(IrqHandle {
|
||||
irq,
|
||||
options: IrqOptions { trigger, level },
|
||||
intc: self.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -246,14 +220,34 @@ impl Gic {
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure the addresses actually point to the GIC components.
|
||||
pub unsafe fn new(gicd_base: PhysicalAddress, gicc_base: PhysicalAddress) -> Self {
|
||||
Self {
|
||||
gicc: OneTimeInit::new(),
|
||||
gicd: OneTimeInit::new(),
|
||||
gicd_base,
|
||||
gicc_base,
|
||||
pub unsafe fn new(
|
||||
gicd_base: PhysicalAddress,
|
||||
gicc_base: PhysicalAddress,
|
||||
) -> Result<Self, Error> {
|
||||
log::debug!("Init GIC: gicd={:#x}, gicc={:#x}", gicd_base, gicc_base);
|
||||
let gicd_mmio = Arc::new(RawDeviceMemoryMapping::map(
|
||||
gicd_base.into_u64(),
|
||||
0x1000,
|
||||
Default::default(),
|
||||
)?);
|
||||
let gicd_mmio_shared = DeviceMemoryIo::from_raw(gicd_mmio.clone())?;
|
||||
let gicd_mmio_banked = DeviceMemoryIo::from_raw(gicd_mmio)?;
|
||||
let gicc_mmio = DeviceMemoryIo::map(gicc_base, Default::default())?;
|
||||
|
||||
let gicd = Gicd::new(gicd_mmio_shared, gicd_mmio_banked);
|
||||
let gicc = Gicc::new(gicc_mmio);
|
||||
|
||||
gicd.init();
|
||||
gicc.init();
|
||||
|
||||
// self.gicd.init(gicd);
|
||||
// self.gicc.init(gicc);
|
||||
|
||||
Ok(Self {
|
||||
gicd,
|
||||
gicc,
|
||||
table: IrqSafeRwLock::new(FixedInterruptTable::new()),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,7 +268,11 @@ device_tree_driver! {
|
||||
let gicd_base = PhysicalAddress::from_u64(gicd_range.start);
|
||||
let gicc_base = PhysicalAddress::from_u64(gicc_range.start);
|
||||
|
||||
let gic = Arc::new(unsafe { Gic::new(gicd_base, gicc_base) });
|
||||
let gic = Arc::new(unsafe {
|
||||
Gic::new(gicd_base, gicc_base)
|
||||
.inspect_err(|e| log::error!("GIC probe/init error: {e:?}"))
|
||||
.ok()?
|
||||
});
|
||||
|
||||
// Register device-tree interrupt controller
|
||||
node.make_interrupt_controller(gic.clone());
|
||||
|
||||
@@ -5,14 +5,15 @@ use core::sync::atomic::{self, Ordering};
|
||||
use aarch64_cpu::registers::{CNTP_CTL_EL0, CNTP_TVAL_EL0};
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
interrupt::{Irq, LocalInterruptController},
|
||||
interrupt::{IpiDeliveryTarget, IpiMessage, Irq, LocalInterruptController},
|
||||
ResetDevice,
|
||||
};
|
||||
use device_tree::{
|
||||
driver::{unflatten_device_tree, InitSequence},
|
||||
DeviceTree, DeviceTreeNodeExt,
|
||||
};
|
||||
use kernel_arch_aarch64::{mem, ArchitectureImpl, PerCpuData};
|
||||
use kernel_arch::Architecture;
|
||||
use kernel_arch_aarch64::{mem, ArchitectureImpl, PerCpuData, CPU_COUNT};
|
||||
use libk::{arch::Cpu, config, debug, device::external_interrupt_controller, error::Error};
|
||||
use libk_mm::{
|
||||
address::PhysicalAddress,
|
||||
@@ -20,7 +21,7 @@ use libk_mm::{
|
||||
pointer::PhysicalRef,
|
||||
table::EntryLevelExt,
|
||||
};
|
||||
use libk_util::OneTimeInit;
|
||||
use libk_util::{sync::SpinFence, OneTimeInit};
|
||||
use tock_registers::interfaces::Writeable;
|
||||
use ygg_driver_pci::PciBusManager;
|
||||
|
||||
@@ -28,6 +29,7 @@ use crate::{
|
||||
arch::{aarch64::gic::Gic, Platform},
|
||||
device::{power::arm_psci::Psci, MACHINE_NAME},
|
||||
fs::{Initrd, INITRD_DATA},
|
||||
panic,
|
||||
util::call_init_array,
|
||||
};
|
||||
|
||||
@@ -68,6 +70,8 @@ impl<const SIZE: usize> BootStack<SIZE> {
|
||||
}
|
||||
}
|
||||
|
||||
static SHUTDOWN_FENCE: SpinFence = SpinFence::new();
|
||||
|
||||
impl Platform for AArch64 {
|
||||
unsafe fn start_application_processors(&self) {
|
||||
if let Some(compatible) = self.machine_compatible.try_get() {
|
||||
@@ -96,6 +100,18 @@ impl Platform for AArch64 {
|
||||
psci.reset()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn power_off(&self) -> Result<!, Error> {
|
||||
Self::halt_aps()?;
|
||||
|
||||
if let Some(psci) = self.psci.try_get() {
|
||||
log::info!("Powering off");
|
||||
psci.power_off()
|
||||
} else {
|
||||
log::warn!("No power off method, halting");
|
||||
ArchitectureImpl::halt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AArch64 {
|
||||
@@ -103,6 +119,41 @@ impl AArch64 {
|
||||
GIC.init(gic);
|
||||
}
|
||||
|
||||
fn handle_ipi(ipi: Option<IpiMessage>) {
|
||||
let Some(ipi) = ipi else {
|
||||
log::warn!("Spurious IPI received, assuming panic");
|
||||
panic::panic_secondary();
|
||||
};
|
||||
|
||||
match ipi {
|
||||
IpiMessage::Panic => panic::panic_secondary(),
|
||||
IpiMessage::Shutdown => Self::secondary_halt_handler(),
|
||||
}
|
||||
}
|
||||
|
||||
fn secondary_halt_handler() {
|
||||
log::info!("CPU halted");
|
||||
SHUTDOWN_FENCE.signal();
|
||||
ArchitectureImpl::halt();
|
||||
}
|
||||
|
||||
unsafe fn halt_aps() -> Result<(), Error> {
|
||||
let ap_count = CPU_COUNT.load(Ordering::Acquire) - 1;
|
||||
|
||||
if ap_count > 0 {
|
||||
if let Some(gic) = GIC.try_get() {
|
||||
gic.send_ipi(IpiDeliveryTarget::OtherCpus, IpiMessage::Shutdown)?;
|
||||
SHUTDOWN_FENCE.wait_all(ap_count);
|
||||
Ok(())
|
||||
} else {
|
||||
log::warn!("No way to handle secondary CPUs");
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
unsafe fn init_memory_management(
|
||||
&'static self,
|
||||
|
||||
@@ -7,16 +7,16 @@ use abi::{error::Error, time::NANOSECONDS_IN_SECOND};
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{FullIrq, InterruptHandler, IrqVector},
|
||||
interrupt::{InterruptHandler, IrqHandle, IrqVector},
|
||||
};
|
||||
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
|
||||
use kernel_arch::task::Scheduler;
|
||||
use libk::{arch::Cpu, device::external_interrupt_controller, task::runtime, time};
|
||||
use libk::{arch::Cpu, task::runtime, time};
|
||||
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
|
||||
|
||||
/// ARM Generic Timer driver
|
||||
pub struct ArmTimer {
|
||||
irq: FullIrq,
|
||||
irq: IrqHandle,
|
||||
}
|
||||
|
||||
/// ARM timer tick interval (in some time units?)
|
||||
@@ -63,14 +63,12 @@ impl Device for ArmTimer {
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
log::info!("ARM Generic Timer frequency={}Hz", CNTFRQ_EL0.get());
|
||||
|
||||
let intc = external_interrupt_controller()?;
|
||||
|
||||
intc.register_irq(self.irq.irq, self.irq.options, self.clone())?;
|
||||
self.irq.register(self.clone())?;
|
||||
|
||||
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::IMASK::CLEAR);
|
||||
CNTP_TVAL_EL0.set(TICK_INTERVAL);
|
||||
|
||||
intc.enable_irq(self.irq.irq)?;
|
||||
self.irq.enable()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -82,7 +80,7 @@ impl ArmTimer {
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure the function has not been called before.
|
||||
pub const unsafe fn new(irq: FullIrq) -> Self {
|
||||
pub const unsafe fn new(irq: IrqHandle) -> Self {
|
||||
Self { irq }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Provides architecture/platform-specific implementation details
|
||||
|
||||
use abi::error::Error;
|
||||
use kernel_arch::{Architecture, ArchitectureImpl};
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||
@@ -51,6 +52,19 @@ pub trait Platform {
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs full system powerdown.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure it is actually safe to power down, i.e. no critical processes will be
|
||||
/// aborted and no data will be lost.
|
||||
unsafe fn power_off(&self) -> Result<!, Error> {
|
||||
ArchitectureImpl::set_interrupt_mask(true);
|
||||
loop {
|
||||
ArchitectureImpl::wait_for_interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
// /// Adds a reset device to the system
|
||||
// fn register_reset_device(&self, reset: Arc<dyn ResetDevice>) -> Result<(), Error> {
|
||||
// Err(Error::NotImplemented)
|
||||
|
||||
@@ -7,7 +7,7 @@ use core::{
|
||||
|
||||
use elf::{abi, relocation::Elf64_Rela};
|
||||
use kernel_arch::{Architecture, ArchitectureImpl};
|
||||
use kernel_arch_riscv64::{mem, CPU_COUNT};
|
||||
use kernel_arch_riscv64::{mem, BOOT_HART_ID, CPU_COUNT};
|
||||
use libk::{
|
||||
debug,
|
||||
fs::{devfs, sysfs},
|
||||
@@ -28,7 +28,6 @@ const BOOT_STACK_SIZE: usize = 65536;
|
||||
|
||||
static mut KERNEL_LOAD_BASE: u64 = 0;
|
||||
static mut DTB_ADDRESS: PhysicalAddress = PhysicalAddress::ZERO;
|
||||
static mut BOOT_HART_ID: u64 = 0;
|
||||
static mut BOOT_STACK: BootStack<BOOT_STACK_SIZE> = BootStack::zeroed();
|
||||
|
||||
#[repr(C, align(0x10))]
|
||||
@@ -58,11 +57,6 @@ unsafe extern "C" fn relocate_kernel(image_base: i64, rela_start: usize, rela_en
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the ID of the bootstrap HART
|
||||
pub fn boot_hart_id() -> u64 {
|
||||
unsafe { BOOT_HART_ID }
|
||||
}
|
||||
|
||||
unsafe fn long_jump(pc: usize, sp: usize, a0: usize) -> ! {
|
||||
core::arch::asm!("mv sp, {sp}; jr {pc}", in("a0") a0, sp = in(reg) sp, pc = in(reg) pc, options(noreturn))
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use kernel_arch::{Architecture, ArchitectureImpl};
|
||||
use kernel_arch_riscv64::{
|
||||
mem,
|
||||
registers::{SIE, SSTATUS},
|
||||
PerCpuData,
|
||||
sbi, PerCpuData,
|
||||
};
|
||||
use libk::{arch::Cpu, config};
|
||||
use libk_mm::{
|
||||
@@ -51,6 +51,10 @@ impl Platform for Riscv64 {
|
||||
ArchitectureImpl::halt();
|
||||
}
|
||||
|
||||
unsafe fn power_off(&self) -> Result<!, Error> {
|
||||
sbi::sbi_system_shutdown()
|
||||
}
|
||||
|
||||
unsafe fn start_application_processors(&self) {
|
||||
// TODO asymmetric systems with different hart types are not yet supported.
|
||||
// e.g., in JH7110 there're two different types of cores
|
||||
|
||||
@@ -6,7 +6,7 @@ use abi::error::Error;
|
||||
use device_api::interrupt::{IpiDeliveryTarget, IpiMessage};
|
||||
use device_tree::{DeviceTree, DeviceTreeNodeExt};
|
||||
use kernel_arch_riscv64::{
|
||||
mem::auto_lower_address, registers::SIP, sbi, ArchitectureImpl, CPU_COUNT,
|
||||
boot_hart_id, mem::auto_lower_address, registers::SIP, sbi, ArchitectureImpl, CPU_COUNT,
|
||||
};
|
||||
use libk::arch::Cpu;
|
||||
use libk_mm::{
|
||||
@@ -15,7 +15,7 @@ use libk_mm::{
|
||||
};
|
||||
use tock_registers::interfaces::ReadWriteable;
|
||||
|
||||
use crate::{arch::riscv64::boot::boot_hart_id, panic};
|
||||
use crate::panic;
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub const SECONDARY_STACK_SIZE: usize = 32768;
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
use abi::error::Error;
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
device::Device,
|
||||
interrupt::{InterruptHandler, Irq, IrqVector},
|
||||
};
|
||||
use device_api::device::Device;
|
||||
use kernel_arch::{Architecture, ArchitectureImpl};
|
||||
use kernel_arch_x86::{
|
||||
intrinsics::{io_wait, IoPort, IoPortAccess},
|
||||
ISA_IRQ_OFFSET,
|
||||
};
|
||||
use libk::{device::external_interrupt_controller, time};
|
||||
use kernel_arch_x86::intrinsics::{io_wait, IoPort, IoPortAccess};
|
||||
use libk::fs::sysfs::{self, nodes::SysfsRtcNode};
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
|
||||
const NMI_DISABLE: u8 = 1 << 7;
|
||||
const CMOS_REG_SEC: u8 = 0x00;
|
||||
const CMOS_REG_MIN: u8 = 0x02;
|
||||
const CMOS_REG_HOUR: u8 = 0x04;
|
||||
@@ -20,13 +13,8 @@ const CMOS_REG_DAY: u8 = 0x07;
|
||||
const CMOS_REG_MON: u8 = 0x08;
|
||||
const CMOS_REG_YEAR: u8 = 0x09;
|
||||
const CMOS_REG_STATUS_A: u8 = 0x0A;
|
||||
const CMOS_REG_STATUS_B: u8 = 0x0B;
|
||||
const CMOS_REG_STATUS_C: u8 = 0x0C;
|
||||
const STATUS_A_UPDATE_IN_PROGRESS: u8 = 1 << 7;
|
||||
|
||||
// Refresh every 4 ticks (every 2s)
|
||||
const REFRESH_INTERVAL: u32 = 4;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
struct DateTime {
|
||||
seconds: u8,
|
||||
@@ -40,14 +28,18 @@ struct DateTime {
|
||||
struct Inner {
|
||||
command: IoPort<u8>,
|
||||
data: IoPort<u8>,
|
||||
counter: u32,
|
||||
last_timestamp: Option<DateTime>,
|
||||
}
|
||||
|
||||
pub struct Rtc {
|
||||
inner: IrqSafeSpinlock<Inner>,
|
||||
}
|
||||
|
||||
fn bcd_to_dec(x: u8) -> u8 {
|
||||
let a = (x >> 4) & 0xF;
|
||||
let b = x & 0xF;
|
||||
a * 10 + b
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
fn read_date_time(&mut self) -> DateTime {
|
||||
self.wait_for_update();
|
||||
@@ -71,12 +63,12 @@ impl Inner {
|
||||
}
|
||||
|
||||
fn try_read_time(&mut self) -> DateTime {
|
||||
let seconds = self.read_reg(CMOS_REG_SEC);
|
||||
let minutes = self.read_reg(CMOS_REG_MIN);
|
||||
let hours = self.read_reg(CMOS_REG_HOUR);
|
||||
let day_of_month = self.read_reg(CMOS_REG_DAY);
|
||||
let month = self.read_reg(CMOS_REG_MON);
|
||||
let year = self.read_reg(CMOS_REG_YEAR);
|
||||
let seconds = bcd_to_dec(self.read_reg(CMOS_REG_SEC));
|
||||
let minutes = bcd_to_dec(self.read_reg(CMOS_REG_MIN));
|
||||
let hours = bcd_to_dec(self.read_reg(CMOS_REG_HOUR));
|
||||
let day_of_month = bcd_to_dec(self.read_reg(CMOS_REG_DAY));
|
||||
let month = bcd_to_dec(self.read_reg(CMOS_REG_MON));
|
||||
let year = bcd_to_dec(self.read_reg(CMOS_REG_YEAR));
|
||||
DateTime {
|
||||
seconds,
|
||||
minutes,
|
||||
@@ -99,59 +91,16 @@ impl Inner {
|
||||
}
|
||||
value
|
||||
}
|
||||
|
||||
fn write_reg(&mut self, reg: u8, value: u8) {
|
||||
assert!(ArchitectureImpl::interrupt_mask());
|
||||
self.command.write(reg);
|
||||
for _ in 0..10 {
|
||||
io_wait();
|
||||
}
|
||||
self.data.write(value);
|
||||
for _ in 0..10 {
|
||||
io_wait();
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_irq(&mut self) {
|
||||
let rate = 15; // freq = 2Hz
|
||||
let old_a = self.read_reg(CMOS_REG_STATUS_A | NMI_DISABLE);
|
||||
let old_b = self.read_reg(CMOS_REG_STATUS_B | NMI_DISABLE);
|
||||
let new_a = (old_a & 0xF0) | rate;
|
||||
let new_b = old_b | (1 << 6) | (1 << 2) | (1 << 1);
|
||||
self.write_reg(CMOS_REG_STATUS_A | NMI_DISABLE, new_a);
|
||||
self.write_reg(CMOS_REG_STATUS_B | NMI_DISABLE, new_b);
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptHandler for Rtc {
|
||||
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||
let mut inner = self.inner.lock();
|
||||
if inner.counter == 0 {
|
||||
let time = inner.read_date_time();
|
||||
inner.last_timestamp = Some(time);
|
||||
|
||||
time::set_real_seconds(time.to_seconds(), true);
|
||||
}
|
||||
inner.read_reg(CMOS_REG_STATUS_C);
|
||||
|
||||
inner.counter += 1;
|
||||
if inner.counter == REFRESH_INTERVAL {
|
||||
inner.counter = 0;
|
||||
}
|
||||
true
|
||||
impl SysfsRtcNode for Rtc {
|
||||
fn read(&self) -> Result<u64, Error> {
|
||||
let time = self.inner.lock().read_date_time();
|
||||
Ok(time.to_seconds())
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Rtc {
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
let irq = Irq::External(ISA_IRQ_OFFSET + 8);
|
||||
let intc = external_interrupt_controller()?;
|
||||
self.inner.lock().setup_irq();
|
||||
intc.register_irq(irq, Default::default(), self.clone())?;
|
||||
intc.enable_irq(irq)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
"x86 RTC"
|
||||
}
|
||||
@@ -163,15 +112,13 @@ impl Rtc {
|
||||
inner: IrqSafeSpinlock::new(Inner {
|
||||
command: IoPort::new(0x70),
|
||||
data: IoPort::new(0x71),
|
||||
counter: 0,
|
||||
last_timestamp: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup() -> Result<Arc<Self>, Error> {
|
||||
let this = Arc::new(Self::new());
|
||||
unsafe { this.clone().init_irq() }?;
|
||||
sysfs::nodes::add_rtc_node(this.clone());
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ use libk::{
|
||||
};
|
||||
use static_assertions::{const_assert, const_assert_eq};
|
||||
|
||||
use crate::arch::x86_64::X86_64;
|
||||
|
||||
use super::exception;
|
||||
|
||||
pub mod ioapic;
|
||||
@@ -115,8 +117,8 @@ unsafe extern "C" fn dummy_irq_handler() {
|
||||
}
|
||||
|
||||
unsafe extern "C" fn ipi_handler() {
|
||||
let cpu = Cpu::local();
|
||||
todo!("Processor {} received an IPI", cpu.id());
|
||||
let ipi = Cpu::local().get_ipi();
|
||||
X86_64::handle_ipi(ipi);
|
||||
}
|
||||
|
||||
global_asm!(
|
||||
|
||||
@@ -4,12 +4,12 @@ use core::{arch::global_asm, mem::size_of};
|
||||
use abi::{bitflags, primitive_enum, process::Signal};
|
||||
use kernel_arch_x86::registers::{CR2, CR3};
|
||||
use kernel_arch_x86_64::context::ExceptionFrame;
|
||||
use libk::task::thread::Thread;
|
||||
use libk::{arch::Cpu, task::thread::Thread};
|
||||
use libk_mm::PageFaultKind;
|
||||
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
|
||||
use tock_registers::interfaces::Readable;
|
||||
|
||||
use crate::arch::x86_64::apic;
|
||||
use crate::arch::x86_64::{apic, X86_64};
|
||||
|
||||
primitive_enum! {
|
||||
enum ExceptionKind: u64 {
|
||||
@@ -231,7 +231,13 @@ extern "C" fn __x86_64_exception_handler(frame: *mut ExceptionFrame) {
|
||||
}
|
||||
|
||||
extern "C" fn __x86_64_nmi_handler() -> ! {
|
||||
crate::panic::panic_secondary();
|
||||
let ipi = Cpu::local().get_ipi();
|
||||
if let Some(ipi) = ipi {
|
||||
X86_64::handle_ipi(Some(ipi));
|
||||
unreachable!();
|
||||
} else {
|
||||
panic!("Spurious/unknown NMI received");
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the interrupt descriptor table for the given CPU.
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
//! x86-64 architecture implementation
|
||||
|
||||
use core::ptr::null_mut;
|
||||
use core::{ptr::null_mut, sync::atomic::Ordering};
|
||||
|
||||
use abi::error::Error;
|
||||
use acpi::{mcfg::Mcfg, AcpiTables, HpetInfo, InterruptModel};
|
||||
use alloc::sync::Arc;
|
||||
use device_api::device::Device;
|
||||
use device_api::{
|
||||
device::Device,
|
||||
interrupt::{IpiDeliveryTarget, IpiMessage},
|
||||
};
|
||||
use kernel_arch::{Architecture, ArchitectureImpl};
|
||||
use kernel_arch_x86::{
|
||||
cpuid::{self, CpuFeatures, EcxFeatures, EdxFeatures, ExtEdxFeatures},
|
||||
gdt,
|
||||
};
|
||||
use kernel_arch_x86_64::{mem, LocalApicInterface, PerCpuData};
|
||||
use kernel_arch_x86_64::{mem, LocalApicInterface, PerCpuData, CPU_COUNT};
|
||||
use libk::{
|
||||
arch::Cpu,
|
||||
config, debug,
|
||||
@@ -27,7 +31,10 @@ use libk_mm::{
|
||||
pointer::PhysicalRef,
|
||||
table::EntryLevel,
|
||||
};
|
||||
use libk_util::OneTimeInit;
|
||||
use libk_util::{
|
||||
sync::{IrqGuard, SpinFence},
|
||||
OneTimeInit,
|
||||
};
|
||||
use yboot_proto::{
|
||||
v1::{self, AvailableMemoryRegion},
|
||||
LoadProtocolV1,
|
||||
@@ -45,6 +52,7 @@ use crate::{
|
||||
Platform,
|
||||
},
|
||||
device::display::linear_fb::LinearFramebuffer,
|
||||
panic,
|
||||
util::call_init_array,
|
||||
};
|
||||
|
||||
@@ -72,7 +80,9 @@ pub static PLATFORM: X86_64 = X86_64 {
|
||||
|
||||
fbconsole: OneTimeInit::new(),
|
||||
};
|
||||
//
|
||||
|
||||
static SHUTDOWN_FENCE: SpinFence = SpinFence::new();
|
||||
|
||||
impl Platform for X86_64 {
|
||||
unsafe fn start_application_processors(&self) {
|
||||
if let Some(acpi) = self.acpi.try_get() {
|
||||
@@ -87,9 +97,42 @@ impl Platform for X86_64 {
|
||||
smp::start_ap_cores(&pinfo);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn power_off(&self) -> Result<!, Error> {
|
||||
let _guard = IrqGuard::acquire();
|
||||
|
||||
let ap_count = CPU_COUNT.load(Ordering::Acquire) - 1;
|
||||
if ap_count > 0 {
|
||||
self.send_ipi(IpiDeliveryTarget::OtherCpus, IpiMessage::Shutdown)?;
|
||||
SHUTDOWN_FENCE.wait_all(ap_count);
|
||||
}
|
||||
|
||||
ygg_driver_acpi::power_off()
|
||||
}
|
||||
}
|
||||
|
||||
impl X86_64 {
|
||||
fn handle_ipi(ipi: Option<IpiMessage>) {
|
||||
let Some(ipi) = ipi else {
|
||||
log::warn!("Spurious IPI received, assuming panic");
|
||||
panic::panic_secondary();
|
||||
};
|
||||
|
||||
match ipi {
|
||||
IpiMessage::Shutdown => {
|
||||
log::info!("Core halted");
|
||||
SHUTDOWN_FENCE.signal();
|
||||
ArchitectureImpl::halt()
|
||||
}
|
||||
IpiMessage::Panic => panic::panic_secondary(),
|
||||
}
|
||||
}
|
||||
|
||||
fn send_ipi(&self, target: IpiDeliveryTarget, message: IpiMessage) -> Result<(), Error> {
|
||||
let cpu = Cpu::local();
|
||||
cpu.local_apic.send_ipi(target, message)
|
||||
}
|
||||
|
||||
fn set_boot_data(&self, data: BootData) {
|
||||
match data {
|
||||
BootData::YBoot(data) => {
|
||||
@@ -196,6 +239,12 @@ impl X86_64 {
|
||||
call_init_array();
|
||||
|
||||
x86::init_platform_devices(early);
|
||||
|
||||
extern "C" {
|
||||
static __kernel_start: u8;
|
||||
}
|
||||
|
||||
log::info!("Load base: {:p}", &raw const __kernel_start);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
//! Clock controller drivers
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||
pub mod bcm2835_aux;
|
||||
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod jh7110_clocks;
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod fixed;
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
//! Interrupt controller drivers
|
||||
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod riscv_plic;
|
||||
@@ -1,4 +0,0 @@
|
||||
//! Mailbox interfaces
|
||||
|
||||
#[cfg(any(rust_analyzer, target_arch = "aarch64"))]
|
||||
mod bcm2835_mbox;
|
||||
@@ -6,13 +6,12 @@ use libk_util::OneTimeInit;
|
||||
pub mod bus;
|
||||
pub mod clock;
|
||||
pub mod display;
|
||||
pub mod gpio;
|
||||
pub mod interrupt;
|
||||
pub mod mbox;
|
||||
pub mod pinctrl;
|
||||
pub mod power;
|
||||
pub mod serial;
|
||||
// pub mod timer;
|
||||
|
||||
#[cfg(any(rust_analyzer, not(target_arch = "x86_64")))]
|
||||
pub mod gpio;
|
||||
|
||||
/// Generic machine description string
|
||||
pub static MACHINE_NAME: OneTimeInit<String> = OneTimeInit::new();
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
//! GPIO/Pin controller, multiplexer drivers
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||
mod bcm2711_gpio;
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
mod jh7110_pinctrl;
|
||||
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||
mod pl061;
|
||||
@@ -61,6 +61,7 @@ impl ResetDevice for Psci {
|
||||
|
||||
impl Psci {
|
||||
const SYSTEM_RESET: u32 = 0x84000009;
|
||||
const SYSTEM_OFF: u32 = 0x84000008;
|
||||
|
||||
#[inline]
|
||||
unsafe fn call(&self, mut x0: u64, x1: u64, x2: u64, x3: u64) -> u64 {
|
||||
@@ -74,6 +75,12 @@ impl Psci {
|
||||
}
|
||||
x0
|
||||
}
|
||||
|
||||
/// Shut down the system
|
||||
pub unsafe fn power_off(&self) -> Result<!, Error> {
|
||||
self.call(Self::SYSTEM_OFF as _, 0, 0, 0);
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
//! Serial device interfaces
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||
pub mod bcm2835_aux_uart;
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||
pub mod pl011;
|
||||
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod ns16550a;
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
|
||||
@@ -3,12 +3,12 @@ use abi::{error::Error, io::TerminalOptions};
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{FullIrq, InterruptHandler, IrqVector},
|
||||
interrupt::{InterruptHandler, IrqHandle, IrqVector},
|
||||
};
|
||||
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
|
||||
use libk::{
|
||||
debug::DebugSink,
|
||||
device::{external_interrupt_controller, manager::DEVICE_REGISTRY},
|
||||
device::manager::DEVICE_REGISTRY,
|
||||
vfs::{Terminal, TerminalInput, TerminalOutput},
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
||||
@@ -79,7 +79,7 @@ struct Inner {
|
||||
pub struct Ns16550a {
|
||||
inner: OneTimeInit<Arc<Terminal<Inner>>>,
|
||||
base: PhysicalAddress,
|
||||
irq: FullIrq,
|
||||
irq: IrqHandle,
|
||||
}
|
||||
|
||||
impl Io {
|
||||
@@ -135,9 +135,8 @@ impl Device for Ns16550a {
|
||||
}
|
||||
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
let intc = external_interrupt_controller()?;
|
||||
intc.register_irq(self.irq.irq, self.irq.options, self.clone())?;
|
||||
intc.enable_irq(self.irq.irq)?;
|
||||
self.irq.register(self.clone())?;
|
||||
self.irq.enable()?;
|
||||
let io = self.inner.get().output().io.lock();
|
||||
io.regs.IER.modify(IER::RDR::SET);
|
||||
Ok(())
|
||||
|
||||
@@ -4,12 +4,12 @@ use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::{ClockHandle, Hertz, ResetHandle},
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{FullIrq, InterruptHandler, IrqVector},
|
||||
interrupt::{InterruptHandler, IrqHandle, IrqVector},
|
||||
};
|
||||
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
|
||||
use libk::{
|
||||
debug::{self, DebugSink},
|
||||
device::{external_interrupt_controller, manager::DEVICE_REGISTRY},
|
||||
device::manager::DEVICE_REGISTRY,
|
||||
vfs::{Terminal, TerminalInput, TerminalOutput},
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
||||
@@ -110,7 +110,7 @@ struct Inner {
|
||||
/// Synopsys DesignWare 8250 UART
|
||||
pub struct DwUart {
|
||||
base: PhysicalAddress,
|
||||
irq: FullIrq,
|
||||
irq: IrqHandle,
|
||||
clk_baud: ClockHandle,
|
||||
#[allow(unused)]
|
||||
clk_apb: Option<ClockHandle>,
|
||||
@@ -241,9 +241,8 @@ impl Device for DwUart {
|
||||
}
|
||||
|
||||
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
|
||||
let intc = external_interrupt_controller()?;
|
||||
intc.register_irq(self.irq.irq, Default::default(), self.clone())?;
|
||||
intc.enable_irq(self.irq.irq)?;
|
||||
self.irq.register(self.clone())?;
|
||||
self.irq.enable()?;
|
||||
|
||||
let output = self.inner.get().output();
|
||||
let io = output.io.lock();
|
||||
|
||||
@@ -87,6 +87,8 @@ pub fn kinit() -> Result<(), Error> {
|
||||
|
||||
random::init();
|
||||
|
||||
loop {}
|
||||
|
||||
let root = setup_root().inspect_err(|error| {
|
||||
log::error!("Cannot setup root filesystem: {error:?}");
|
||||
})?;
|
||||
|
||||
@@ -74,7 +74,14 @@ extern crate ygg_driver_virtio_net;
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_arch = "x86_64")] {
|
||||
extern crate ygg_driver_net_igbe;
|
||||
} else if #[cfg(target_arch = "aarch64")] {
|
||||
extern crate ygg_driver_bsp_arm;
|
||||
extern crate ygg_driver_bsp_bcm283x;
|
||||
extern crate ygg_driver_usb_dwc2;
|
||||
} else if #[cfg(target_arch = "riscv64")] {
|
||||
extern crate ygg_driver_bsp_riscv;
|
||||
extern crate ygg_driver_bsp_jh7110;
|
||||
|
||||
extern crate ygg_driver_net_stmmac;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ use abi::{
|
||||
option::OptionValue,
|
||||
path::Path,
|
||||
process::{ExecveOptions, ProcessId},
|
||||
system::{self, SystemInfoVariant},
|
||||
system::{self, SystemControlVariant, SystemInfoVariant},
|
||||
time::{ClockType, SystemTime},
|
||||
};
|
||||
use libk::{
|
||||
@@ -26,7 +26,10 @@ use libk::{
|
||||
};
|
||||
use libk_mm::phys;
|
||||
|
||||
use crate::fs;
|
||||
use crate::{
|
||||
arch::{Platform, PLATFORM},
|
||||
fs,
|
||||
};
|
||||
|
||||
use super::run_with_io;
|
||||
|
||||
@@ -86,6 +89,16 @@ pub(crate) fn set_clock(ty: ClockType, value: &SystemTime) -> Result<(), Error>
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn system_control(option: u32, value: &mut [u8], size: usize) -> Result<usize, Error> {
|
||||
let _ = (value, size);
|
||||
let option = SystemControlVariant::try_from(option)?;
|
||||
match option {
|
||||
SystemControlVariant::PowerOff => {
|
||||
unsafe { PLATFORM.power_off() }?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_system_info(option: u32, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
let option = SystemInfoVariant::try_from(option)?;
|
||||
match option {
|
||||
|
||||
@@ -47,6 +47,8 @@ struct SpawnOptions<'a> {
|
||||
pub environment: &'a [&'a str],
|
||||
/// Working directory
|
||||
pub directory: Option<&'a str>,
|
||||
/// Root path
|
||||
pub root: Option<&'a str>,
|
||||
/// Optional arguments to specify details of the creation
|
||||
pub optional: &'a [SpawnOption],
|
||||
/// Extra flags controlling the new process behavior
|
||||
|
||||
@@ -80,6 +80,7 @@ syscall unmount(opts: &UnmountOptions) -> Result<()>;
|
||||
syscall load_module(path: &str) -> Result<()>;
|
||||
|
||||
syscall filesystem_control(fd: Option<RawFd>, option: u32, value: &mut [u8], len: usize) -> Result<usize>;
|
||||
syscall system_control(option: u32, value: &mut [u8], len: usize) -> Result<usize>;
|
||||
|
||||
syscall get_system_info(option: u32, value: &mut [u8]) -> Result<usize>;
|
||||
|
||||
|
||||
@@ -21,6 +21,14 @@ option_group!(
|
||||
}
|
||||
);
|
||||
|
||||
request_group!(
|
||||
#[doc = "Common system controls"]
|
||||
pub enum SystemControlVariant<'de> {
|
||||
#[doc = "Power down the system"]
|
||||
0x1000: PowerOff((), ()),
|
||||
}
|
||||
);
|
||||
|
||||
abi_serde::impl_struct_serde!(
|
||||
SystemMemoryStats: [total_usable_pages, allocated_pages, free_pages, page_size]
|
||||
);
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
//! System-related parameters
|
||||
|
||||
pub use abi::system::*;
|
||||
use abi::{error::Error, option::OptionValue};
|
||||
use abi::{
|
||||
error::Error,
|
||||
option::{OptionValue, RequestValue},
|
||||
};
|
||||
|
||||
/// Helper macro for [get_system_info]
|
||||
pub macro get_system_info($variant_ty:ty) {{
|
||||
@@ -14,3 +17,13 @@ pub fn get_system_info<'de, T: OptionValue<'de>>(buffer: &'de mut [u8]) -> Resul
|
||||
let len = unsafe { crate::sys::get_system_info(T::VARIANT.into(), buffer) }?;
|
||||
T::load(&buffer[..len])
|
||||
}
|
||||
|
||||
/// Performs an operation on the system, like power management, reset etc
|
||||
pub fn system_control<'de, T: RequestValue<'de>>(
|
||||
buffer: &'de mut [u8],
|
||||
request: &T::Request,
|
||||
) -> Result<T::Response, Error> {
|
||||
let len = T::store_request(request, buffer)?;
|
||||
let len = unsafe { crate::sys::system_control(T::VARIANT.into(), buffer, len) }?;
|
||||
T::load_response(&buffer[..len])
|
||||
}
|
||||
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
build_dir=$3
|
||||
|
||||
cd "$build_dir/doomgeneric/doomgeneric"
|
||||
make -f Makefile.yggdrasil SYSROOT=$Y_SYSROOT TARGET=$Y_TRIPLE
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
REPO_URL="https://git.alnyan.me/yggdrasil/doomgeneric.git"
|
||||
REPO_BRANCH="alnyan/yggdrasil"
|
||||
|
||||
build_dir=$3
|
||||
|
||||
mkdir -p "$build_dir"
|
||||
|
||||
if [ ! -f "$build_dir/.source-ready" ]; then
|
||||
cd "$build_dir"
|
||||
git clone --branch="$REPO_BRANCH" "$REPO_URL"
|
||||
ln -s "$Y_WORKSPACE_ROOT" "$build_dir/doomgeneric/doomgeneric/doomgeneric-yggdrasil/yggdrasil-root"
|
||||
touch "$build_dir/.source-ready"
|
||||
fi
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
build_dir=$3
|
||||
|
||||
mkdir -p $Y_SYSROOT/bin
|
||||
install -m0755 $build_dir/doomgeneric/doomgeneric/doomgeneric $Y_SYSROOT/bin/doomgeneric
|
||||
@@ -0,0 +1,2 @@
|
||||
description = "Doom port"
|
||||
version = "1.0.0"
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../meta-port-scripts/gnu-compile.sh
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../meta-port-scripts/gnu-fetch.sh
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
export GNU_PROJECT=gmp
|
||||
export SRC_SHA256=e56fd59d76810932a0555aa15a14b61c16bed66110d3c75cc2ac49ddaa9ab24c
|
||||
export GNU_CONFIGURE_OPTIONS=
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../meta-port-scripts/gnu-install.sh
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description = "GNU Multiple Precision Arithmetic Library"
|
||||
version = "6.3.0"
|
||||
@@ -26,7 +26,7 @@ members = [
|
||||
"tools/shell",
|
||||
"tools/strace",
|
||||
]
|
||||
exclude = ["dynload-program", "test-kernel-module", "lib/ygglibc"]
|
||||
exclude = ["dynload-program", "test-kernel-module", "lib/ygglibc", "target"]
|
||||
|
||||
[workspace.dependencies]
|
||||
log = "0.4.22"
|
||||
@@ -65,8 +65,6 @@ sha2 = { version = "0.10.9" }
|
||||
crypto-common = "0.1.6"
|
||||
webpki-roots = "1.0.1"
|
||||
|
||||
raqote = { version = "0.8.3", default-features = false }
|
||||
|
||||
# Vendored/patched dependencies
|
||||
rand = { git = "https://git.alnyan.me/yggdrasil/rand.git", branch = "alnyan/yggdrasil-rng_core-0.6.4" }
|
||||
rand_core = { git = "https://git.alnyan.me/yggdrasil/rand.git", branch = "alnyan/yggdrasil-rng_core-0.6.4" }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Sync every 20 minutes
|
||||
/bin/date set --from-rtc
|
||||
/sbin/service start -- /bin/ntpc -i 1200 time.google.com:123
|
||||
|
||||
@@ -56,6 +56,7 @@ unsafe extern "C" fn posix_spawn(
|
||||
arguments: &args,
|
||||
environment: &envs,
|
||||
directory: None,
|
||||
root: None,
|
||||
optional: &[
|
||||
SpawnOption::CopyFile {
|
||||
source: RawFd::STDIN,
|
||||
|
||||
@@ -33,6 +33,9 @@ yggdrasil-abi.workspace = true
|
||||
yggdrasil-rt.workspace = true
|
||||
runtime.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
runtime.workspace = true
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
|
||||
@@ -41,6 +44,18 @@ path = "src/lib.rs"
|
||||
name = "mount"
|
||||
path = "src/mount.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "poweroff"
|
||||
path = "src/poweroff.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "reboot"
|
||||
path = "src/reboot.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "chroot"
|
||||
path = "src/chroot.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "login"
|
||||
path = "src/login.rs"
|
||||
@@ -66,6 +81,10 @@ path = "src/echo.rs"
|
||||
name = "ls"
|
||||
path = "src/ls.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "tree"
|
||||
path = "src/tree.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "mv"
|
||||
path = "src/mv.rs"
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
use std::process::ExitCode;
|
||||
|
||||
fn usage(cmd: &str) {
|
||||
eprintln!("Usage: {cmd} PATH PROGRAM [ARGS...]");
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let mut args = std::env::args();
|
||||
let cmd = args.next().unwrap();
|
||||
let Some(path) = args.next() else {
|
||||
usage(&cmd);
|
||||
return ExitCode::FAILURE;
|
||||
};
|
||||
let Some(program) = args.next() else {
|
||||
usage(&cmd);
|
||||
return ExitCode::FAILURE;
|
||||
};
|
||||
let cmd_args = args.collect::<Vec<_>>();
|
||||
|
||||
println!("Run {program:?} at {path:?} with args {cmd_args:?}");
|
||||
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
@@ -1,25 +1,86 @@
|
||||
use std::process::ExitCode;
|
||||
#![feature(rustc_private)]
|
||||
use std::{fs, io, process::ExitCode};
|
||||
|
||||
use chrono::DateTime;
|
||||
use yggdrasil_rt::time::get_real_time;
|
||||
use clap::{Parser, Subcommand};
|
||||
use cross::time::set_real_time;
|
||||
use runtime::rt::time::{get_real_time, SystemTime};
|
||||
|
||||
const DEFAULT_RTC_PATH: &str = "/sys/device/rtc/rtc0/time";
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
struct Args {
|
||||
#[clap(subcommand)]
|
||||
action: Option<Action>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum Action {
|
||||
Get {
|
||||
#[clap(short, long)]
|
||||
rtc: bool,
|
||||
},
|
||||
Set {
|
||||
#[clap(short = 'r', long)]
|
||||
from_rtc: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl Default for Action {
|
||||
fn default() -> Self {
|
||||
Self::Get { rtc: false }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
enum Error {
|
||||
#[error("Could not get current time: {0:?}")]
|
||||
GetTime(yggdrasil_rt::Error),
|
||||
Io(#[from] io::Error),
|
||||
#[error("Time conversion error")]
|
||||
UtcTime,
|
||||
}
|
||||
|
||||
fn run() -> Result<(), Error> {
|
||||
let now = get_real_time().map_err(Error::GetTime)?;
|
||||
let now = DateTime::from_timestamp(now.seconds() as _, now.subsec_nanos() as _).ok_or(Error::UtcTime)?;
|
||||
fn read_rtc_time() -> Result<SystemTime, Error> {
|
||||
let rtc_timestamp: u64 = fs::read_to_string(DEFAULT_RTC_PATH)?
|
||||
.trim()
|
||||
.parse()
|
||||
.map_err(|_| Error::UtcTime)?;
|
||||
|
||||
Ok(SystemTime::new(rtc_timestamp, 0))
|
||||
}
|
||||
|
||||
fn get_date(rtc: bool) -> Result<(), Error> {
|
||||
let time = if rtc {
|
||||
read_rtc_time()?
|
||||
} else {
|
||||
get_real_time().map_err(io::Error::from)?
|
||||
};
|
||||
|
||||
let now = DateTime::from_timestamp(time.seconds() as _, time.subsec_nanos() as _)
|
||||
.ok_or(Error::UtcTime)?;
|
||||
println!("{now}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_date(from_rtc: bool) -> Result<(), Error> {
|
||||
let time = if from_rtc { read_rtc_time()? } else { todo!() };
|
||||
|
||||
set_real_time(time.seconds(), 0)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(args: Args) -> Result<(), Error> {
|
||||
let action = args.action.unwrap_or_default();
|
||||
match action {
|
||||
Action::Get { rtc } => get_date(rtc),
|
||||
Action::Set { from_rtc } => set_date(from_rtc),
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
match run() {
|
||||
let args = Args::parse();
|
||||
match run(args) {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(error) => {
|
||||
eprintln!("Error: {error}");
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#![feature(rustc_private)]
|
||||
|
||||
use std::{io, process::ExitCode};
|
||||
|
||||
use runtime::{abi::system, rt::system::system_control};
|
||||
|
||||
fn main() -> ExitCode {
|
||||
if let Err(error) = system_control::<system::PowerOff>(&mut [], &()) {
|
||||
let error = io::Error::from(error);
|
||||
eprintln!("{error}");
|
||||
ExitCode::FAILURE
|
||||
} else {
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
fn main() {}
|
||||
@@ -0,0 +1,90 @@
|
||||
use std::{
|
||||
fs, io,
|
||||
path::{Path, PathBuf},
|
||||
process::ExitCode,
|
||||
};
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
fn list<P: AsRef<Path>>(path: P, depth: usize, last_mask: u64) -> io::Result<()> {
|
||||
fn indent(depth: usize, mask: u64) {
|
||||
for i in 0..depth + 1 {
|
||||
if i < depth {
|
||||
if (1 << i) & mask == 0 {
|
||||
print!("│ ");
|
||||
} else {
|
||||
print!(" ");
|
||||
}
|
||||
} else if (1 << i) & mask == 0 {
|
||||
print!("├─");
|
||||
} else {
|
||||
print!("└─");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut entries = vec![];
|
||||
let dir = fs::read_dir(path)?;
|
||||
for entry in dir {
|
||||
let Ok((ty, entry)) = entry.and_then(|e| Ok((e.file_type()?, e))) else {
|
||||
entries.push(None);
|
||||
continue;
|
||||
};
|
||||
|
||||
let name = entry.file_name().into_string().unwrap();
|
||||
|
||||
if name == "." || name == ".." {
|
||||
continue;
|
||||
}
|
||||
|
||||
entries.push(Some((name, entry.path(), ty.is_dir())));
|
||||
}
|
||||
|
||||
entries.sort_by(|a, b| {
|
||||
let (Some((a, _, _)), Some((b, _, _))) = (a.as_ref(), b.as_ref()) else {
|
||||
return Ord::cmp(&a.is_none(), &b.is_none());
|
||||
};
|
||||
|
||||
Ord::cmp(a, b)
|
||||
});
|
||||
|
||||
let len = entries.len();
|
||||
for (i, entry) in entries.into_iter().enumerate() {
|
||||
let last_bit = ((i == len - 1) as u64) << depth;
|
||||
indent(depth, last_mask | last_bit);
|
||||
|
||||
let Some((name, path, dir)) = entry else {
|
||||
println!("<error>");
|
||||
continue;
|
||||
};
|
||||
|
||||
println!("{name}");
|
||||
|
||||
if dir {
|
||||
list(path, depth + 1, last_mask | last_bit).ok();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
struct Args {
|
||||
path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
fn run<P: AsRef<Path>>(path: P) -> io::Result<()> {
|
||||
list(path, 0, 0)
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let args = Args::parse();
|
||||
let path = args.path.unwrap_or_else(|| PathBuf::from("."));
|
||||
match run(&path) {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(error) => {
|
||||
eprintln!("{}: {}", path.display(), error);
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,34 +29,38 @@ const PROGRAMS: &[(&str, &str)] = &[
|
||||
// shell
|
||||
("shell", "bin/sh"),
|
||||
// sysutils
|
||||
("echo", "bin/echo"),
|
||||
("mount", "sbin/mount"),
|
||||
("login", "sbin/login"),
|
||||
("strace", "bin/strace"),
|
||||
("ls", "bin/ls"),
|
||||
("mv", "bin/mv"),
|
||||
("ln", "bin/ln"),
|
||||
("mkdir", "bin/mkdir"),
|
||||
("touch", "bin/touch"),
|
||||
("env", "bin/env"),
|
||||
("rm", "bin/rm"),
|
||||
("cat", "bin/cat"),
|
||||
("hexd", "bin/hexd"),
|
||||
("dd", "bin/dd"),
|
||||
("random", "bin/random"),
|
||||
("view", "bin/view"),
|
||||
("grep", "bin/grep"),
|
||||
("chmod", "bin/chmod"),
|
||||
("sha256sum", "bin/sha256sum"),
|
||||
("sysmon", "bin/sysmon"),
|
||||
("chroot", "sbin/chroot"),
|
||||
("date", "bin/date"),
|
||||
("sync", "bin/sync"),
|
||||
("sleep", "bin/sleep"),
|
||||
("dd", "bin/dd"),
|
||||
("echo", "bin/echo"),
|
||||
("env", "bin/env"),
|
||||
("grep", "bin/grep"),
|
||||
("hexd", "bin/hexd"),
|
||||
("ln", "bin/ln"),
|
||||
("login", "sbin/login"),
|
||||
("ls", "bin/ls"),
|
||||
("lspci", "bin/lspci"),
|
||||
("ps", "bin/ps"),
|
||||
("top", "bin/top"),
|
||||
("tst", "bin/tst"),
|
||||
("md2txt", "bin/md2txt"),
|
||||
("mkdir", "bin/mkdir"),
|
||||
("mount", "sbin/mount"),
|
||||
("mv", "bin/mv"),
|
||||
("poweroff", "sbin/poweroff"),
|
||||
("ps", "bin/ps"),
|
||||
("random", "bin/random"),
|
||||
("reboot", "sbin/reboot"),
|
||||
("rm", "bin/rm"),
|
||||
("sha256sum", "bin/sha256sum"),
|
||||
("sleep", "bin/sleep"),
|
||||
("strace", "bin/strace"),
|
||||
("sync", "bin/sync"),
|
||||
("sysmon", "bin/sysmon"),
|
||||
("top", "bin/top"),
|
||||
("touch", "bin/touch"),
|
||||
("tree", "bin/tree"),
|
||||
("tst", "bin/tst"),
|
||||
("view", "bin/view"),
|
||||
// netutils
|
||||
("netconf", "sbin/netconf"),
|
||||
("dhcp-client", "sbin/dhcp-client"),
|
||||
|
||||
Reference in New Issue
Block a user