Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 717bbda123 | |||
| aa86f377f2 | |||
| e8acfb5a40 | |||
| b5d704064d | |||
| e934b4d696 | |||
| 6d8d97d492 | |||
| ca01f57873 | |||
| 9be467d5d5 | |||
| 0c2ebbf7b3 | |||
| cb4c0bc4b0 | |||
| c1b62aef1d | |||
| 6469914be1 | |||
| 131e6adc3d | |||
| 322cb0a958 | |||
| 6552fa8059 | |||
| 0ff48fd520 |
Generated
+40
@@ -258,6 +258,26 @@ version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
|
||||
|
||||
[[package]]
|
||||
name = "bitfield"
|
||||
version = "0.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db1bcd90f88eabbf0cadbfb87a45bceeaebcd3b4bc9e43da379cd2ef0162590d"
|
||||
dependencies = [
|
||||
"bitfield-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitfield-macros"
|
||||
version = "0.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3787a07661997bfc05dd3431e379c0188573f78857080cf682e1393ab8e4d64c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.98",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@@ -2727,6 +2747,25 @@ dependencies = [
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_mmc_generic_sdhci"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bitfield",
|
||||
"bitflags 2.8.0",
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"device-tree",
|
||||
"futures-util",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_net_core"
|
||||
version = "0.1.0"
|
||||
@@ -3021,6 +3060,7 @@ dependencies = [
|
||||
"ygg_driver_bsp_riscv",
|
||||
"ygg_driver_fat32",
|
||||
"ygg_driver_input",
|
||||
"ygg_driver_mmc_generic_sdhci",
|
||||
"ygg_driver_net_core",
|
||||
"ygg_driver_net_igbe",
|
||||
"ygg_driver_net_loopback",
|
||||
|
||||
@@ -7,6 +7,7 @@ exclude = [
|
||||
"toolchain",
|
||||
"userspace/dynload-program",
|
||||
"userspace/lib/ygglibc",
|
||||
"userspace",
|
||||
"toolchain-c"
|
||||
]
|
||||
members = [
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ $ booti ${loadaddr} ${initrd_addr_r}:<SIZE-PRINTED-WHEN-LOADING-INITRD> ${fdt_ad
|
||||
###### a quick command for a development boot
|
||||
###### (FIXME when initrd gets larger than 64MiB)
|
||||
|
||||
env set ipaddr 13.0.0.2; env set fdt_addr_r 0x11000000; env set initrd_addr_r 0x04000000; tftpboot ${initrd_addr_r} 13.0.0.1:initrd.tar; tftpboot ${loadaddr} 13.0.0.1:kernel.bin; load mmc 0:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb; fdt addr ${fdt_addr_r}; fdt resize; fdt memory 0x0 0x3C000000; booti ${loadaddr} ${initrd_addr_r}:67108864 ${fdt_addr_r}
|
||||
env set ipaddr 13.0.0.2; env set fdt_addr_r 0x11000000; env set initrd_addr_r 0x20000000; tftpboot ${initrd_addr_r} 13.0.0.1:initrd.img; tftpboot ${loadaddr} 13.0.0.1:yggdrasil-kernel.bin; load mmc 1:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb; fdt addr ${fdt_addr_r}; fdt resize; fdt memory 0x0 0x3C000000; booti ${loadaddr} ${initrd_addr_r}:0x4000000 ${fdt_addr_r}
|
||||
|
||||
dhcp;
|
||||
env set initrd_addr_r 0x20000000; tftpboot ${initrd_addr_r} 192.168.88.10:initrd.img; tftpboot ${loadaddr} 192.168.88.10:yggdrasil-kernel.bin; load mmc 1:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb; fdt addr ${fdt_addr_r}; fdt resize; fdt memory 0x0 0x3C000000; booti ${loadaddr} ${initrd_addr_r}:0x4000000 ${fdt_addr_r}
|
||||
|
||||
@@ -58,6 +58,7 @@ 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_mmc_generic_sdhci.path = "driver/mmc/generic-sdhci"
|
||||
|
||||
[target.'cfg(target_arch = "riscv64")'.dependencies]
|
||||
device-tree.workspace = true
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -235,7 +235,7 @@ impl BlockDevice for ScsiUnit {
|
||||
let mut transport = self.enclosure.transport.lock().await;
|
||||
|
||||
// TODO DmaSliceMut subslicing
|
||||
let (buffer, range) = buffer.into_parts();
|
||||
let (buffer, range) = unsafe { buffer.into_parts() };
|
||||
let mut offset = range.start;
|
||||
|
||||
for i in (0..lba_count).step_by(self.max_lba_per_request) {
|
||||
|
||||
@@ -4,4 +4,5 @@
|
||||
extern crate alloc;
|
||||
|
||||
mod pl011;
|
||||
mod pl031;
|
||||
mod pl061;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
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};
|
||||
@@ -68,7 +68,7 @@ struct Pl011Inner {
|
||||
pub struct Pl011 {
|
||||
inner: OneTimeInit<Arc<Terminal<Pl011Inner>>>,
|
||||
base: PhysicalAddress,
|
||||
irq: FullIrq,
|
||||
irq: IrqHandle,
|
||||
}
|
||||
|
||||
impl Io {
|
||||
@@ -164,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ use device_api::{
|
||||
GpioController, GpioInterruptEvent, GpioInterruptMode, GpioPinLevel, PinHandle,
|
||||
SinglePinDirection,
|
||||
},
|
||||
interrupt::{FullIrq, InterruptHandler, IrqVector},
|
||||
interrupt::{InterruptHandler, IrqHandle, IrqVector},
|
||||
};
|
||||
use device_tree::{
|
||||
driver::{
|
||||
@@ -17,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::{
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ 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};
|
||||
@@ -61,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>>>,
|
||||
}
|
||||
@@ -171,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();
|
||||
|
||||
@@ -2,7 +2,7 @@ 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::{
|
||||
@@ -11,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::{
|
||||
@@ -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,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,4 +3,5 @@
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod goldfish_rtc;
|
||||
mod plic;
|
||||
|
||||
@@ -2,8 +2,8 @@ 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::{
|
||||
@@ -261,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>,
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "ygg_driver_mmc_generic_sdhci"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
device-tree.workspace = true
|
||||
device-api.workspace = true
|
||||
yggdrasil-abi.workspace = true
|
||||
libk.workspace = true
|
||||
libk-util.workspace = true
|
||||
libk-mm.workspace = true
|
||||
|
||||
log.workspace = true
|
||||
tock-registers.workspace = true
|
||||
bitflags.workspace = true
|
||||
bytemuck.workspace = true
|
||||
async-trait.workspace = true
|
||||
futures-util.workspace = true
|
||||
bitfield = "0.19.1"
|
||||
@@ -0,0 +1,230 @@
|
||||
use core::{mem::MaybeUninit, time::Duration};
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use async_trait::async_trait;
|
||||
use device_api::device::{Device, DeviceInitContext};
|
||||
use libk::{
|
||||
device::block::BlockDevice,
|
||||
dma::{DmaBuffer, DmaSlice, DmaSliceMut},
|
||||
error::Error,
|
||||
task::runtime,
|
||||
};
|
||||
use libk_mm::{
|
||||
address::PhysicalAddress, table::MapAttributes, OnDemandPage, PageProvider, VirtualPage,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
command::{self, CsdStructure, OcrRegister},
|
||||
host::SdhciHost,
|
||||
Sdhci,
|
||||
};
|
||||
|
||||
pub struct SdCard<H: SdhciHost> {
|
||||
hc: Arc<Sdhci<H>>,
|
||||
pub(crate) rca: u16,
|
||||
pub(crate) csd: CsdStructure,
|
||||
high_capacity: bool,
|
||||
}
|
||||
|
||||
impl<H: SdhciHost> SdCard<H> {
|
||||
/*
|
||||
* SD/MMC card setup state machine:
|
||||
*
|
||||
* IDLE <------\ If card cannot handle req. voltage
|
||||
* | |
|
||||
* | <--- Send CMD8
|
||||
* |
|
||||
* No response | Voltage range not compatible
|
||||
* | | |
|
||||
* V V V
|
||||
* /--------- ACMD41 --------> INA <-------- CMD15 from data xfer mode
|
||||
* | |
|
||||
* | |
|
||||
* V V
|
||||
* MMC card READY
|
||||
* |
|
||||
* V
|
||||
* CMD2
|
||||
* |
|
||||
* V
|
||||
* IDENT
|
||||
* |
|
||||
* V
|
||||
* CMD3
|
||||
* |
|
||||
* | <--- Card responds with a RCA
|
||||
* Ident mode |
|
||||
* -----------------------+----------------------------------------
|
||||
* Xfer mode |
|
||||
* V
|
||||
* STBY
|
||||
*
|
||||
*/
|
||||
pub async fn setup(hc: Arc<Sdhci<H>>) -> Result<Arc<Self>, Error> {
|
||||
// GO_IDLE_STATE
|
||||
hc.submit_command::<command::CMD0, _>((), ()).await?;
|
||||
// SEND_IF_COND, 2.7-3.3V range
|
||||
// TODO: actually check result
|
||||
hc.submit_command::<command::CMD8, _>(0x1AA, ()).await?;
|
||||
|
||||
// TODO adjust voltage as needed
|
||||
|
||||
// SEND_OP_COND
|
||||
let mut attempts = 10;
|
||||
let mut high_capacity = false;
|
||||
while attempts != 0 {
|
||||
let Ok(r3) = hc
|
||||
.submit_command::<command::ACMD41, _>(0x40FF8000, ())
|
||||
.await
|
||||
else {
|
||||
runtime::sleep(Duration::from_millis(200)).await;
|
||||
attempts -= 1;
|
||||
continue;
|
||||
};
|
||||
log::trace!("r3 = {r3:?}");
|
||||
|
||||
if r3.0.contains(OcrRegister::POWER_UP) {
|
||||
high_capacity = r3.0.contains(OcrRegister::CCS);
|
||||
break;
|
||||
}
|
||||
|
||||
attempts -= 1;
|
||||
runtime::sleep(Duration::from_millis(100)).await;
|
||||
}
|
||||
if attempts == 0 {
|
||||
log::error!("ACMD41 timeout/failure");
|
||||
return Err(Error::InvalidOperation);
|
||||
}
|
||||
|
||||
// ALL_SEND_CID
|
||||
hc.submit_command::<command::CMD2, _>((), ()).await?;
|
||||
// SEND_RELATIVE_ADDR
|
||||
let rca = hc
|
||||
.submit_command::<command::CMD3, _>((), ())
|
||||
.await?
|
||||
.card_address;
|
||||
|
||||
// Card is now in data transfer mode
|
||||
|
||||
// Get card capacity
|
||||
// SEND_CSD
|
||||
let r2 = hc
|
||||
.submit_command::<command::CMD9, _>((rca as u32) << 16, ())
|
||||
.await?;
|
||||
let csd = r2.parse_csd_structure()?;
|
||||
|
||||
hc.submit_command::<command::CMD7, _>((rca as u32) << 16, ())
|
||||
.await?;
|
||||
|
||||
Ok(Arc::new(Self {
|
||||
hc,
|
||||
rca,
|
||||
csd,
|
||||
high_capacity,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<H: SdhciHost> BlockDevice for SdCard<H> {
|
||||
fn allocate_buffer(&self, size: usize) -> Result<DmaBuffer<[MaybeUninit<u8>]>, Error> {
|
||||
DmaBuffer::new_uninit_slice(self.hc.dma_allocator().as_ref(), size)
|
||||
}
|
||||
|
||||
// TODO read directly to cache
|
||||
async fn read_aligned(
|
||||
&self,
|
||||
position: u64,
|
||||
buffer: DmaSliceMut<'_, MaybeUninit<u8>>,
|
||||
) -> Result<(), Error> {
|
||||
assert!(self.high_capacity);
|
||||
assert_eq!(position % 512, 0);
|
||||
assert_eq!(buffer.len() % 512, 0);
|
||||
|
||||
let block_count = buffer.len() / 512;
|
||||
let address: u32 = (position / 512)
|
||||
.try_into()
|
||||
.map_err(|_| Error::InvalidArgument)?;
|
||||
|
||||
let (buffer, range) = unsafe { buffer.into_parts() };
|
||||
for i in 0..block_count {
|
||||
let mut block_slice =
|
||||
buffer.slice_mut(range.start + i * 512..range.start + i * 512 + 512);
|
||||
self.hc
|
||||
.read_single_block_pio(address + i as u32, &mut block_slice)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn write_aligned(&self, position: u64, buffer: DmaSlice<'_, u8>) -> Result<(), Error> {
|
||||
assert!(self.high_capacity);
|
||||
assert_eq!(position % 512, 0);
|
||||
assert_eq!(buffer.len() % 512, 0);
|
||||
|
||||
let block_count = buffer.len() / 512;
|
||||
let address: u32 = (position / 512)
|
||||
.try_into()
|
||||
.map_err(|_| Error::InvalidArgument)?;
|
||||
|
||||
let (buffer, range) = unsafe { buffer.into_parts() };
|
||||
for i in 0..block_count {
|
||||
let block_slice = buffer.slice(range.start + i * 512..range.start + i * 512 + 512);
|
||||
self.hc
|
||||
.write_single_block_pio(address + i as u32, &block_slice)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn block_size(&self) -> usize {
|
||||
self.csd.read_block_size
|
||||
}
|
||||
|
||||
fn block_count(&self) -> u64 {
|
||||
self.csd.capacity
|
||||
}
|
||||
|
||||
fn max_blocks_per_request(&self) -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: SdhciHost> PageProvider for SdCard<H> {
|
||||
fn ondemand_fetch(&self, _opaque: u64) -> Result<OnDemandPage, Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_page(&self, _offset: u64) -> Result<VirtualPage, Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn release_page(
|
||||
&self,
|
||||
_offset: u64,
|
||||
_phys: PhysicalAddress,
|
||||
_dirty: bool,
|
||||
) -> Result<(), Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn clone_page(
|
||||
&self,
|
||||
_offset: u64,
|
||||
_src_phys: PhysicalAddress,
|
||||
_src_attrs: MapAttributes,
|
||||
) -> Result<PhysicalAddress, Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
impl<H: SdhciHost> Device for SdCard<H> {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
"SD Card"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
pub trait SdArgument {
|
||||
fn into_u32(self) -> u32;
|
||||
}
|
||||
|
||||
impl SdArgument for u32 {
|
||||
fn into_u32(self) -> u32 {
|
||||
self
|
||||
}
|
||||
}
|
||||
impl SdArgument for () {
|
||||
fn into_u32(self) -> u32 {
|
||||
0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
pub mod argument;
|
||||
pub mod register;
|
||||
pub mod response;
|
||||
pub mod transfer;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SdCommandIndex {
|
||||
ACmd(u8),
|
||||
Cmd(u8),
|
||||
}
|
||||
|
||||
pub trait SdCommand {
|
||||
const INDEX: SdCommandIndex;
|
||||
|
||||
type Response: SdResponse = ();
|
||||
type Argument: SdArgument = ();
|
||||
type Transfer<'a>: SdTransfer = ();
|
||||
}
|
||||
|
||||
pub use argument::SdArgument;
|
||||
pub use register::{CsdStructure, CsdStructureV1, CsdStructureV2, OcrRegister};
|
||||
pub use response::{R1b, SdResponse, SdResponseWidth, R1, R2, R3, R6, R7};
|
||||
pub use transfer::{SdDeviceToHostTransfer, SdHostToDeviceTransfer, SdTransfer};
|
||||
|
||||
/// GO_IDLE_STATE
|
||||
pub struct CMD0;
|
||||
/// ALL_SEND_CID
|
||||
pub struct CMD2;
|
||||
/// SEND_RELATIVE_ADDR
|
||||
pub struct CMD3;
|
||||
/// SELECT/DESELECT_CARD
|
||||
///
|
||||
/// Argument: RCA
|
||||
pub struct CMD7;
|
||||
/// SEND_IF_COND
|
||||
///
|
||||
/// Argument: TODO
|
||||
pub struct CMD8;
|
||||
/// SEND_CSD
|
||||
///
|
||||
/// Argument: RCA
|
||||
pub struct CMD9;
|
||||
/// STOP_TRANSMISSION
|
||||
pub struct CMD12;
|
||||
/// READ_SINGLE_BLOCK
|
||||
///
|
||||
/// Argument: data address
|
||||
pub struct CMD17;
|
||||
/// READ_MULTIPLE_BLOCK
|
||||
///
|
||||
/// Argument: data address
|
||||
pub struct CMD18;
|
||||
/// WRITE_SINGLE_BLOCK
|
||||
///
|
||||
/// Argument: data address
|
||||
pub struct CMD24;
|
||||
/// APP_CMD
|
||||
pub struct CMD55;
|
||||
/// SD_SEND_OP_COND
|
||||
///
|
||||
/// Argument: TODO
|
||||
pub struct ACMD41;
|
||||
impl SdCommand for CMD0 {
|
||||
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(0);
|
||||
}
|
||||
impl SdCommand for CMD2 {
|
||||
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(2);
|
||||
type Argument = ();
|
||||
type Response = R2;
|
||||
}
|
||||
impl SdCommand for CMD3 {
|
||||
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(3);
|
||||
type Argument = ();
|
||||
type Response = R6;
|
||||
}
|
||||
impl SdCommand for CMD7 {
|
||||
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(7);
|
||||
type Argument = u32;
|
||||
type Response = R1b;
|
||||
}
|
||||
impl SdCommand for CMD8 {
|
||||
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(8);
|
||||
type Argument = u32;
|
||||
type Response = R7;
|
||||
}
|
||||
impl SdCommand for CMD9 {
|
||||
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(9);
|
||||
|
||||
type Argument = u32;
|
||||
type Response = R2;
|
||||
}
|
||||
impl SdCommand for CMD12 {
|
||||
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(12);
|
||||
type Argument = ();
|
||||
type Response = R1b;
|
||||
}
|
||||
impl SdCommand for CMD17 {
|
||||
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(17);
|
||||
type Argument = u32;
|
||||
type Response = R1;
|
||||
type Transfer<'a> = SdDeviceToHostTransfer<'a>;
|
||||
}
|
||||
impl SdCommand for CMD18 {
|
||||
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(18);
|
||||
type Argument = u32;
|
||||
type Response = R1;
|
||||
type Transfer<'a> = SdDeviceToHostTransfer<'a>;
|
||||
}
|
||||
impl SdCommand for CMD24 {
|
||||
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(24);
|
||||
type Argument = u32;
|
||||
type Response = R1;
|
||||
type Transfer<'a> = SdHostToDeviceTransfer<'a>;
|
||||
}
|
||||
impl SdCommand for CMD55 {
|
||||
const INDEX: SdCommandIndex = SdCommandIndex::Cmd(55);
|
||||
type Argument = u32;
|
||||
type Response = R1;
|
||||
}
|
||||
impl SdCommand for ACMD41 {
|
||||
const INDEX: SdCommandIndex = SdCommandIndex::ACmd(41);
|
||||
type Argument = u32;
|
||||
type Response = R3;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
use core::fmt;
|
||||
|
||||
use bitfield::BitRange;
|
||||
use bitflags::bitflags;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CsdStructure {
|
||||
pub read_block_size: usize,
|
||||
pub write_block_size: usize,
|
||||
pub capacity: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct CsdStructureV1(u128);
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct CsdStructureV2(u128);
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct OcrRegister: u32 {
|
||||
const VDD_2_7V_2_8V = 1 << 15;
|
||||
const VDD_2_8V_2_9V = 1 << 16;
|
||||
const VDD_2_9V_3_0V = 1 << 17;
|
||||
const VDD_3_0V_3_1V = 1 << 18;
|
||||
const VDD_3_1V_3_2V = 1 << 19;
|
||||
const VDD_3_2V_3_3V = 1 << 20;
|
||||
const VDD_3_3V_3_4V = 1 << 21;
|
||||
const VDD_3_4V_3_5V = 1 << 22;
|
||||
const VDD_3_5V_3_6V = 1 << 23;
|
||||
const CCS = 1 << 30;
|
||||
const POWER_UP = 1 << 31;
|
||||
}
|
||||
}
|
||||
unsafe impl Pod for OcrRegister {}
|
||||
unsafe impl Zeroable for OcrRegister {}
|
||||
|
||||
impl CsdStructureV1 {
|
||||
pub fn csd_structure(&self) -> u32 {
|
||||
self.0.bit_range(127 - 8, 126 - 8)
|
||||
}
|
||||
|
||||
pub fn read_bl_len(&self) -> u32 {
|
||||
self.0.bit_range(83 - 8, 80 - 8)
|
||||
}
|
||||
|
||||
pub fn c_size(&self) -> u32 {
|
||||
self.0.bit_range(73 - 8, 62 - 8)
|
||||
}
|
||||
|
||||
pub fn c_size_mult(&self) -> u32 {
|
||||
self.0.bit_range(49 - 8, 47 - 8)
|
||||
}
|
||||
}
|
||||
|
||||
impl CsdStructureV2 {
|
||||
pub fn csd_structure(&self) -> u32 {
|
||||
self.0.bit_range(127 - 8, 126 - 8)
|
||||
}
|
||||
pub fn c_size(&self) -> u32 {
|
||||
self.0.bit_range(69 - 8, 48 - 8)
|
||||
}
|
||||
|
||||
pub fn read_bl_len(&self) -> u32 {
|
||||
self.0.bit_range(83 - 8, 80 - 8)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for CsdStructureV1 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("CsdStructureV1")
|
||||
.field("CSD_STRUCTURE", &self.csd_structure())
|
||||
.field("READ_BL_LEN", &self.read_bl_len())
|
||||
.field("C_SIZE", &self.c_size())
|
||||
.field("C_SIZE_MULT", &self.c_size_mult())
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for CsdStructureV2 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("CsdStructureV2")
|
||||
.field("CSD_STRUCTURE", &self.csd_structure())
|
||||
.field("READ_BL_LEN", &self.read_bl_len())
|
||||
.field("C_SIZE", &self.c_size())
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
use libk::error::Error;
|
||||
|
||||
use super::{register::OcrRegister, CsdStructure, CsdStructureV1, CsdStructureV2};
|
||||
|
||||
pub enum SdResponseWidth {
|
||||
None,
|
||||
W48,
|
||||
W48b,
|
||||
W136,
|
||||
}
|
||||
|
||||
pub trait SdResponse {
|
||||
const BIT_WIDTH: SdResponseWidth;
|
||||
const CRC_CHECK: bool = true;
|
||||
const INDEX_CHECK: bool = false;
|
||||
|
||||
fn from_bits(bits: &[u32; 4]) -> Self;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct R1(pub u32);
|
||||
#[derive(Debug)]
|
||||
pub struct R1b(pub u32);
|
||||
#[derive(Debug)]
|
||||
pub struct R2(pub [u32; 4]);
|
||||
#[derive(Debug)]
|
||||
pub struct R3(pub OcrRegister);
|
||||
/// R6 - Published RCA Response
|
||||
#[derive(Debug)]
|
||||
pub struct R6 {
|
||||
pub card_status: u16,
|
||||
pub card_address: u16,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub struct R7(pub u32);
|
||||
|
||||
impl SdResponse for () {
|
||||
const BIT_WIDTH: SdResponseWidth = SdResponseWidth::None;
|
||||
const CRC_CHECK: bool = false;
|
||||
|
||||
fn from_bits(_bits: &[u32; 4]) -> Self {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
impl SdResponse for R1 {
|
||||
const BIT_WIDTH: SdResponseWidth = SdResponseWidth::W48;
|
||||
fn from_bits(bits: &[u32; 4]) -> Self {
|
||||
Self(bits[0])
|
||||
}
|
||||
}
|
||||
|
||||
impl SdResponse for R1b {
|
||||
const BIT_WIDTH: SdResponseWidth = SdResponseWidth::W48b;
|
||||
fn from_bits(bits: &[u32; 4]) -> Self {
|
||||
Self(bits[0])
|
||||
}
|
||||
}
|
||||
|
||||
impl SdResponse for R2 {
|
||||
const BIT_WIDTH: SdResponseWidth = SdResponseWidth::W136;
|
||||
fn from_bits(bits: &[u32; 4]) -> Self {
|
||||
Self(*bits)
|
||||
}
|
||||
}
|
||||
|
||||
impl R2 {
|
||||
pub fn parse_csd_structure(&self) -> Result<CsdStructure, Error> {
|
||||
let v1 = bytemuck::cast::<_, CsdStructureV1>(self.0);
|
||||
match v1.csd_structure() {
|
||||
0 => {
|
||||
// block_size = 1 << READ_BL_LEN
|
||||
// size_mult = 1 << (C_SIZE_MULT + 2)
|
||||
// capacity = (C_SIZE + 1) * size_mult [blocks]
|
||||
let size_mult = 1usize << (v1.c_size_mult() + 2);
|
||||
Ok(CsdStructure {
|
||||
read_block_size: 1usize << v1.read_bl_len(),
|
||||
write_block_size: 512, // TODO
|
||||
capacity: (v1.c_size() as u64 + 1) * (size_mult as u64),
|
||||
})
|
||||
}
|
||||
1 => {
|
||||
// block_size = 512
|
||||
// capacity = (C_SIZE + 1) * block_size * 1024 [bytes]
|
||||
let v2 = bytemuck::cast::<_, CsdStructureV2>(self.0);
|
||||
|
||||
Ok(CsdStructure {
|
||||
read_block_size: 512,
|
||||
write_block_size: 512,
|
||||
capacity: (v2.c_size() as u64 + 1) * 1024,
|
||||
})
|
||||
}
|
||||
_ => Err(Error::InvalidArgument),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SdResponse for R3 {
|
||||
const BIT_WIDTH: SdResponseWidth = SdResponseWidth::W48;
|
||||
const CRC_CHECK: bool = false;
|
||||
fn from_bits(bits: &[u32; 4]) -> Self {
|
||||
Self(bytemuck::cast(bits[0]))
|
||||
}
|
||||
}
|
||||
|
||||
impl SdResponse for R6 {
|
||||
const BIT_WIDTH: SdResponseWidth = SdResponseWidth::W48;
|
||||
fn from_bits(bits: &[u32; 4]) -> Self {
|
||||
Self {
|
||||
card_address: (bits[0] >> 16) as u16,
|
||||
card_status: bits[0] as u16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SdResponse for R7 {
|
||||
const BIT_WIDTH: SdResponseWidth = SdResponseWidth::W48;
|
||||
const INDEX_CHECK: bool = true;
|
||||
fn from_bits(bits: &[u32; 4]) -> Self {
|
||||
Self(bits[0])
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
use core::{future, mem::MaybeUninit};
|
||||
|
||||
use libk::{
|
||||
dma::{DmaSlice, DmaSliceMut},
|
||||
error::Error,
|
||||
};
|
||||
use tock_registers::fields::FieldValue;
|
||||
|
||||
use crate::{host::SdhciHost, regs, Sdhci};
|
||||
|
||||
pub trait SdTransfer {
|
||||
fn perform_transfer<H: SdhciHost>(
|
||||
self,
|
||||
hc: &Sdhci<H>,
|
||||
) -> impl Future<Output = Result<(), Error>>;
|
||||
|
||||
fn command_bits(&self) -> FieldValue<u32, regs::COMMAND::Register>;
|
||||
fn block_count(&self) -> u16;
|
||||
fn block_size(&self) -> u16;
|
||||
}
|
||||
|
||||
pub struct SdDeviceToHostTransfer<'a>(&'a mut DmaSliceMut<'a, MaybeUninit<u8>>);
|
||||
pub struct SdHostToDeviceTransfer<'a>(&'a DmaSlice<'a, u8>);
|
||||
|
||||
impl<'a> From<&'a mut DmaSliceMut<'a, MaybeUninit<u8>>> for SdDeviceToHostTransfer<'a> {
|
||||
fn from(value: &'a mut DmaSliceMut<'a, MaybeUninit<u8>>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl SdTransfer for SdDeviceToHostTransfer<'_> {
|
||||
fn perform_transfer<H: SdhciHost>(
|
||||
self,
|
||||
hc: &Sdhci<H>,
|
||||
) -> impl Future<Output = Result<(), Error>> {
|
||||
async move {
|
||||
let block_count = self.block_count() as usize;
|
||||
let block_size = self.block_size() as usize;
|
||||
|
||||
for i in 0..block_count {
|
||||
let off = i * block_size;
|
||||
let slice = &mut self.0[off..off + block_size];
|
||||
|
||||
hc.receive_single_block_pio(slice, block_size).await?;
|
||||
}
|
||||
|
||||
hc.wait_for_interrupt(regs::EVENT_MASK::TRANSFER_COMPLETE::SET)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn command_bits(&self) -> FieldValue<u32, regs::COMMAND::Register> {
|
||||
(if self.block_count() > 1 {
|
||||
regs::COMMAND::MULTI_BLOCK_SELECT::MultiBlock
|
||||
} else {
|
||||
regs::COMMAND::MULTI_BLOCK_SELECT::SingleBlock
|
||||
}) + regs::COMMAND::DATA_PRESENT_SELECT::SET
|
||||
+ regs::COMMAND::DATA_TRANSFER_DIRECTION::CardToHost
|
||||
}
|
||||
|
||||
fn block_size(&self) -> u16 {
|
||||
// TODO
|
||||
512
|
||||
}
|
||||
|
||||
fn block_count(&self) -> u16 {
|
||||
(self.0.len() / 512).try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a DmaSlice<'a, u8>> for SdHostToDeviceTransfer<'a> {
|
||||
fn from(value: &'a DmaSlice<'a, u8>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl SdTransfer for SdHostToDeviceTransfer<'_> {
|
||||
fn perform_transfer<H: SdhciHost>(
|
||||
self,
|
||||
hc: &Sdhci<H>,
|
||||
) -> impl Future<Output = Result<(), Error>> {
|
||||
async move {
|
||||
let block_count = self.block_count() as usize;
|
||||
let block_size = self.block_size() as usize;
|
||||
|
||||
for i in 0..block_count {
|
||||
let off = i * block_size;
|
||||
let slice = &self.0[off..off + block_size];
|
||||
|
||||
hc.send_single_block_pio(slice, block_size).await?;
|
||||
}
|
||||
|
||||
hc.wait_for_interrupt(regs::EVENT_MASK::TRANSFER_COMPLETE::SET)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn command_bits(&self) -> FieldValue<u32, regs::COMMAND::Register> {
|
||||
(if self.block_count() > 1 {
|
||||
regs::COMMAND::MULTI_BLOCK_SELECT::MultiBlock
|
||||
} else {
|
||||
regs::COMMAND::MULTI_BLOCK_SELECT::SingleBlock
|
||||
}) + regs::COMMAND::DATA_PRESENT_SELECT::SET
|
||||
+ regs::COMMAND::DATA_TRANSFER_DIRECTION::HostToCard
|
||||
}
|
||||
|
||||
fn block_size(&self) -> u16 {
|
||||
// TODO
|
||||
512
|
||||
}
|
||||
|
||||
fn block_count(&self) -> u16 {
|
||||
(self.0.len() / 512).try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl SdTransfer for () {
|
||||
fn perform_transfer<H: SdhciHost>(
|
||||
self,
|
||||
_hc: &Sdhci<H>,
|
||||
) -> impl Future<Output = Result<(), Error>> {
|
||||
future::ready(Ok(()))
|
||||
}
|
||||
|
||||
fn command_bits(&self) -> FieldValue<u32, regs::COMMAND::Register> {
|
||||
FieldValue::none()
|
||||
}
|
||||
|
||||
fn block_size(&self) -> u16 {
|
||||
0
|
||||
}
|
||||
|
||||
fn block_count(&self) -> u16 {
|
||||
0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
use core::cell::UnsafeCell;
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
dma::DmaAllocator,
|
||||
interrupt::{InterruptHandler, IrqHandle},
|
||||
};
|
||||
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
|
||||
use libk::error::Error;
|
||||
use libk_mm::device::DeviceMemoryIoMut;
|
||||
use libk_util::OneTimeInit;
|
||||
|
||||
use crate::Sdhci;
|
||||
|
||||
pub trait SdhciHost: Send + Sync + 'static {
|
||||
unsafe fn init(&self, cx: DeviceInitContext) -> Result<(), Error>;
|
||||
unsafe fn init_irq(&self, handler: Arc<dyn InterruptHandler>) -> Result<(), Error>;
|
||||
|
||||
fn dma_allocator(&self) -> &Arc<dyn DmaAllocator>;
|
||||
|
||||
fn name(&self) -> &'static str;
|
||||
|
||||
fn read4(&self, reg: usize) -> u32;
|
||||
fn read2(&self, reg: usize) -> u16;
|
||||
fn read1(&self, reg: usize) -> u8;
|
||||
fn write4(&self, reg: usize, val: u32);
|
||||
fn write2(&self, reg: usize, val: u16);
|
||||
fn write1(&self, reg: usize, val: u8);
|
||||
|
||||
// max_clock(), set_clock(...)
|
||||
// reset()
|
||||
// set_bus_width(...)
|
||||
}
|
||||
|
||||
pub struct IprocSdhci {
|
||||
// base: PhysicalAddress,
|
||||
mmio: UnsafeCell<DeviceMemoryIoMut<'static, [u32]>>,
|
||||
irq: IrqHandle,
|
||||
name: &'static str,
|
||||
|
||||
dma: OneTimeInit<Arc<dyn DmaAllocator>>,
|
||||
}
|
||||
|
||||
unsafe impl Sync for IprocSdhci {}
|
||||
unsafe impl Send for IprocSdhci {}
|
||||
|
||||
impl SdhciHost for IprocSdhci {
|
||||
fn name(&self) -> &'static str {
|
||||
self.name
|
||||
}
|
||||
|
||||
unsafe fn init(&self, cx: DeviceInitContext) -> Result<(), Error> {
|
||||
self.dma.init(cx.dma_allocator);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn init_irq(&self, handler: Arc<dyn InterruptHandler>) -> Result<(), Error> {
|
||||
self.irq.register(handler)?;
|
||||
self.irq.enable()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn dma_allocator(&self) -> &Arc<dyn DmaAllocator> {
|
||||
self.dma.get()
|
||||
}
|
||||
|
||||
fn read4(&self, reg: usize) -> u32 {
|
||||
assert_eq!(reg % 4, 0);
|
||||
let mmio = unsafe { &mut *self.mmio.get() };
|
||||
unsafe { core::ptr::read_volatile(&mmio[reg / 4]) }
|
||||
}
|
||||
|
||||
fn read2(&self, reg: usize) -> u16 {
|
||||
assert_eq!(reg % 2, 0);
|
||||
let mmio = unsafe { &mut *self.mmio.get() };
|
||||
let address32 = reg / 4;
|
||||
let shift32 = (reg & 2) << 3;
|
||||
let val32 = unsafe { core::ptr::read_volatile(&mmio[address32]) };
|
||||
(val32 >> shift32) as u16
|
||||
}
|
||||
|
||||
fn read1(&self, reg: usize) -> u8 {
|
||||
let mmio = unsafe { &mut *self.mmio.get() };
|
||||
let address32 = reg / 4;
|
||||
let shift32 = (reg & 3) << 3;
|
||||
let val32 = unsafe { core::ptr::read_volatile(&mmio[address32]) };
|
||||
(val32 >> shift32) as u8
|
||||
}
|
||||
|
||||
fn write4(&self, reg: usize, val: u32) {
|
||||
assert_eq!(reg % 4, 0);
|
||||
let mmio = unsafe { &mut *self.mmio.get() };
|
||||
unsafe { core::ptr::write_volatile(&mut mmio[reg / 4], val) };
|
||||
}
|
||||
|
||||
fn write2(&self, reg: usize, val: u16) {
|
||||
let mmio = unsafe { &mut *self.mmio.get() };
|
||||
let address32 = reg / 4;
|
||||
let shift32 = (reg & 2) << 3;
|
||||
let mut val32 = unsafe { core::ptr::read_volatile(&mmio[address32]) };
|
||||
val32 &= !(0xFFFF << shift32);
|
||||
val32 |= (val as u32) << shift32;
|
||||
unsafe { core::ptr::write_volatile(&mut mmio[address32], val32) };
|
||||
}
|
||||
|
||||
fn write1(&self, reg: usize, val: u8) {
|
||||
let mmio = unsafe { &mut *self.mmio.get() };
|
||||
let address32 = reg / 4;
|
||||
let shift32 = (reg & 3) << 3;
|
||||
let mut val32 = unsafe { core::ptr::read_volatile(&mmio[address32]) };
|
||||
val32 &= !(0xFF << shift32);
|
||||
val32 |= (val as u32) << shift32;
|
||||
unsafe { core::ptr::write_volatile(&mut mmio[address32], val32) };
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
// compatible: ["brcm,bcm2835-mmc", "brcm,bcm2835-sdhci"],
|
||||
compatible: ["brcm,bcm2711-emmc2"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let base = node.map_base(context, 0)?;
|
||||
let irq = node.interrupt(0)?;
|
||||
let name = node.name()?;
|
||||
|
||||
let mmio = UnsafeCell::new(unsafe {
|
||||
DeviceMemoryIoMut::map_slice(
|
||||
base, 0x100 / 4, Default::default()
|
||||
)
|
||||
.inspect_err(|e| log::error!("{name}: memory mapping error {e:?}"))
|
||||
.ok()?
|
||||
});
|
||||
|
||||
let hc = IprocSdhci {
|
||||
mmio,
|
||||
irq,
|
||||
name,
|
||||
dma: OneTimeInit::new(),
|
||||
};
|
||||
let sdhc = Sdhci::from_host(hc).inspect_err(|e| log::error!("{name}: {e:?}")).ok()?;
|
||||
|
||||
Some(Arc::new(sdhc))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,772 @@
|
||||
#![feature(associated_type_defaults, maybe_uninit_slice)]
|
||||
#![no_std]
|
||||
|
||||
use core::{
|
||||
future::poll_fn,
|
||||
mem::MaybeUninit,
|
||||
ops::BitAndAssign,
|
||||
sync::atomic::{AtomicBool, AtomicU32, Ordering},
|
||||
task::Poll,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use alloc::{format, sync::Arc};
|
||||
use card::SdCard;
|
||||
use command::{SdArgument, SdCommand, SdCommandIndex, SdResponse, SdResponseWidth, SdTransfer};
|
||||
use device_api::{
|
||||
clock::{Hertz, IntoHertz},
|
||||
device::{Device, DeviceInitContext},
|
||||
dma::DmaAllocator,
|
||||
interrupt::{InterruptHandler, IrqVector},
|
||||
};
|
||||
use futures_util::task::AtomicWaker;
|
||||
use host::SdhciHost;
|
||||
use libk::{
|
||||
device::manager::probe_partitions,
|
||||
dma::{DmaSlice, DmaSliceMut},
|
||||
error::Error,
|
||||
fs::devfs,
|
||||
task::{runtime, sync::AsyncMutex},
|
||||
};
|
||||
use libk_util::OneTimeInit;
|
||||
use regs::{
|
||||
Reg, CLOCK_CONTROL, EVENT_MASK, INTERRUPT_SIGNAL_ENABLE, INTERRUPT_STATUS_ENABLE,
|
||||
POWER_CONTROL, PRESENT_STATE, SOFTWARE_RESET,
|
||||
};
|
||||
use tock_registers::{fields::FieldValue, LocalRegisterCopy, RegisterLongName, UIntLike};
|
||||
use yggdrasil_abi::io::FileMode;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub mod card;
|
||||
pub mod command;
|
||||
pub mod host;
|
||||
pub mod regs;
|
||||
|
||||
pub enum ClockMode {
|
||||
None,
|
||||
Internal,
|
||||
All,
|
||||
}
|
||||
|
||||
pub struct Capabilities {
|
||||
pub version: HostVersion,
|
||||
pub clock_multiplier: Option<u32>,
|
||||
pub voltage_support_1_8v: bool,
|
||||
pub voltage_support_3_0v: bool,
|
||||
pub voltage_support_3_3v: bool,
|
||||
pub sdma_support: bool,
|
||||
pub adma2_support: bool,
|
||||
pub base_frequency: Option<Hertz>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum HostVersion {
|
||||
V1_00,
|
||||
V2_00,
|
||||
V3_00,
|
||||
V4_00,
|
||||
V4_10,
|
||||
V4_20,
|
||||
}
|
||||
|
||||
struct SdhciInner<H: SdhciHost> {
|
||||
capabilities: Capabilities,
|
||||
host: H,
|
||||
}
|
||||
|
||||
struct InterruptStatus {
|
||||
interrupt_status: AtomicU32,
|
||||
notify: AtomicWaker,
|
||||
}
|
||||
|
||||
pub struct Sdhci<H: SdhciHost> {
|
||||
inner: SdhciInner<H>,
|
||||
dma: OneTimeInit<Arc<dyn DmaAllocator>>,
|
||||
|
||||
interrupt_status: InterruptStatus,
|
||||
command_lock: AsyncMutex<()>,
|
||||
|
||||
card_present: AtomicBool,
|
||||
}
|
||||
|
||||
impl InterruptStatus {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
interrupt_status: AtomicU32::new(0),
|
||||
notify: AtomicWaker::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn signal(&self, value: u32) {
|
||||
self.interrupt_status.fetch_or(value, Ordering::Release);
|
||||
self.notify.wake();
|
||||
}
|
||||
|
||||
async fn wait(&self, mask: u32) -> LocalRegisterCopy<u32, EVENT_MASK::Register> {
|
||||
let not_mask = !mask;
|
||||
poll_fn(|cx| {
|
||||
let value = self.interrupt_status.fetch_and(not_mask, Ordering::Acquire);
|
||||
if value & mask != 0 {
|
||||
Poll::Ready(LocalRegisterCopy::new(value))
|
||||
} else {
|
||||
self.notify.register(cx.waker());
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
// impl<H: SdhciHost> From<H> for Sdhci<H> {
|
||||
// fn from(value: H) -> Self {
|
||||
// Self {
|
||||
// inner: SdhciInner { host: value },
|
||||
//
|
||||
// interrupt_status: InterruptStatus::new(),
|
||||
// command_lock: AsyncMutex::new(()),
|
||||
//
|
||||
// card_present: AtomicBool::new(false),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
impl TryFrom<u16> for HostVersion {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
||||
match value & 0xFF {
|
||||
0x00 => Ok(Self::V1_00),
|
||||
0x01 => Ok(Self::V2_00),
|
||||
0x02 => Ok(Self::V3_00),
|
||||
0x03 => Ok(Self::V4_00),
|
||||
0x04 => Ok(Self::V4_10),
|
||||
0x05 => Ok(Self::V4_20),
|
||||
_ => Err(Error::InvalidArgument),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Capabilities {
|
||||
fn from_host<H: SdhciHost>(host: &H) -> Result<Self, Error> {
|
||||
let version = HostVersion::try_from(host.read2(0xFE))?;
|
||||
log::info!("HC version: {version:?}");
|
||||
|
||||
let reg_caps_0 = host.read4(regs::CAPABILITIES::Register::ADDRESS);
|
||||
let reg_caps_1 = host.read4(regs::CAPABILITIES::Register::ADDRESS + 4);
|
||||
let reg_caps = LocalRegisterCopy::<u64, regs::CAPABILITIES::Register>::new(
|
||||
(reg_caps_0 as u64) | ((reg_caps_1 as u64) << 32),
|
||||
);
|
||||
|
||||
let base_frequency = reg_caps.read(regs::CAPABILITIES::BASE_CLOCK_FREQ);
|
||||
let base_frequency = if base_frequency == 0 {
|
||||
None
|
||||
} else {
|
||||
if version < HostVersion::V3_00 {
|
||||
assert_eq!(base_frequency & !0b111111, 0);
|
||||
}
|
||||
Some(base_frequency.mhz())
|
||||
};
|
||||
let clock_multiplier = match reg_caps.read(regs::CAPABILITIES::BASE_CLOCK_FREQ) {
|
||||
0 => None,
|
||||
n => Some(n as u32 + 1),
|
||||
};
|
||||
|
||||
if let Some(base_frequency) = base_frequency {
|
||||
log::info!("SD base frequency: {base_frequency}");
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
version,
|
||||
base_frequency,
|
||||
clock_multiplier,
|
||||
adma2_support: reg_caps.matches_all(regs::CAPABILITIES::ADMA2_SUPPORT::SET),
|
||||
sdma_support: reg_caps.matches_all(regs::CAPABILITIES::SDMA_SUPPORT::SET),
|
||||
voltage_support_1_8v: reg_caps.matches_all(regs::CAPABILITIES::VOLTAGE_1_8V::SET),
|
||||
voltage_support_3_0v: reg_caps.matches_all(regs::CAPABILITIES::VOLTAGE_3_0V::SET),
|
||||
voltage_support_3_3v: reg_caps.matches_all(regs::CAPABILITIES::VOLTAGE_3_3V::SET),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: SdhciHost> Sdhci<H> {
|
||||
pub fn from_host(host: H) -> Result<Self, Error> {
|
||||
let capabilities = Capabilities::from_host(&host)?;
|
||||
let inner = SdhciInner { capabilities, host };
|
||||
Ok(Self {
|
||||
inner,
|
||||
dma: OneTimeInit::new(),
|
||||
interrupt_status: InterruptStatus::new(),
|
||||
command_lock: AsyncMutex::new(()),
|
||||
card_present: AtomicBool::new(false),
|
||||
})
|
||||
}
|
||||
|
||||
async fn late_init(self: Arc<Self>) -> Result<(), Error> {
|
||||
log::info!("{}: initialize", self.display_name());
|
||||
|
||||
self.inner.software_reset().await?;
|
||||
self.inner.set_default_interrupt_mask();
|
||||
self.inner.set_vdd1_power();
|
||||
self.inner.set_clock_rate(400u64.khz()).await?;
|
||||
self.inner.set_clock_mode(ClockMode::All).await?;
|
||||
|
||||
if self.inner.is_card_inserted() {
|
||||
self.card_present.store(true, Ordering::Release);
|
||||
runtime::spawn(self.clone().card_task())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn try_setup_card(self: &Arc<Self>) -> Result<Arc<SdCard<H>>, Error> {
|
||||
// Enable SD bus clock
|
||||
self.inner.set_clock_mode(ClockMode::All).await?;
|
||||
|
||||
let mut last_error = Error::InvalidArgument;
|
||||
for _ in 0..3 {
|
||||
match SdCard::setup(self.clone()).await {
|
||||
Ok(card) => return Ok(card),
|
||||
Err(error) => {
|
||||
log::error!("Card setup error: {error:?}");
|
||||
self.inner.set_clock_mode(ClockMode::Internal).await?;
|
||||
|
||||
last_error = error;
|
||||
runtime::sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(last_error)
|
||||
}
|
||||
|
||||
async fn card_task(self: Arc<Self>) {
|
||||
log::info!("Card inserted, try setup");
|
||||
|
||||
let card = match Self::try_setup_card(&self).await {
|
||||
Ok(card) => card,
|
||||
Err(error) => {
|
||||
log::error!("Card setup error: {error:?}");
|
||||
self.card_present.store(false, Ordering::Release);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
log::info!(
|
||||
"Set up card: rca={:#04x}, capacity={}GiB",
|
||||
card.rca,
|
||||
(card.csd.capacity * card.csd.read_block_size as u64) / (1024 * 1024 * 1024)
|
||||
);
|
||||
|
||||
// Register card as a block device
|
||||
register_sdcard(card, true);
|
||||
}
|
||||
|
||||
async fn receive_single_block_pio(
|
||||
&self,
|
||||
buffer: &mut [MaybeUninit<u8>],
|
||||
block_size: usize,
|
||||
) -> Result<(), Error> {
|
||||
assert_eq!(block_size % 4, 0);
|
||||
|
||||
let words = bytemuck::cast_slice_mut::<_, u32>(unsafe { buffer.assume_init_mut() });
|
||||
|
||||
self.wait_for_interrupt(EVENT_MASK::BUFFER_READ_READY::SET)
|
||||
.await?;
|
||||
|
||||
for word in words {
|
||||
*word = self.inner.get::<regs::BUFFER_DATA::Register>();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn send_single_block_pio(&self, buffer: &[u8], block_size: usize) -> Result<(), Error> {
|
||||
assert_eq!(block_size % 4, 0);
|
||||
|
||||
let words = bytemuck::cast_slice::<_, u32>(buffer);
|
||||
|
||||
for word in words {
|
||||
self.wait_for_interrupt(EVENT_MASK::BUFFER_WRITE_READY::SET)
|
||||
.await?;
|
||||
|
||||
self.inner.set::<regs::BUFFER_DATA::Register>(*word);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn read_single_block_pio<'a>(
|
||||
&self,
|
||||
address: u32,
|
||||
buffer: &'a mut DmaSliceMut<'a, MaybeUninit<u8>>,
|
||||
) -> Result<(), Error> {
|
||||
assert_eq!(buffer.len(), 512);
|
||||
self.submit_command::<command::CMD17, _>(address, buffer)
|
||||
.await
|
||||
.inspect_err(|e| log::error!("CMD17 error: {e:?}"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn write_single_block_pio<'a>(
|
||||
&self,
|
||||
address: u32,
|
||||
buffer: &'a DmaSlice<'a, u8>,
|
||||
) -> Result<(), Error> {
|
||||
assert_eq!(buffer.len(), 512);
|
||||
self.submit_command::<command::CMD24, _>(address, buffer)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn submit_command<'a, C: SdCommand, T: Into<C::Transfer<'a>>>(
|
||||
&self,
|
||||
argument: C::Argument,
|
||||
transfer: T,
|
||||
) -> Result<C::Response, Error> {
|
||||
let transfer = transfer.into();
|
||||
let argument = argument.into_u32();
|
||||
|
||||
let _guard = self.command_lock.lock().await;
|
||||
|
||||
let response_bits = match C::INDEX {
|
||||
SdCommandIndex::Cmd(index) => {
|
||||
log::trace!(">> CMD{index} : {argument:#x}");
|
||||
self.submit_command_inner::<C>(argument, index, Some(&transfer))
|
||||
.await
|
||||
}
|
||||
SdCommandIndex::ACmd(index) => {
|
||||
log::trace!(">> ACMD{index} : {argument:#x}");
|
||||
self.submit_command_inner::<command::CMD55>(0, 55, None)
|
||||
.await?;
|
||||
self.submit_command_inner::<C>(argument, index, Some(&transfer))
|
||||
.await
|
||||
}
|
||||
}?;
|
||||
log::trace!("<< {:x?}", response_bits);
|
||||
let response = C::Response::from_bits(&response_bits);
|
||||
|
||||
transfer.perform_transfer(self).await?;
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
async fn submit_command_inner<'a, C: SdCommand>(
|
||||
&self,
|
||||
argument: u32,
|
||||
index: u8,
|
||||
transfer: Option<&C::Transfer<'a>>,
|
||||
) -> Result<[u32; 4], Error> {
|
||||
// TODO only wait on DAT inhbit if DAT line is required
|
||||
self.inner
|
||||
.wait_inhibit(true, true, Duration::from_secs(1))
|
||||
.await
|
||||
.inspect_err(|_| log::warn!("CMD/DAT inhbit ready timeout"))?;
|
||||
|
||||
let response_type = match C::Response::BIT_WIDTH {
|
||||
SdResponseWidth::None => regs::COMMAND::RESPONSE_TYPE::None,
|
||||
SdResponseWidth::W48 => regs::COMMAND::RESPONSE_TYPE::Response48,
|
||||
SdResponseWidth::W48b => regs::COMMAND::RESPONSE_TYPE::Response48b,
|
||||
SdResponseWidth::W136 => regs::COMMAND::RESPONSE_TYPE::Response136,
|
||||
};
|
||||
|
||||
// let status = self.grab_command().await;
|
||||
let index_check = if C::Response::INDEX_CHECK {
|
||||
regs::COMMAND::COMMAND_INDEX_CHECK::SET
|
||||
} else {
|
||||
regs::COMMAND::COMMAND_INDEX_CHECK::CLEAR
|
||||
};
|
||||
let crc_check = if C::Response::CRC_CHECK {
|
||||
regs::COMMAND::COMMAND_CRC_CHECK::SET
|
||||
} else {
|
||||
regs::COMMAND::COMMAND_CRC_CHECK::CLEAR
|
||||
};
|
||||
|
||||
let command_bits = if let Some(transfer) = transfer {
|
||||
self.inner
|
||||
.set::<regs::BLOCK_SIZE::Register>(transfer.block_size());
|
||||
self.inner
|
||||
.set::<regs::BLOCK_COUNT::Register>(transfer.block_count());
|
||||
|
||||
transfer.command_bits() + regs::COMMAND::DATA_PRESENT_SELECT::SET
|
||||
} else {
|
||||
FieldValue::none()
|
||||
};
|
||||
self.inner.set::<regs::ARGUMENT::Register>(argument);
|
||||
self.inner.write(
|
||||
response_type
|
||||
+ index_check
|
||||
+ crc_check
|
||||
+ command_bits
|
||||
+ regs::COMMAND::COMMAND_TYPE::Normal
|
||||
+ regs::COMMAND::COMMAND_INDEX.val(index as u32),
|
||||
);
|
||||
|
||||
match runtime::with_timeout(
|
||||
self.wait_for_interrupt(EVENT_MASK::COMMAND_COMPLETE::SET),
|
||||
Duration::from_secs(1),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(res) => {
|
||||
res?;
|
||||
}
|
||||
Err(_) => {
|
||||
// TODO abort the command?
|
||||
log::warn!("Command complete timeout");
|
||||
return Err(Error::TimedOut);
|
||||
}
|
||||
}
|
||||
|
||||
let response_bits = match C::Response::BIT_WIDTH {
|
||||
SdResponseWidth::None => [0; 4],
|
||||
SdResponseWidth::W48 | SdResponseWidth::W48b => self.inner.read_response_48(),
|
||||
SdResponseWidth::W136 => self.inner.read_response_136(),
|
||||
};
|
||||
|
||||
Ok(response_bits)
|
||||
}
|
||||
|
||||
async fn wait_for_interrupt(
|
||||
&self,
|
||||
mask: FieldValue<u32, EVENT_MASK::Register>,
|
||||
) -> Result<LocalRegisterCopy<u32, EVENT_MASK::Register>, Error> {
|
||||
// Include errors
|
||||
let mask = mask.value | 0xFFFF0000;
|
||||
|
||||
loop {
|
||||
let status = self.interrupt_status.wait(mask).await;
|
||||
|
||||
if status.get() & 0xFFFF0000 != 0 {
|
||||
todo!("Handle errors")
|
||||
}
|
||||
|
||||
// Otherwise matches the expected mask
|
||||
break Ok(status);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dma_allocator(&self) -> &Arc<dyn DmaAllocator> {
|
||||
self.dma.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: SdhciHost> Device for Sdhci<H> {
|
||||
unsafe fn init(self: Arc<Self>, cx: DeviceInitContext) -> Result<(), Error> {
|
||||
self.dma.init(cx.dma_allocator.clone());
|
||||
unsafe { self.inner.host.init(cx) }?;
|
||||
unsafe { self.inner.host.init_irq(self.clone()) }?;
|
||||
runtime::spawn(self.clone().late_init())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
self.inner.host.name()
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: SdhciHost> InterruptHandler for Sdhci<H> {
|
||||
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||
let status = self.inner.clear_status();
|
||||
self.interrupt_status.signal(status.get());
|
||||
status.get() != 0
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RegisterAccess<T: UIntLike> {
|
||||
fn read_register(&self, address: usize) -> T;
|
||||
fn write_register(&self, address: usize, value: T);
|
||||
}
|
||||
|
||||
impl<H: SdhciHost> RegisterAccess<u8> for SdhciInner<H> {
|
||||
fn read_register(&self, address: usize) -> u8 {
|
||||
self.host.read1(address)
|
||||
}
|
||||
fn write_register(&self, address: usize, value: u8) {
|
||||
self.host.write1(address, value);
|
||||
}
|
||||
}
|
||||
impl<H: SdhciHost> RegisterAccess<u16> for SdhciInner<H> {
|
||||
fn read_register(&self, address: usize) -> u16 {
|
||||
self.host.read2(address)
|
||||
}
|
||||
fn write_register(&self, address: usize, value: u16) {
|
||||
self.host.write2(address, value);
|
||||
}
|
||||
}
|
||||
impl<H: SdhciHost> RegisterAccess<u32> for SdhciInner<H> {
|
||||
fn read_register(&self, address: usize) -> u32 {
|
||||
self.host.read4(address)
|
||||
}
|
||||
fn write_register(&self, address: usize, value: u32) {
|
||||
self.host.write4(address, value);
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: SdhciHost> SdhciInner<H> {
|
||||
// async fn wait_command_inhibit(&self, timeout: Duration) -> Result<(), Error> {
|
||||
// const POLL: Duration = Duration::from_millis(25);
|
||||
|
||||
// let deadline = monotonic_time() + timeout;
|
||||
|
||||
// while self.command_inhibit() || self.data_inhibit() {
|
||||
// let now = monotonic_time();
|
||||
// if now >= deadline {
|
||||
// log::error!("sdhci: timeout waiting for command inhibit");
|
||||
// return Err(Error::TimedOut);
|
||||
// }
|
||||
// let sleep = deadline.sub_time(&now).unwrap_or_default().min(POLL);
|
||||
// runtime::sleep(sleep).await;
|
||||
// }
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// fn command_inhibit(&self) -> bool {
|
||||
// self.matches_all(regs::PRESENT_STATE::COMMAND_INHIBIT_CMD::SET)
|
||||
// }
|
||||
// fn data_inhibit(&self) -> bool {
|
||||
// self.matches_all(regs::PRESENT_STATE::COMMAND_INHIBIT_DAT::SET)
|
||||
// }
|
||||
|
||||
fn write<R: Reg + RegisterLongName>(&self, value: FieldValue<R::Repr, R>)
|
||||
where
|
||||
Self: RegisterAccess<R::Repr>,
|
||||
{
|
||||
self.set::<R>(value.value);
|
||||
}
|
||||
|
||||
fn modify<R: Reg + RegisterLongName>(&self, modify: FieldValue<R::Repr, R>)
|
||||
where
|
||||
Self: RegisterAccess<R::Repr>,
|
||||
R::Repr: BitAndAssign,
|
||||
{
|
||||
let mut value = self.get::<R>();
|
||||
value &= !modify.mask();
|
||||
value |= modify.value;
|
||||
self.set::<R>(value);
|
||||
}
|
||||
|
||||
fn matches_all<R: Reg + RegisterLongName>(&self, pattern: FieldValue<R::Repr, R>) -> bool
|
||||
where
|
||||
Self: RegisterAccess<R::Repr>,
|
||||
{
|
||||
pattern.matches_all(self.get::<R>())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get<R: Reg>(&self) -> R::Repr
|
||||
where
|
||||
Self: RegisterAccess<R::Repr>,
|
||||
{
|
||||
self.read_register(R::ADDRESS)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set<R: Reg>(&self, value: R::Repr)
|
||||
where
|
||||
Self: RegisterAccess<R::Repr>,
|
||||
{
|
||||
self.write_register(R::ADDRESS, value);
|
||||
}
|
||||
|
||||
pub async fn software_reset(&self) -> Result<(), Error> {
|
||||
self.write(SOFTWARE_RESET::SOFTWARE_RESET_ALL::SET);
|
||||
runtime::sleep_wait(Duration::from_secs(1), Duration::from_millis(20), || {
|
||||
self.matches_all(SOFTWARE_RESET::SOFTWARE_RESET_ALL::CLEAR)
|
||||
})
|
||||
.await
|
||||
// self.write(SOFTWARE_RESET::SOFTWARE_RESET_ALL::SET);
|
||||
// let mut timeout = 1000000;
|
||||
// while timeout > 0 {
|
||||
// if self.matches_all(SOFTWARE_RESET::SOFTWARE_RESET_ALL::CLEAR) {
|
||||
// break;
|
||||
// }
|
||||
// timeout -= 1;
|
||||
// }
|
||||
// if timeout == 0 {
|
||||
// Err(Error::TimedOut)
|
||||
// } else {
|
||||
// Ok(())
|
||||
// }
|
||||
}
|
||||
|
||||
pub async fn wait_inhibit(
|
||||
&self,
|
||||
wait_cmd: bool,
|
||||
wait_dat: bool,
|
||||
timeout: Duration,
|
||||
) -> Result<(), Error> {
|
||||
runtime::sleep_wait(timeout, Duration::from_millis(20), || {
|
||||
(!wait_cmd || self.matches_all(PRESENT_STATE::COMMAND_INHIBIT_CMD::CLEAR))
|
||||
&& (!wait_dat || self.matches_all(PRESENT_STATE::COMMAND_INHIBIT_DAT::CLEAR))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn wait_clock_stable(&self) -> Result<(), Error> {
|
||||
// TODO doesn't work for some reason
|
||||
runtime::sleep_wait(
|
||||
Duration::from_millis(100),
|
||||
Duration::from_millis(10),
|
||||
|| self.matches_all(regs::CLOCK_CONTROL::INTERNAL_CLOCK_STABLE::SET),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub fn set_default_interrupt_mask(&self) {
|
||||
let mask = 0xFFFF0000
|
||||
| (EVENT_MASK::COMMAND_COMPLETE::SET
|
||||
+ EVENT_MASK::TRANSFER_COMPLETE::SET
|
||||
+ EVENT_MASK::BUFFER_READ_READY::SET
|
||||
+ EVENT_MASK::BUFFER_WRITE_READY::SET)
|
||||
.value;
|
||||
self.set::<INTERRUPT_STATUS_ENABLE::Register>(mask);
|
||||
self.set::<INTERRUPT_SIGNAL_ENABLE::Register>(mask);
|
||||
}
|
||||
|
||||
pub async fn set_clock_rate(&self, freq: Hertz) -> Result<(), Error> {
|
||||
let want_divider = if let Some(base_freq) = self.capabilities.base_frequency {
|
||||
log::info!("Want frequency {freq}, base frequency {base_freq}");
|
||||
base_freq / freq
|
||||
} else {
|
||||
log::warn!("Set SD clock without known base frequency");
|
||||
16
|
||||
};
|
||||
let sd_was_enabled = self.matches_all(regs::CLOCK_CONTROL::SD_CLOCK_ENABLE::SET);
|
||||
let internal_was_enabled =
|
||||
self.matches_all(regs::CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::SET);
|
||||
|
||||
self.wait_inhibit(true, true, Duration::from_secs(1))
|
||||
.await?;
|
||||
|
||||
if sd_was_enabled || internal_was_enabled {
|
||||
self.modify(regs::CLOCK_CONTROL::SD_CLOCK_ENABLE::CLEAR);
|
||||
}
|
||||
if internal_was_enabled {
|
||||
self.modify(regs::CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::CLEAR);
|
||||
}
|
||||
|
||||
// Pick a matching divider
|
||||
let div2 = want_divider / 2;
|
||||
let divider_lo = (div2 & 0xFF) as u16;
|
||||
let divider_hi = (div2 >> 8) as u16;
|
||||
if self.capabilities.version < HostVersion::V3_00 {
|
||||
todo!("8-bit divided clock mode");
|
||||
}
|
||||
assert_eq!(divider_hi & !0x3, 0);
|
||||
|
||||
self.modify(
|
||||
regs::CLOCK_CONTROL::SDCLK_FREQ_LOWER.val(divider_lo)
|
||||
+ regs::CLOCK_CONTROL::SDCLK_FREQ_UPPER.val(divider_hi)
|
||||
+ regs::CLOCK_CONTROL::CLOCK_GENERATOR_SELECT::ModeDivided,
|
||||
);
|
||||
|
||||
if internal_was_enabled {
|
||||
// Wait for clock to stabilize
|
||||
self.modify(regs::CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::SET);
|
||||
self.wait_clock_stable()
|
||||
.await
|
||||
.inspect_err(|_| log::warn!("Clock stabilization timeout"))?;
|
||||
}
|
||||
|
||||
if sd_was_enabled && internal_was_enabled {
|
||||
self.modify(regs::CLOCK_CONTROL::SD_CLOCK_ENABLE::SET);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_vdd1_power(&self) {
|
||||
self.write(
|
||||
POWER_CONTROL::SD_BUS_POWER_VDD1::SET + POWER_CONTROL::SD_BUS_VOLTAGE_VDD1::Voltage3_3v,
|
||||
);
|
||||
|
||||
// // let tmp = self.host.read1(Self::REG_POWER_CONTROL);
|
||||
// // self.host.write1(Self::REG_POWER_CONTROL, 0xF);
|
||||
}
|
||||
|
||||
pub async fn set_clock_mode(&self, mode: ClockMode) -> Result<(), Error> {
|
||||
match mode {
|
||||
ClockMode::None => {
|
||||
self.modify(
|
||||
CLOCK_CONTROL::SD_CLOCK_ENABLE::CLEAR
|
||||
+ CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::CLEAR,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
ClockMode::Internal => {
|
||||
self.modify(CLOCK_CONTROL::SD_CLOCK_ENABLE::CLEAR);
|
||||
|
||||
if self.matches_all(CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::CLEAR) {
|
||||
self.modify(CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::SET);
|
||||
self.wait_clock_stable().await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
ClockMode::All => {
|
||||
if self.matches_all(CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::CLEAR) {
|
||||
self.modify(CLOCK_CONTROL::INTERNAL_CLOCK_ENABLE::SET);
|
||||
self.wait_clock_stable().await?;
|
||||
}
|
||||
self.modify(CLOCK_CONTROL::SD_CLOCK_ENABLE::SET);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
// if enable {
|
||||
// self.modify(CLOCK_CONTROL::SD_CLOCK_ENABLE::SET);
|
||||
// self.wait_clock_stable().await
|
||||
// } else {
|
||||
// self.modify(CLOCK_CONTROL::SD_CLOCK_ENABLE::CLEAR);
|
||||
// Ok(())
|
||||
// }
|
||||
}
|
||||
|
||||
pub fn is_card_inserted(&self) -> bool {
|
||||
self.matches_all(PRESENT_STATE::CARD_INSERTED::SET)
|
||||
}
|
||||
|
||||
pub fn clear_status(&self) -> LocalRegisterCopy<u32, EVENT_MASK::Register> {
|
||||
let value = self.get::<regs::INTERRUPT_STATUS::Register>();
|
||||
self.set::<regs::INTERRUPT_STATUS::Register>(value);
|
||||
LocalRegisterCopy::new(value)
|
||||
}
|
||||
|
||||
pub fn read_response_136(&self) -> [u32; 4] {
|
||||
[
|
||||
self.get::<regs::RESPONSE0::Register>(),
|
||||
self.get::<regs::RESPONSE1::Register>(),
|
||||
self.get::<regs::RESPONSE2::Register>(),
|
||||
self.get::<regs::RESPONSE3::Register>(),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn read_response_48(&self) -> [u32; 4] {
|
||||
[self.get::<regs::RESPONSE0::Register>(), 0, 0, 0]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register_sdcard<H: SdhciHost>(card: Arc<SdCard<H>>, probe: bool) {
|
||||
// TODO card index allocation
|
||||
let name = format!("mmc0");
|
||||
log::info!("Register SD Card: {name}");
|
||||
devfs::add_named_block_device(card.clone(), name.clone(), FileMode::new(0o600)).ok();
|
||||
|
||||
if probe {
|
||||
runtime::spawn(async move {
|
||||
let name = name;
|
||||
log::info!("Probing partitions for {name}");
|
||||
probe_partitions(card, |index, partition| {
|
||||
let partition_name = format!("{name}p{}", index + 1);
|
||||
devfs::add_named_block_device(
|
||||
Arc::new(partition),
|
||||
partition_name,
|
||||
FileMode::new(0o600),
|
||||
)
|
||||
.ok();
|
||||
})
|
||||
.await
|
||||
.inspect_err(|error| log::error!("{name}: partition probe failed: {error:?}"))
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,400 @@
|
||||
#![allow(non_camel_case_types, non_snake_case)]
|
||||
|
||||
use tock_registers::{register_bitfields, UIntLike};
|
||||
|
||||
pub trait Reg {
|
||||
const ADDRESS: usize;
|
||||
type Repr: UIntLike;
|
||||
}
|
||||
|
||||
macro_rules! define_register {
|
||||
($vis:vis $name:ident: $repr:ident @ $address:literal) => {
|
||||
$vis mod $name {
|
||||
pub struct Register;
|
||||
}
|
||||
impl Reg for $name::Register {
|
||||
const ADDRESS: usize = $address;
|
||||
type Repr = $repr;
|
||||
}
|
||||
};
|
||||
(
|
||||
$vis:vis $name:ident: $repr:ident @ $address:literal {
|
||||
$( $field:ident OFFSET($offset:literal) NUMBITS($numbits:literal) [
|
||||
$( $fieldVariant:ident = $fieldVariantValue:literal ),* $(,)?
|
||||
] ),+ $(,)?
|
||||
}
|
||||
) => {
|
||||
tock_registers::register_bitfields! {
|
||||
$repr,
|
||||
$vis $name [
|
||||
$( $field OFFSET($offset) NUMBITS($numbits) [
|
||||
$( $fieldVariant = $fieldVariantValue ),*
|
||||
] ),+
|
||||
],
|
||||
}
|
||||
impl Reg for $name::Register {
|
||||
const ADDRESS: usize = $address;
|
||||
type Repr = $repr;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
pub EVENT_MASK [
|
||||
BUFFER_READ_READY OFFSET(5) NUMBITS(1) [],
|
||||
BUFFER_WRITE_READY OFFSET(4) NUMBITS(1) [],
|
||||
DMA_INTERRUPT OFFSET(3) NUMBITS(1) [],
|
||||
BLOCK_GAP_EVENT OFFSET(2) NUMBITS(1) [],
|
||||
TRANSFER_COMPLETE OFFSET(1) NUMBITS(1) [],
|
||||
COMMAND_COMPLETE OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
}
|
||||
|
||||
define_register!(pub BLOCK_SIZE: u16 @ 0x04);
|
||||
define_register!(pub BLOCK_COUNT: u16 @ 0x06);
|
||||
|
||||
define_register!(pub ARGUMENT: u32 @ 0x08);
|
||||
define_register!(pub COMMAND: u32 @ 0x0C {
|
||||
// Transfer mode
|
||||
DMA_ENABLE OFFSET(0) NUMBITS(1) [],
|
||||
BLOCK_COUNT_ENABLE OFFSET(1) NUMBITS(1) [],
|
||||
AUTO_CMD_ENABLE OFFSET(2) NUMBITS(2) [],
|
||||
DATA_TRANSFER_DIRECTION OFFSET(4) NUMBITS(1) [
|
||||
HostToCard = 0,
|
||||
CardToHost = 1,
|
||||
],
|
||||
MULTI_BLOCK_SELECT OFFSET(5) NUMBITS(1) [
|
||||
SingleBlock = 0,
|
||||
MultiBlock = 1,
|
||||
],
|
||||
RESPONSE_TYPE_R1_R5 OFFSET(6) NUMBITS(1) [
|
||||
R1 = 0,
|
||||
R5 = 1,
|
||||
],
|
||||
RESPONSE_ERROR_CHECK OFFSET(7) NUMBITS(1) [],
|
||||
RESPONSE_INTERRUPT_DISABLE OFFSET(8) NUMBITS(1) [],
|
||||
// Command
|
||||
RESPONSE_TYPE OFFSET(16) NUMBITS(2) [
|
||||
None = 0,
|
||||
Response136 = 1,
|
||||
Response48 = 2,
|
||||
Response48b = 3,
|
||||
],
|
||||
SUB_COMMAND_FLAG OFFSET(18) NUMBITS(1) [],
|
||||
COMMAND_CRC_CHECK OFFSET(19) NUMBITS(1) [],
|
||||
COMMAND_INDEX_CHECK OFFSET(20) NUMBITS(1) [],
|
||||
DATA_PRESENT_SELECT OFFSET(21) NUMBITS(1) [],
|
||||
COMMAND_TYPE OFFSET(22) NUMBITS(2) [
|
||||
Normal = 0,
|
||||
Suspend = 1,
|
||||
Resume = 2,
|
||||
Abort = 3,
|
||||
],
|
||||
COMMAND_INDEX OFFSET(24) NUMBITS(6) [],
|
||||
});
|
||||
define_register!(pub RESPONSE0: u32 @ 0x10);
|
||||
define_register!(pub RESPONSE1: u32 @ 0x14);
|
||||
define_register!(pub RESPONSE2: u32 @ 0x18);
|
||||
define_register!(pub RESPONSE3: u32 @ 0x1C);
|
||||
define_register!(pub BUFFER_DATA: u32 @ 0x20);
|
||||
define_register!(pub PRESENT_STATE: u32 @ 0x24 {
|
||||
COMMAND_INHIBIT_CMD OFFSET(0) NUMBITS(1) [],
|
||||
COMMAND_INHIBIT_DAT OFFSET(1) NUMBITS(1) [],
|
||||
DAT_LINE_ACTIVE OFFSET(2) NUMBITS(1) [],
|
||||
RETUNING_REQUEST OFFSET(3) NUMBITS(1) [],
|
||||
DAT_LINE_UPPER OFFSET(4) NUMBITS(4) [],
|
||||
WRITE_TRANSFER_ACTIVE OFFSET(8) NUMBITS(1) [],
|
||||
READ_TRANSFER_ACTIVE OFFSET(9) NUMBITS(1) [],
|
||||
BUFFER_WRITE_ENABLE OFFSET(10) NUMBITS(1) [],
|
||||
BUFFER_READ_ENABLE OFFSET(11) NUMBITS(1) [],
|
||||
CARD_INSERTED OFFSET(16) NUMBITS(1) [],
|
||||
CARD_STATE_STABLE OFFSET(17) NUMBITS(1) [],
|
||||
CARD_DETECT_PIN_LEVEL OFFSET(18) NUMBITS(1) [],
|
||||
WRITE_PROTECT_LEVEL OFFSET(19) NUMBITS(1) [],
|
||||
DAT_LINE_LOWER OFFSET(20) NUMBITS(4) [],
|
||||
CMD_LINE_LEVEL OFFSET(24) NUMBITS(1) [],
|
||||
HOST_REGULATOR_V_STABLE OFFSET(25) NUMBITS(1) [],
|
||||
CMD_NOT_ISSUED_BY_ERROR OFFSET(27) NUMBITS(1) [],
|
||||
SUB_CMD_STATUS OFFSET(28) NUMBITS(1) [],
|
||||
DORMANT_STATE OFFSET(29) NUMBITS(1) [],
|
||||
LANE_SYNC OFFSET(30) NUMBITS(1) [],
|
||||
UHS2_IF_DETECT OFFSET(31) NUMBITS(1) [],
|
||||
});
|
||||
define_register!(pub HOST_CONTROL1: u8 @ 0x28 {
|
||||
LED_CONTROL OFFSET(0) NUMBITS(1) [],
|
||||
});
|
||||
define_register!(pub POWER_CONTROL: u8 @ 0x29 {
|
||||
SD_BUS_POWER_VDD1 OFFSET(0) NUMBITS(1) [],
|
||||
SD_BUS_VOLTAGE_VDD1 OFFSET(1) NUMBITS(3) [
|
||||
Voltage1_8v = 0b101,
|
||||
Voltage3_0v = 0b110,
|
||||
Voltage3_3v = 0b111,
|
||||
],
|
||||
SD_BUS_POWER_VDD2 OFFSET(4) NUMBITS(1) [],
|
||||
SD_BUS_VOLTAGE_VDD2 OFFSET(5) NUMBITS(3) [
|
||||
Voltage1_8v = 0b101,
|
||||
],
|
||||
});
|
||||
define_register!(pub CLOCK_CONTROL: u16 @ 0x2C {
|
||||
INTERNAL_CLOCK_ENABLE OFFSET(0) NUMBITS(1) [],
|
||||
INTERNAL_CLOCK_STABLE OFFSET(1) NUMBITS(1) [],
|
||||
SD_CLOCK_ENABLE OFFSET(2) NUMBITS(1) [],
|
||||
PLL_ENABLE OFFSET(3) NUMBITS(1) [],
|
||||
CLOCK_GENERATOR_SELECT OFFSET(5) NUMBITS(1) [
|
||||
ModeDivided = 0,
|
||||
ModeProgrammable = 1,
|
||||
],
|
||||
SDCLK_FREQ_UPPER OFFSET(6) NUMBITS(2) [],
|
||||
SDCLK_FREQ_LOWER OFFSET(8) NUMBITS(8) [],
|
||||
});
|
||||
define_register!(pub SOFTWARE_RESET: u8 @ 0x2F {
|
||||
SOFTWARE_RESET_ALL OFFSET(0) NUMBITS(1) [],
|
||||
RESET_CMD_LINE OFFSET(1) NUMBITS(1) [],
|
||||
RESET_DAT_LINE OFFSET(2) NUMBITS(1) [],
|
||||
});
|
||||
define_register!(pub INTERRUPT_STATUS: u32 @ 0x30);
|
||||
define_register!(pub INTERRUPT_STATUS_ENABLE: u32 @ 0x34);
|
||||
define_register!(pub INTERRUPT_SIGNAL_ENABLE: u32 @ 0x38);
|
||||
define_register!(pub CAPABILITIES: u64 @ 0x40 {
|
||||
BASE_CLOCK_FREQ OFFSET(8) NUMBITS(8) [],
|
||||
ADMA2_SUPPORT OFFSET(19) NUMBITS(1) [],
|
||||
SDMA_SUPPORT OFFSET(22) NUMBITS(1) [],
|
||||
VOLTAGE_3_3V OFFSET(24) NUMBITS(1) [],
|
||||
VOLTAGE_3_0V OFFSET(25) NUMBITS(1) [],
|
||||
VOLTAGE_1_8V OFFSET(26) NUMBITS(1) [],
|
||||
CLOCK_MULTIPLIER OFFSET(48) NUMBITS(8) [],
|
||||
ADMA3_SUPPORT OFFSET(59) NUMBITS(1) [],
|
||||
});
|
||||
|
||||
// use core::time::Duration;
|
||||
//
|
||||
// use libk::{
|
||||
// error::Error,
|
||||
// task::runtime::{psleep, pwait},
|
||||
// };
|
||||
// use tock_registers::{
|
||||
// interfaces::{ReadWriteable, Readable, Writeable},
|
||||
// register_bitfields, register_structs,
|
||||
// registers::ReadWrite,
|
||||
// };
|
||||
//
|
||||
// register_bitfields! {
|
||||
// u32,
|
||||
// WORD9 [
|
||||
// COMMAND_INHIBIT_CMD OFFSET(0) NUMBITS(1) [],
|
||||
// COMMAND_INHIBIT_DAT OFFSET(1) NUMBITS(1) [],
|
||||
// DAT_LINE_HIGH OFFSET(2) NUMBITS(1) [],
|
||||
// RETUNING_REQUEST OFFSET(3) NUMBITS(1) [],
|
||||
// DAT_SIGNAL_LEVEL OFFSET(4) NUMBITS(4) [],
|
||||
// WRITE_TRANSFER_ACTIVE OFFSET(8) NUMBITS(1) [],
|
||||
// READ_TRANSFER_ACTIVE OFFSET(9) NUMBITS(1) [],
|
||||
// BUFFER_WRITE_ENABLE OFFSET(10) NUMBITS(1) [],
|
||||
// BUFFER_READ_ENABLE OFFSET(11) NUMBITS(1) [],
|
||||
// CARD_INSERTED OFFSET(16) NUMBITS(1) [],
|
||||
// CARD_STATE_STABLE OFFSET(17) NUMBITS(1) [],
|
||||
// CARD_DETECT_PIN_LEVEL OFFSET(18) NUMBITS(1) [],
|
||||
// WRITE_PROTECT_PIN_LEVEL OFFSET(19) NUMBITS(1) [],
|
||||
// DAT_LINE_LOW OFFSET(20) NUMBITS(4) [],
|
||||
// CMD_LINE OFFSET(24) NUMBITS(1) [],
|
||||
// HOST_REGULATOR_V_STABLE OFFSET(25) NUMBITS(1) [],
|
||||
// COMMAND_NOT_ISSUED OFFSET(27) NUMBITS(1) [],
|
||||
// SUB_COMMAND_STATUS OFFSET(28) NUMBITS(1) [],
|
||||
// DORMANT_STATE OFFSET(29) NUMBITS(1) [],
|
||||
// LANE_SYNC OFFSET(30) NUMBITS(1) [],
|
||||
// UHS2_IF_DETECT OFFSET(31) NUMBITS(1) [],
|
||||
// ],
|
||||
// WORD10 [
|
||||
// LED_CONTROL OFFSET(0) NUMBITS(1) [],
|
||||
// DATA_TRANSFER_WIDTH OFFSET(1) NUMBITS(1) [
|
||||
// Width1b = 0,
|
||||
// Width4b = 1,
|
||||
// ],
|
||||
// HIGH_SPEED_ENABLE OFFSET(2) NUMBITS(1) [],
|
||||
// DMA_SELECT OFFSET(3) NUMBITS(2) [
|
||||
// ModeSdma = 0,
|
||||
// Adma2 = 2,
|
||||
// Adma2or3 = 3,
|
||||
// ],
|
||||
// EXT_DATA_TRANSFER_WIDTH OFFSET(5) NUMBITS(1) [
|
||||
// WidthNormal = 0,
|
||||
// Width8b = 1,
|
||||
// ],
|
||||
// CARD_DETECT_TEST_LEVEL OFFSET(6) NUMBITS(1) [
|
||||
// NotInserted = 0,
|
||||
// Inserted = 1,
|
||||
// ],
|
||||
// CARD_DETECT_SIGNAL_SELECT OFFSET(7) NUMBITS(1) [
|
||||
// SdcdSignal = 0,
|
||||
// TestSignal = 1,
|
||||
// ],
|
||||
// SD_BUS_POWER_VDD1 OFFSET(8) NUMBITS(1) [],
|
||||
// SD_BUS_VOLTAGE_VDD1 OFFSET(9) NUMBITS(3) [
|
||||
// Voltage1_8v = 5,
|
||||
// Voltage3_0v = 6,
|
||||
// Voltage3_3v = 7,
|
||||
// ],
|
||||
// SD_BUS_POWER_VDD2 OFFSET(12) NUMBITS(1) [],
|
||||
// SD_BUS_VOLTAGE_VDD2 OFFSET(13) NUMBITS(3) [
|
||||
// Voltage1_8v = 5,
|
||||
// NotSupported = 0,
|
||||
// ],
|
||||
// STOP_AT_BLOCK_GAP_REQ OFFSET(16) NUMBITS(1) [],
|
||||
// CONTINUE_REQ OFFSET(17) NUMBITS(1) [],
|
||||
// READ_WAIT_CONTROL OFFSET(18) NUMBITS(1) [],
|
||||
// INTERRUPT_AT_BLOCK_GAP OFFSET(19) NUMBITS(1) [],
|
||||
// WAKEUP_ON_CARD_INTERRUPT OFFSET(24) NUMBITS(1) [],
|
||||
// WAKEUP_ON_CARD_INSERT OFFSET(25) NUMBITS(1) [],
|
||||
// WAKEUP_ON_CARD_REMOVE OFFSET(26) NUMBITS(1) [],
|
||||
// ],
|
||||
// WORD11 [
|
||||
// INTERNAL_CLOCK_ENABLE OFFSET(0) NUMBITS(1) [],
|
||||
// INTERNAL_CLOCK_STABLE OFFSET(1) NUMBITS(1) [],
|
||||
// SD_CLOCK_ENABLE OFFSET(2) NUMBITS(1) [],
|
||||
// PLL_ENABLE OFFSET(3) NUMBITS(1) [],
|
||||
// CLOCK_GENERATOR_SELECT OFFSET(5) NUMBITS(1) [
|
||||
// DividedClock = 0,
|
||||
// ProgrammableClock = 1,
|
||||
// ],
|
||||
// SDCLK_FREQ_UPPER OFFSET(6) NUMBITS(2) [],
|
||||
// SDCLK_FREQ_LOWER OFFSET(8) NUMBITS(8) [],
|
||||
// DATA_TIMEOUT_COUNTER OFFSET(16) NUMBITS(4) [],
|
||||
// SOFTWARE_RESET_ALL OFFSET(24) NUMBITS(1) [],
|
||||
// SOFTWARE_RESET_CMD_LINE OFFSET(25) NUMBITS(1) [],
|
||||
// SOFTWARE_RESET_DAT_LINE OFFSET(26) NUMBITS(1) [],
|
||||
// ],
|
||||
// WORD12 [
|
||||
// COMMAND_COMPLETE OFFSET(0) NUMBITS(1) [],
|
||||
// TRANSFER_COMPLETE OFFSET(1) NUMBITS(1) [],
|
||||
// BLOCK_GAP_EVENT OFFSET(2) NUMBITS(1) [],
|
||||
// DMA_INTERRUPT OFFSET(3) NUMBITS(1) [],
|
||||
// BUFFER_WRITE_READY OFFSET(4) NUMBITS(1) [],
|
||||
// BUFFER_READ_READY OFFSET(5) NUMBITS(1) [],
|
||||
// CARD_INSERTION OFFSET(6) NUMBITS(1) [],
|
||||
// CARD_REMOVAL OFFSET(7) NUMBITS(1) [],
|
||||
// CARD_INTERRUPT OFFSET(8) NUMBITS(1) [],
|
||||
// INT_A OFFSET(9) NUMBITS(1) [],
|
||||
// INT_B OFFSET(10) NUMBITS(1) [],
|
||||
// INT_C OFFSET(11) NUMBITS(1) [],
|
||||
// RETUNING_EVENT OFFSET(12) NUMBITS(1) [],
|
||||
// FX_EVENT OFFSET(13) NUMBITS(1) [],
|
||||
// COMMAND_TIMEOUT_ERROR OFFSET(16) NUMBITS(1) [],
|
||||
// COMMAND_CRC_ERROR OFFSET(17) NUMBITS(1) [],
|
||||
// COMMAND_END_BIT_ERROR OFFSET(18) NUMBITS(1) [],
|
||||
// COMMAND_INDEX_ERROR OFFSET(19) NUMBITS(1) [],
|
||||
// DATA_TIMEOUT_ERROR OFFSET(20) NUMBITS(1) [],
|
||||
// DATA_CRC_ERROR OFFSET(21) NUMBITS(1) [],
|
||||
// DATA_END_BIT_ERROR OFFSET(22) NUMBITS(1) [],
|
||||
// CURRENT_LIMIT_ERROR OFFSET(23) NUMBITS(1) [],
|
||||
// AUTO_CMD_ERROR OFFSET(24) NUMBITS(1) [],
|
||||
// ADMA_ERROR OFFSET(25) NUMBITS(1) [],
|
||||
// TUNING_ERROR OFFSET(26) NUMBITS(1) [],
|
||||
// RESPONSE_ERROR OFFSET(27) NUMBITS(1) [],
|
||||
// ],
|
||||
// WORD15 [
|
||||
// AUTO_CMD12_NOT_EXECUTED OFFSET(0) NUMBITS(1) [],
|
||||
// AUTO_CMD_TIMEOUT_ERROR OFFSET(1) NUMBITS(1) [],
|
||||
// AUTO_CMD_CRC_ERROR OFFSET(2) NUMBITS(1) [],
|
||||
// AUTO_CMD_END_BIT_ERROR OFFSET(3) NUMBITS(1) [],
|
||||
// AUTO_CMD_INDEX_ERROR OFFSET(4) NUMBITS(1) [],
|
||||
// AUTO_CMD_RESPONSE_ERROR OFFSET(5) NUMBITS(1) [],
|
||||
// COMMAND_NOT_ISSUED_CMD12 OFFSET(7) NUMBITS(1) [],
|
||||
// UHS_MODE_SELECT OFFSET(16) NUMBITS(3) [
|
||||
// ModeSdr12 = 0,
|
||||
// ModeSdr25 = 1,
|
||||
// ModeSdr50 = 2,
|
||||
// ModeSdr104 = 3,
|
||||
// ModeDdr50 = 4,
|
||||
// Uhs2 = 7,
|
||||
// ],
|
||||
// SIGNALING_1_8V_ENABLE OFFSET(19) NUMBITS(1) [],
|
||||
// DRIVER_SIGNAL_SELECT OFFSET(20) NUMBITS(2) [
|
||||
// DriverTypeB = 0,
|
||||
// DriverTypeA = 1,
|
||||
// DriverTypeC = 2,
|
||||
// DriverTypeD = 3,
|
||||
// ],
|
||||
// EXECUTE_TUNING OFFSET(22) NUMBITS(1) [],
|
||||
// SAMPLING_CLOCK_SELECT OFFSET(23) NUMBITS(1) [],
|
||||
// UHS2_INTERFACE_ENABLE OFFSET(24) NUMBITS(1) [],
|
||||
// ADMA2_LENGTH_MODE OFFSET(26) NUMBITS(1) [
|
||||
// Length16b = 0,
|
||||
// Length26b = 1,
|
||||
// ],
|
||||
// CMD23_ENABLE OFFSET(27) NUMBITS(1) [],
|
||||
// HOST_V4_ENABLE OFFSET(28) NUMBITS(1) [],
|
||||
// ADDRESS_64_BIT OFFSET(29) NUMBITS(1) [],
|
||||
// ASYNC_INTERRUPT_ENABLE OFFSET(30) NUMBITS(1) [],
|
||||
// PRESET_VALUE_ENABLE OFFSET(31) NUMBITS(1) [],
|
||||
// ],
|
||||
// }
|
||||
//
|
||||
// register_structs! {
|
||||
// #[allow(non_snake_case)]
|
||||
// pub Regs {
|
||||
// // SDMA system address
|
||||
// (0x000 => WORD0: ReadWrite<u32>),
|
||||
// // Block size, block count
|
||||
// (0x004 => WORD1: ReadWrite<u32>),
|
||||
// // Argument
|
||||
// (0x008 => WORD2: ReadWrite<u32>),
|
||||
// // Transfer mode, command
|
||||
// (0x00C => WORD3: ReadWrite<u32>),
|
||||
// // Response0, Response1
|
||||
// (0x010 => WORD4: ReadWrite<u32>),
|
||||
// // Response2, Response3
|
||||
// (0x014 => WORD5: ReadWrite<u32>),
|
||||
// // Response4, Response5
|
||||
// (0x018 => WORD6: ReadWrite<u32>),
|
||||
// // Response6, Response7
|
||||
// (0x01C => WORD7: ReadWrite<u32>),
|
||||
// // Buffer data port 0, port 1
|
||||
// (0x020 => WORD8: ReadWrite<u32>),
|
||||
// // Present state
|
||||
// (0x024 => WORD9: ReadWrite<u32, WORD9::Register>),
|
||||
// // Host control 1, power control, block gap control, wakeup control
|
||||
// (0x028 => WORD10: ReadWrite<u32, WORD10::Register>),
|
||||
// // Clock control, timeout control, software reset
|
||||
// (0x02C => WORD11: ReadWrite<u32, WORD11::Register>),
|
||||
// // Normal interrupt status, error interrupt status
|
||||
// (0x030 => WORD12: ReadWrite<u32, WORD12::Register>),
|
||||
// // Normal interrupt status enable, error interrupt status enable
|
||||
// (0x034 => WORD13: ReadWrite<u32, WORD12::Register>),
|
||||
// // Normal interrupt signal enable, error interrupt signal enable
|
||||
// (0x038 => WORD14: ReadWrite<u32, WORD12::Register>),
|
||||
// // Auto CMD error status, host control 2
|
||||
// (0x03C => WORD15: ReadWrite<u32, WORD15::Register>),
|
||||
// (0x040 => WORD16: ReadWrite<u32>),
|
||||
// (0x044 => WORD17: ReadWrite<u32>),
|
||||
// (0x048 => WORD18: ReadWrite<u32>),
|
||||
// (0x04C => WORD19: ReadWrite<u32>),
|
||||
// (0x050 => _0),
|
||||
// (0x100 => @END),
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl Regs {
|
||||
// pub fn software_reset_all(&self, timeout: Duration) -> Result<(), Error> {
|
||||
// self.WORD11.modify(WORD11::SOFTWARE_RESET_ALL::SET);
|
||||
// pwait(timeout, Duration::from_millis(50), || {
|
||||
// self.WORD11.matches_all(WORD11::SOFTWARE_RESET_ALL::CLEAR)
|
||||
// })?;
|
||||
// Ok(())
|
||||
// }
|
||||
//
|
||||
// pub fn setup_default_interrupts(&self) {
|
||||
// self.WORD13
|
||||
// .write(WORD12::COMMAND_COMPLETE::SET + WORD12::TRANSFER_COMPLETE::SET);
|
||||
//
|
||||
// self.WORD11.modify(WORD11::SOFTWARE_RESET_ALL::CLEAR);
|
||||
//
|
||||
// // Setup VDD1 power
|
||||
// self.WORD10
|
||||
// .modify(WORD10::SD_BUS_POWER_VDD1::SET + WORD10::SD_BUS_VOLTAGE_VDD1::Voltage3_3v);
|
||||
//
|
||||
// loop {
|
||||
// psleep(Duration::from_secs(1));
|
||||
// log::info!("{:#x}", self.WORD9.get());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@@ -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 })?;
|
||||
|
||||
@@ -50,6 +50,14 @@ impl Div<u32> for Hertz {
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<Hertz> for Hertz {
|
||||
type Output = u32;
|
||||
|
||||
fn div(self, rhs: Hertz) -> Self::Output {
|
||||
(self.0 / rhs.0) as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Hertz {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let (scale, suffix) = match self.0 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -6,7 +6,7 @@ use yggdrasil_abi::error::Error;
|
||||
|
||||
mod controller;
|
||||
mod macros;
|
||||
mod registry;
|
||||
pub(crate) mod registry;
|
||||
mod syscon;
|
||||
mod traits;
|
||||
mod tree;
|
||||
|
||||
@@ -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;
|
||||
@@ -46,7 +46,7 @@ pub struct Node {
|
||||
bus_size_cells: usize,
|
||||
interrupt_parent: Option<Phandle>,
|
||||
name: Option<&'static str>,
|
||||
compatible: Option<TProp<'static>>,
|
||||
pub(crate) compatible: Option<TProp<'static>>,
|
||||
|
||||
// Hierachy info
|
||||
children: OneTimeInit<Vec<Arc<Node>>>,
|
||||
@@ -150,6 +150,7 @@ impl Node {
|
||||
if let Some(status) = self.property("status") {
|
||||
let status = status.as_str().unwrap_or("");
|
||||
if status == "disabled" {
|
||||
// log::info!("Not probing disabled {:?}", self.compatible);
|
||||
return (None, parent_bus);
|
||||
}
|
||||
}
|
||||
@@ -410,7 +411,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 +419,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))
|
||||
|
||||
@@ -23,7 +23,62 @@ pub mod property;
|
||||
pub mod tree;
|
||||
pub mod util;
|
||||
|
||||
use core::fmt;
|
||||
|
||||
pub use node::DeviceTreeNodeExt;
|
||||
pub use property::DeviceTreePropertyRead;
|
||||
pub use tree::{DeviceTree, TNode, TProp};
|
||||
pub use util::DeviceTreeMemoryRegionIter;
|
||||
|
||||
fn dump_node(level: log::Level, node: &driver::Node, depth: usize) {
|
||||
fn indent(level: log::Level, x: usize) {
|
||||
for _ in 0..x {
|
||||
log::log!(target: ":raw", level, " ");
|
||||
}
|
||||
}
|
||||
|
||||
// fn field<D: fmt::Debug>(level: log::Level, depth: usize, name: &str, value: &D) {
|
||||
// indent(level, depth);
|
||||
// log::log!(target: ":raw", level, "{name} = {value:?},\n");
|
||||
// }
|
||||
|
||||
fn field_list<'a, D: fmt::Debug + ?Sized + 'a, I: IntoIterator<Item = &'a D>>(
|
||||
level: log::Level,
|
||||
depth: usize,
|
||||
name: &str,
|
||||
value: I,
|
||||
) {
|
||||
indent(level, depth);
|
||||
log::log!(target: ":raw", level, "{name} = ");
|
||||
for (i, item) in value.into_iter().enumerate() {
|
||||
if i != 0 {
|
||||
log::log!(target: ":raw", level, ", ");
|
||||
}
|
||||
log::log!(target: ":raw", level, "{item:?}");
|
||||
}
|
||||
log::log!(target: ":raw", level, ",\n");
|
||||
}
|
||||
|
||||
indent(level, depth);
|
||||
log::log!(target: ":raw", level, "\"{}\" {{\n", node.name().unwrap_or(""));
|
||||
|
||||
if let Some(compatible) = node.compatible.as_ref() {
|
||||
field_list(level, depth + 1, "compatible", compatible.as_str_list());
|
||||
}
|
||||
// for value in node.compatible
|
||||
|
||||
for (i, node) in node.children().enumerate() {
|
||||
if i == 0 {
|
||||
log::log!(target: ":raw", level, "\n");
|
||||
}
|
||||
dump_node(level, node, depth + 1);
|
||||
}
|
||||
indent(level, depth);
|
||||
log::log!(target: ":raw", level, "}},\n");
|
||||
}
|
||||
|
||||
/// Dump the device tree to debug output
|
||||
pub fn dump(level: log::Level) {
|
||||
let root = driver::registry::ROOT.get();
|
||||
dump_node(level, root, 0);
|
||||
}
|
||||
|
||||
@@ -324,7 +324,7 @@ impl<'a, T> DmaSlice<'a, T> {
|
||||
}
|
||||
|
||||
// TODO subslicing
|
||||
pub fn into_parts(self) -> (&'a DmaBuffer<[T]>, Range<usize>) {
|
||||
pub unsafe fn into_parts(self) -> (&'a DmaBuffer<[T]>, Range<usize>) {
|
||||
(self.buffer, self.range)
|
||||
}
|
||||
|
||||
@@ -353,7 +353,7 @@ impl<'a, T> DmaSliceMut<'a, T> {
|
||||
}
|
||||
|
||||
// TODO subslicing
|
||||
pub fn into_parts(self) -> (&'a mut DmaBuffer<[T]>, Range<usize>) {
|
||||
pub unsafe fn into_parts(self) -> (&'a mut DmaBuffer<[T]>, Range<usize>) {
|
||||
(self.buffer, self.range)
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -9,5 +9,6 @@ mod timer;
|
||||
pub use executor::{run_to_completion, spawn, spawn_async_worker};
|
||||
pub use task_queue::init_task_queue;
|
||||
pub use timer::{
|
||||
maybe_timeout, psleep, pwait, pwait_try, sleep, sleep_until, tick, with_timeout, SleepFuture,
|
||||
maybe_timeout, psleep, pwait, pwait_try, sleep, sleep_until, sleep_wait, tick, with_timeout,
|
||||
SleepFuture,
|
||||
};
|
||||
|
||||
@@ -201,6 +201,25 @@ pub fn pwait_try<P: Fn() -> Result<bool, Error>>(
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn sleep_wait<P: Fn() -> bool>(
|
||||
timeout: Duration,
|
||||
poll_rate: Duration,
|
||||
predicate: P,
|
||||
) -> Result<(), Error> {
|
||||
let now = monotonic_time();
|
||||
let deadline = now + timeout;
|
||||
|
||||
loop {
|
||||
if predicate() {
|
||||
return Ok(());
|
||||
}
|
||||
if monotonic_time() > deadline {
|
||||
return Err(Error::TimedOut);
|
||||
}
|
||||
sleep(poll_rate).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_timeout<'a, T: 'a, F: Future<Output = T> + Send + 'a>(
|
||||
fut: F,
|
||||
timeout: Duration,
|
||||
|
||||
@@ -350,7 +350,6 @@ impl Thread {
|
||||
if debug.tracer.is_some() {
|
||||
let timestamp = monotonic_time();
|
||||
debug.store_state(frame);
|
||||
// log::info!("TRACE {payload:?}");
|
||||
self.events.trace.write(TraceEvent {
|
||||
suspend,
|
||||
timestamp,
|
||||
@@ -514,11 +513,7 @@ impl Thread {
|
||||
|
||||
/// Returns `true` if the thread is a tracee of given process
|
||||
pub fn is_tracee_of(&self, pid: ProcessId) -> bool {
|
||||
{
|
||||
let tracer = self.debug.lock().tracer;
|
||||
// log::info!("{:?}'s tracer: {:?}", self.id, tracer);
|
||||
tracer == Some(pid)
|
||||
}
|
||||
self.debug.lock().tracer == Some(pid)
|
||||
}
|
||||
|
||||
pub fn attach_trace(&self, tracer: ProcessId) -> Result<(), Error> {
|
||||
@@ -582,13 +577,11 @@ impl Thread {
|
||||
DebugControl::Detach => todo!(),
|
||||
DebugControl::Resume => {
|
||||
let single_step = debug::Resume::load_request(input)?;
|
||||
// log::info!("Resume single_step={single_step}");
|
||||
self.resume(single_step);
|
||||
debug::Resume::store_response(&(), buffer)
|
||||
}
|
||||
DebugControl::SetTraceFlags => {
|
||||
let flags = debug::SetTraceFlags::load_request(buffer)?;
|
||||
// log::info!("SetTraceFlags {flags:?}");
|
||||
self.set_trace_flags(flags);
|
||||
debug::SetTraceFlags::store_response(&(), buffer)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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! {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -71,6 +71,9 @@ extern crate ygg_driver_virtio_blk;
|
||||
extern crate ygg_driver_virtio_gpu;
|
||||
extern crate ygg_driver_virtio_net;
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||
extern crate ygg_driver_mmc_generic_sdhci;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_arch = "x86_64")] {
|
||||
extern crate ygg_driver_net_igbe;
|
||||
@@ -86,6 +89,7 @@ cfg_if::cfg_if! {
|
||||
}
|
||||
|
||||
pub mod arch;
|
||||
|
||||
pub mod device;
|
||||
pub mod fs;
|
||||
pub mod init;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
Generated
-2
@@ -2970,8 +2970,6 @@ name = "strace"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"cross",
|
||||
"libc",
|
||||
"runtime",
|
||||
]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
pub(crate) mod sys;
|
||||
|
||||
pub mod debug;
|
||||
pub mod fs;
|
||||
pub mod io;
|
||||
pub mod mem;
|
||||
|
||||
@@ -1,64 +1,6 @@
|
||||
use std::io;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TraceEvent {
|
||||
SyscallEntry(u64, [u64; 6]),
|
||||
SyscallExit(i64),
|
||||
Exited,
|
||||
}
|
||||
|
||||
pub trait ChildTrace {
|
||||
fn next_event(&mut self) -> io::Result<TraceEvent>;
|
||||
fn resume(&mut self) -> io::Result<()>;
|
||||
fn kill(&mut self) -> io::Result<()>;
|
||||
|
||||
unsafe fn peek_u8(&mut self, address: usize) -> io::Result<u8> {
|
||||
let aligned = address & !7;
|
||||
let shift = (address & 7) << 3;
|
||||
let word = self.peek_u64(aligned)?;
|
||||
Ok((word >> shift) as u8)
|
||||
}
|
||||
unsafe fn peek_u32(&mut self, address: usize) -> io::Result<u32> {
|
||||
eprintln!("PEEK U32 @ {address:#x}");
|
||||
assert_eq!(address & 3, 0);
|
||||
let aligned = address & !7;
|
||||
let shift = (address & 7) << 3;
|
||||
let word = self.peek_u64(aligned)?;
|
||||
Ok((word >> shift) as u32)
|
||||
}
|
||||
unsafe fn peek_u64(&mut self, address: usize) -> io::Result<u64>;
|
||||
|
||||
unsafe fn peek_bytes(&mut self, address: usize, buffer: &mut [u8]) -> io::Result<()> {
|
||||
for i in 0..buffer.len() {
|
||||
buffer[i] = self.peek_u8(address + i)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn peek_cstr(&mut self, address: usize, buffer: &mut [u8]) -> io::Result<usize> {
|
||||
let mut len = 0;
|
||||
while len < buffer.len() - 1 {
|
||||
let byte = self.peek_u8(address + len)?;
|
||||
buffer[len] = byte;
|
||||
if byte == 0 {
|
||||
break;
|
||||
}
|
||||
len += 1;
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
unsafe fn peek_usize(&mut self, address: usize) -> io::Result<usize> {
|
||||
// FIXME I only support 64-bit archs
|
||||
Ok(self.peek_u64(address)? as _)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CommandSpawnExt {
|
||||
type ChildTrace: ChildTrace;
|
||||
|
||||
fn create_session(&mut self) -> io::Result<&mut Self>;
|
||||
fn create_process_group(&mut self) -> io::Result<&mut Self>;
|
||||
// TODO options for tracing across subchildren, etc
|
||||
fn spawn_with_trace(&mut self) -> io::Result<Self::ChildTrace>;
|
||||
}
|
||||
|
||||
@@ -10,12 +10,10 @@ pub mod time;
|
||||
pub mod timer;
|
||||
|
||||
use std::{
|
||||
ffi::{c_int, c_void},
|
||||
ffi::c_int,
|
||||
io,
|
||||
mem::MaybeUninit,
|
||||
os::{fd::RawFd, unix::process::CommandExt},
|
||||
process::Command,
|
||||
ptr::null_mut,
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
@@ -28,126 +26,7 @@ pub use socket::{BorrowedAddressImpl, LocalPacketSocketImpl, OwnedAddressImpl};
|
||||
pub use term::RawStdinImpl;
|
||||
pub use timer::TimerFdImpl;
|
||||
|
||||
use crate::process::{ChildTrace, CommandSpawnExt, TraceEvent};
|
||||
|
||||
pub struct ChildTraceImpl {
|
||||
child: libc::pid_t,
|
||||
}
|
||||
|
||||
impl ChildTrace for ChildTraceImpl {
|
||||
fn next_event(&mut self) -> io::Result<TraceEvent> {
|
||||
let ev = loop {
|
||||
unsafe {
|
||||
let mut status = 0;
|
||||
if libc::waitpid(self.child, &mut status, 0) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
if libc::WIFSTOPPED(status) {
|
||||
let stopsig = libc::WSTOPSIG(status);
|
||||
if stopsig == libc::SIGTRAP | 0x80 {
|
||||
let mut syscall_info = MaybeUninit::<libc::ptrace_syscall_info>::zeroed();
|
||||
if libc::ptrace(
|
||||
libc::PTRACE_GET_SYSCALL_INFO,
|
||||
self.child,
|
||||
size_of::<libc::ptrace_syscall_info>(),
|
||||
syscall_info.as_mut_ptr(),
|
||||
) < 0
|
||||
{
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
let syscall_info = syscall_info.assume_init();
|
||||
match syscall_info.op {
|
||||
libc::PTRACE_SYSCALL_INFO_EXIT => {
|
||||
break TraceEvent::SyscallExit(syscall_info.u.exit.sval);
|
||||
}
|
||||
libc::PTRACE_SYSCALL_INFO_ENTRY => {
|
||||
break TraceEvent::SyscallEntry(
|
||||
syscall_info.u.entry.nr,
|
||||
syscall_info.u.entry.args,
|
||||
);
|
||||
}
|
||||
_ => self.resume()?,
|
||||
}
|
||||
} else if stopsig == libc::SIGTRAP {
|
||||
let event = (status >> 16) & 0xffff;
|
||||
eprintln!("event = {event}");
|
||||
match event {
|
||||
libc::PTRACE_EVENT_EXIT => {
|
||||
// Ignore
|
||||
eprintln!("PTRACE_EVENT_EXIT");
|
||||
self.resume()?;
|
||||
}
|
||||
libc::PTRACE_EVENT_VFORK => {
|
||||
eprintln!("PTRACE_EVENT_VFORK");
|
||||
self.resume()?;
|
||||
}
|
||||
libc::PTRACE_EVENT_EXEC => {
|
||||
// Ignore
|
||||
eprintln!("PTRACE_EVENT_EXEC");
|
||||
self.resume()?;
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
} else {
|
||||
todo!("signum == {stopsig}");
|
||||
}
|
||||
} else if libc::WIFEXITED(status) {
|
||||
break TraceEvent::Exited;
|
||||
} else {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(ev)
|
||||
}
|
||||
|
||||
fn kill(&mut self) -> io::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn resume(&mut self) -> io::Result<()> {
|
||||
if unsafe {
|
||||
libc::ptrace(
|
||||
libc::PTRACE_SYSCALL,
|
||||
self.child,
|
||||
null_mut::<c_void>(),
|
||||
null_mut::<c_void>(),
|
||||
)
|
||||
} == 0
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::last_os_error())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn peek_u64(&mut self, address: usize) -> io::Result<u64> {
|
||||
libc::__errno_location().write_volatile(0);
|
||||
let word = libc::ptrace(
|
||||
libc::PTRACE_PEEKDATA,
|
||||
self.child,
|
||||
address,
|
||||
null_mut::<c_void>(),
|
||||
);
|
||||
if libc::__errno_location().read_volatile() != 0 {
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(word as _)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ChildTraceImpl {
|
||||
fn drop(&mut self) {
|
||||
let mut ignore = 0;
|
||||
unsafe {
|
||||
libc::ptrace(libc::PTRACE_DETACH, self.child);
|
||||
libc::waitpid(self.child, &mut ignore, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
use crate::process::CommandSpawnExt;
|
||||
|
||||
fn dummy_sigint() {}
|
||||
|
||||
@@ -182,8 +61,6 @@ pub fn clone_fd(fd: RawFd) -> io::Result<RawFd> {
|
||||
}
|
||||
|
||||
impl CommandSpawnExt for Command {
|
||||
type ChildTrace = ChildTraceImpl;
|
||||
|
||||
fn create_process_group(&mut self) -> io::Result<&mut Self> {
|
||||
Ok(self.process_group(0))
|
||||
}
|
||||
@@ -192,54 +69,4 @@ impl CommandSpawnExt for Command {
|
||||
// TODO
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn spawn_with_trace(&mut self) -> io::Result<Self::ChildTrace> {
|
||||
unsafe {
|
||||
match libc::fork() {
|
||||
0 => {
|
||||
// Child
|
||||
// if libc::ptrace(libc::PTRACE_TRACEME, libc::getpid()) != 0 {
|
||||
// panic!("PTRACE_TRACEME: {}", io::Error::last_os_error());
|
||||
// }
|
||||
libc::raise(libc::SIGSTOP);
|
||||
panic!("exec error: {}", self.exec());
|
||||
}
|
||||
code if code < 0 => {
|
||||
// Error
|
||||
Err(io::Error::last_os_error())
|
||||
}
|
||||
pid => {
|
||||
let options = libc::PTRACE_O_TRACESYSGOOD | libc::PTRACE_O_TRACEEXEC;
|
||||
let mut ignore = 0;
|
||||
// Parent
|
||||
if libc::ptrace(
|
||||
libc::PTRACE_SEIZE,
|
||||
pid,
|
||||
null_mut::<c_void>(),
|
||||
null_mut::<c_void>(),
|
||||
) != 0
|
||||
{
|
||||
// TODO waitpid child? kill child?
|
||||
eprintln!("PTRACE_SEIZE error");
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
if libc::waitpid(pid, &mut ignore, 0) < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
if libc::ptrace(libc::PTRACE_SETOPTIONS, pid, null_mut::<c_void>(), options)
|
||||
!= 0
|
||||
{
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
libc::ptrace(
|
||||
libc::PTRACE_CONT,
|
||||
pid,
|
||||
null_mut::<c_void>(),
|
||||
null_mut::<c_void>(),
|
||||
);
|
||||
Ok(ChildTraceImpl { child: pid })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,20 +11,8 @@ pub mod timer;
|
||||
|
||||
use std::{
|
||||
io,
|
||||
mem::MaybeUninit,
|
||||
os::{
|
||||
fd::RawFd,
|
||||
yggdrasil::process::{ChildExt, CommandExt},
|
||||
},
|
||||
process::{Child, Command},
|
||||
};
|
||||
|
||||
use runtime::{
|
||||
abi::{
|
||||
arch::SavedFrame,
|
||||
debug::{self, TraceFlags},
|
||||
},
|
||||
rt::{debug::debug_control, process::ThreadEvent},
|
||||
os::{fd::RawFd, yggdrasil::process::CommandExt},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
pub use mem::{FileMappingImpl, SharedMemoryImpl};
|
||||
@@ -36,131 +24,7 @@ pub use socket::{BorrowedAddressImpl, LocalPacketSocketImpl, OwnedAddressImpl};
|
||||
pub use term::RawStdinImpl;
|
||||
pub use timer::TimerFdImpl;
|
||||
|
||||
use crate::process::{ChildTrace, CommandSpawnExt, TraceEvent};
|
||||
|
||||
pub struct ChildTraceImpl {
|
||||
#[allow(unused)]
|
||||
child: Child,
|
||||
buffer: [u8; 512],
|
||||
tid: u32,
|
||||
}
|
||||
|
||||
impl ChildTraceImpl {
|
||||
fn attach(child: Child) -> io::Result<Self> {
|
||||
let mut buffer = [0; 512];
|
||||
let tid = child.main_thread_id()?;
|
||||
|
||||
debug_control::<debug::SetTraceFlags>(
|
||||
tid,
|
||||
&mut buffer,
|
||||
&(TraceFlags::SYSCALL_ENTRY | TraceFlags::SYSCALL_EXIT),
|
||||
)
|
||||
.map_err(io::Error::from)
|
||||
.unwrap();
|
||||
|
||||
Ok(Self { child, tid, buffer })
|
||||
}
|
||||
|
||||
fn read_regs(&mut self) -> io::Result<SavedFrame> {
|
||||
debug_control::<debug::GetRegisters>(self.tid, &mut self.buffer, &())
|
||||
.map_err(io::Error::from)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(rust_analyzer, target_arch = "aarch64"))]
|
||||
impl ChildTraceImpl {
|
||||
fn read_syscall_entry(&mut self) -> io::Result<(u64, [u64; 6])> {
|
||||
let frame = self.read_regs()?;
|
||||
Ok((
|
||||
frame.gp_regs[8] as _,
|
||||
[
|
||||
frame.gp_regs[0] as _,
|
||||
frame.gp_regs[1] as _,
|
||||
frame.gp_regs[2] as _,
|
||||
frame.gp_regs[3] as _,
|
||||
frame.gp_regs[4] as _,
|
||||
frame.gp_regs[5] as _,
|
||||
],
|
||||
))
|
||||
}
|
||||
|
||||
fn read_syscall_exit(&mut self) -> io::Result<u64> {
|
||||
let frame = self.read_regs()?;
|
||||
Ok(frame.gp_regs[0] as _)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(rust_analyzer, target_arch = "x86_64"))]
|
||||
impl ChildTraceImpl {
|
||||
fn read_syscall_entry(&mut self) -> io::Result<(u64, [u64; 6])> {
|
||||
let frame = self.read_regs()?;
|
||||
Ok((
|
||||
frame.rax as _,
|
||||
[
|
||||
frame.rdi as _,
|
||||
frame.rsi as _,
|
||||
frame.rdx as _,
|
||||
frame.r10 as _,
|
||||
frame.r8 as _,
|
||||
frame.r9 as _,
|
||||
],
|
||||
))
|
||||
}
|
||||
|
||||
fn read_syscall_exit(&mut self) -> io::Result<u64> {
|
||||
let frame = self.read_regs()?;
|
||||
Ok(frame.rax as _)
|
||||
}
|
||||
}
|
||||
|
||||
impl ChildTrace for ChildTraceImpl {
|
||||
unsafe fn peek_u64(&mut self, address: usize) -> io::Result<u64> {
|
||||
let word = debug_control::<debug::ReadMemory>(self.tid, &mut self.buffer, &address)?;
|
||||
Ok(word as _)
|
||||
}
|
||||
|
||||
fn kill(&mut self) -> io::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn resume(&mut self) -> io::Result<()> {
|
||||
debug_control::<debug::Resume>(self.tid, &mut self.buffer, &false).map_err(io::Error::from)
|
||||
}
|
||||
|
||||
fn next_event(&mut self) -> io::Result<TraceEvent> {
|
||||
loop {
|
||||
let mut event = MaybeUninit::uninit();
|
||||
unsafe { runtime::rt::sys::wait_thread(self.tid, &mut event) }
|
||||
.map_err(io::Error::from)?;
|
||||
let event = unsafe { event.assume_init() };
|
||||
|
||||
match event {
|
||||
ThreadEvent::Trace(trace) => {
|
||||
let event = match trace.payload {
|
||||
debug::TraceEventPayload::SyscallEntry(_seq) => {
|
||||
let (nr, args) = self.read_syscall_entry()?;
|
||||
TraceEvent::SyscallEntry(nr, args)
|
||||
}
|
||||
debug::TraceEventPayload::SyscallExit(_seq) => {
|
||||
let status = self.read_syscall_exit()?;
|
||||
TraceEvent::SyscallExit(status as _)
|
||||
}
|
||||
debug::TraceEventPayload::SingleStep => {
|
||||
self.resume()?;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
break Ok(event);
|
||||
}
|
||||
ThreadEvent::Exited => {
|
||||
// TODO status
|
||||
break Ok(TraceEvent::Exited);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
use crate::process::CommandSpawnExt;
|
||||
|
||||
pub fn set_sigint_handler(_handler: fn()) {}
|
||||
|
||||
@@ -181,13 +45,6 @@ pub fn clone_fd(fd: RawFd) -> io::Result<RawFd> {
|
||||
}
|
||||
|
||||
impl CommandSpawnExt for Command {
|
||||
type ChildTrace = ChildTraceImpl;
|
||||
|
||||
fn spawn_with_trace(&mut self) -> io::Result<Self::ChildTrace> {
|
||||
unsafe { self.attach_tracing() };
|
||||
self.spawn().and_then(ChildTraceImpl::attach)
|
||||
}
|
||||
|
||||
fn create_process_group(&mut self) -> io::Result<&mut Self> {
|
||||
let group = unsafe { runtime::rt::sys::create_process_group() };
|
||||
Ok(self.process_group(group))
|
||||
|
||||
@@ -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,14 @@ 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"
|
||||
@@ -70,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"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,68 @@
|
||||
#![feature(yggdrasil_os, if_let_guard)]
|
||||
|
||||
fn main() {}
|
||||
// use std::{fmt::{LowerHex, Display}, io, path::PathBuf, process::Command};
|
||||
//
|
||||
// use clap::Parser;
|
||||
// use debugger::{Debugger, SymbolResolver};
|
||||
// use imp::TargetImpl;
|
||||
// use yggdrasil_abi::arch::SavedFrame;
|
||||
//
|
||||
// #[cfg(any(target_arch = "x86_64", target_arch = "x86", rust_analyzer))]
|
||||
// #[path = "x86.rs"]
|
||||
// pub mod imp;
|
||||
// #[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||
// #[path = "aarch64.rs"]
|
||||
// pub mod imp;
|
||||
//
|
||||
// pub mod comm;
|
||||
// pub mod state;
|
||||
//
|
||||
// pub mod debugger;
|
||||
//
|
||||
// #[derive(thiserror::Error, Debug)]
|
||||
// pub enum Error {
|
||||
// #[error("I/O error: {0}")]
|
||||
// IoError(#[from] io::Error),
|
||||
// #[error("Terminal error: {0}")]
|
||||
// TermError(#[from] libterm::Error),
|
||||
// #[error("Debug control error: {0:?}")]
|
||||
// DebugError(yggdrasil_rt::Error),
|
||||
// #[error("Invalid address: {0:?}")]
|
||||
// InvalidAddress(String)
|
||||
// }
|
||||
//
|
||||
// pub trait Target {
|
||||
// type Instruction;
|
||||
// type InstructionFormatter: InstructionFormatter<Self::Instruction>;
|
||||
// type Register: LowerHex;
|
||||
//
|
||||
// fn disassemble(
|
||||
// window: &[u8],
|
||||
// ip: usize,
|
||||
// limit: usize,
|
||||
// ) -> Result<Vec<(usize, Self::Instruction)>, Error>;
|
||||
// fn new_instruction_formatter(resolver: SymbolResolver) -> Self::InstructionFormatter;
|
||||
//
|
||||
// fn register_list(frame: &SavedFrame, out: &mut Vec<(String, Self::Register)>);
|
||||
// fn flags_register_as_display(frame: &SavedFrame) -> impl Display;
|
||||
// fn real_ip(frame: &SavedFrame) -> usize;
|
||||
// }
|
||||
//
|
||||
// pub trait InstructionFormatter<I> {
|
||||
// fn format_instruction(&mut self, insn: &I, out: &mut String);
|
||||
// }
|
||||
//
|
||||
// #[derive(Parser)]
|
||||
// struct Args {
|
||||
// program: PathBuf,
|
||||
// }
|
||||
//
|
||||
// fn main() {
|
||||
// let args = Args::parse();
|
||||
//
|
||||
// let command = Command::new(&args.program);
|
||||
// let debug: Debugger<TargetImpl> = Debugger::from_command(&args.program, command).unwrap();
|
||||
//
|
||||
// debug.run().unwrap();
|
||||
// }
|
||||
|
||||
@@ -4,19 +4,9 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
cross.workspace = true
|
||||
runtime.workspace = true
|
||||
|
||||
clap.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "yggdrasil")'.dependencies]
|
||||
runtime.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
libc = "*"
|
||||
|
||||
[dev-dependencies]
|
||||
runtime.workspace = true
|
||||
libc = "*"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
#[cfg(any(rust_analyzer, target_arch = "x86_64"))]
|
||||
#[path = "x86_64.rs"]
|
||||
pub mod imp;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
pub use imp::*;
|
||||
use libc::{c_int, c_uint};
|
||||
|
||||
macro_rules! impl_ioctls {
|
||||
($f:expr, $v:expr, $ty:ty, [$($ioctl:ident),* $(,)?]) => {{
|
||||
#[allow(unreachable_patterns)]
|
||||
let v = match $v as $ty {
|
||||
$(libc::$ioctl => stringify!($ioctl),)*
|
||||
_ => return write!($f, "{:#x}", $v),
|
||||
};
|
||||
write!($f, "{v}")
|
||||
}};
|
||||
}
|
||||
|
||||
fn print_ioctl<W: fmt::Write>(f: &mut W, v: c_uint) -> fmt::Result {
|
||||
impl_ioctls!(
|
||||
f,
|
||||
v,
|
||||
u64,
|
||||
[
|
||||
TCGETS,
|
||||
TCSETS,
|
||||
TCSETSW,
|
||||
TCSETSF,
|
||||
TCGETA,
|
||||
TCSETA,
|
||||
TCSETAW,
|
||||
TCSETAF,
|
||||
TCSBRK,
|
||||
TCXONC,
|
||||
TCFLSH,
|
||||
TIOCEXCL,
|
||||
TIOCNXCL,
|
||||
TIOCSCTTY,
|
||||
TIOCGPGRP,
|
||||
TIOCSPGRP,
|
||||
TIOCOUTQ,
|
||||
TIOCSTI,
|
||||
TIOCGWINSZ,
|
||||
TIOCSWINSZ,
|
||||
TIOCMGET,
|
||||
TIOCMBIS,
|
||||
TIOCMBIC,
|
||||
TIOCMSET,
|
||||
TIOCGSOFTCAR,
|
||||
TIOCSSOFTCAR,
|
||||
FIONREAD,
|
||||
TIOCLINUX,
|
||||
TIOCCONS,
|
||||
TIOCGSERIAL,
|
||||
TIOCSSERIAL,
|
||||
TIOCPKT,
|
||||
FIONBIO,
|
||||
TIOCNOTTY,
|
||||
TIOCSETD,
|
||||
TIOCGETD,
|
||||
TCSBRKP,
|
||||
TIOCSBRK,
|
||||
TIOCCBRK,
|
||||
TIOCGSID,
|
||||
TCGETS2,
|
||||
TCSETS2,
|
||||
TCSETSW2,
|
||||
TCSETSF2,
|
||||
TIOCGRS485,
|
||||
TIOCSRS485,
|
||||
TIOCGPTN,
|
||||
TIOCSPTLCK,
|
||||
TIOCGDEV,
|
||||
TCGETX,
|
||||
TCSETX,
|
||||
TCSETXF,
|
||||
TCSETXW,
|
||||
TIOCSIG,
|
||||
TIOCVHANGUP,
|
||||
TIOCGPKT,
|
||||
TIOCGPTLCK,
|
||||
TIOCGEXCL,
|
||||
TIOCGPTPEER,
|
||||
FIONCLEX,
|
||||
FIOCLEX,
|
||||
FIOASYNC,
|
||||
TIOCSERCONFIG,
|
||||
TIOCSERGWILD,
|
||||
TIOCSERSWILD,
|
||||
TIOCGLCKTRMIOS,
|
||||
TIOCSLCKTRMIOS,
|
||||
TIOCSERGSTRUCT,
|
||||
TIOCSERGETLSR,
|
||||
TIOCSERGETMULTI,
|
||||
TIOCSERSETMULTI,
|
||||
TIOCMIWAIT,
|
||||
TIOCGICOUNT,
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
fn print_prctl<W: fmt::Write>(f: &mut W, c: c_int) -> fmt::Result {
|
||||
impl_ioctls!(
|
||||
f,
|
||||
c,
|
||||
c_int,
|
||||
[
|
||||
PR_SET_PDEATHSIG,
|
||||
PR_GET_PDEATHSIG,
|
||||
PR_GET_DUMPABLE,
|
||||
PR_SET_DUMPABLE,
|
||||
PR_GET_UNALIGN,
|
||||
PR_SET_UNALIGN,
|
||||
PR_GET_KEEPCAPS,
|
||||
PR_SET_KEEPCAPS,
|
||||
PR_GET_FPEMU,
|
||||
PR_SET_FPEMU,
|
||||
PR_GET_FPEXC,
|
||||
PR_SET_FPEXC,
|
||||
PR_GET_TIMING,
|
||||
PR_SET_TIMING,
|
||||
PR_SET_NAME,
|
||||
PR_GET_NAME,
|
||||
PR_GET_ENDIAN,
|
||||
PR_SET_ENDIAN,
|
||||
PR_GET_SECCOMP,
|
||||
PR_SET_SECCOMP,
|
||||
PR_CAPBSET_READ,
|
||||
PR_CAPBSET_DROP,
|
||||
PR_GET_TSC,
|
||||
PR_SET_TSC,
|
||||
PR_GET_SECUREBITS,
|
||||
PR_SET_SECUREBITS,
|
||||
PR_SET_TIMERSLACK,
|
||||
PR_GET_TIMERSLACK,
|
||||
PR_TASK_PERF_EVENTS_DISABLE,
|
||||
PR_TASK_PERF_EVENTS_ENABLE,
|
||||
PR_MCE_KILL,
|
||||
PR_SET_MM,
|
||||
PR_SET_PTRACER,
|
||||
PR_SET_CHILD_SUBREAPER,
|
||||
PR_GET_CHILD_SUBREAPER,
|
||||
PR_SET_NO_NEW_PRIVS,
|
||||
PR_GET_NO_NEW_PRIVS,
|
||||
PR_GET_TID_ADDRESS,
|
||||
PR_SET_THP_DISABLE,
|
||||
PR_GET_THP_DISABLE,
|
||||
PR_MPX_ENABLE_MANAGEMENT,
|
||||
PR_MPX_DISABLE_MANAGEMENT,
|
||||
PR_SET_FP_MODE,
|
||||
PR_GET_FP_MODE,
|
||||
PR_CAP_AMBIENT,
|
||||
PR_GET_SPECULATION_CTRL,
|
||||
PR_SET_SPECULATION_CTRL,
|
||||
PR_SCHED_CORE,
|
||||
PR_SET_MDWE,
|
||||
PR_GET_MDWE,
|
||||
PR_SET_VMA,
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
fn print_access_mode<W: fmt::Write>(f: &mut W, mode: c_int) -> fmt::Result {
|
||||
let s = match mode {
|
||||
libc::F_OK => "F_OK",
|
||||
libc::W_OK => "W_OK",
|
||||
libc::R_OK => "R_OK",
|
||||
libc::X_OK => "X_OK",
|
||||
_ => return write!(f, "{mode:#x}"),
|
||||
};
|
||||
|
||||
write!(f, "{s}")
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
use std::{ffi::c_void, fmt};
|
||||
|
||||
use cross::process::ChildTrace;
|
||||
use libc::{c_char, c_int, c_long, c_uint, c_ulong};
|
||||
|
||||
use crate::definition::{
|
||||
imp::{print_access_mode, print_ioctl, print_prctl},
|
||||
print_cstring, PrintSyscallArgument, Syscall,
|
||||
};
|
||||
|
||||
pub enum SyscallArgument {
|
||||
Fd(c_int),
|
||||
MaybeFd(c_int),
|
||||
Bytes(*const c_char),
|
||||
BytesMut(*const c_char),
|
||||
Size(usize),
|
||||
Filename(*const c_char),
|
||||
OpenOptions(c_int),
|
||||
FileMode(c_int),
|
||||
Ptr(*const c_void),
|
||||
Long(c_long),
|
||||
ULong(c_ulong),
|
||||
Int(c_int),
|
||||
UInt(c_uint),
|
||||
Whence(c_int),
|
||||
Off(libc::off_t),
|
||||
LOff(libc::loff_t),
|
||||
IoctlCmd(c_uint),
|
||||
PrctlCmd(c_int),
|
||||
AccessMode(c_int),
|
||||
AtFd(c_int),
|
||||
}
|
||||
|
||||
impl PrintSyscallArgument for SyscallArgument {
|
||||
fn print_syscall_argument<W: fmt::Write, T: ChildTrace>(&self, f: &mut W, trace: &mut T) {
|
||||
match self {
|
||||
Self::Fd(libc::STDIN_FILENO) => write!(f, "STDIN_FILENO"),
|
||||
Self::Fd(libc::STDOUT_FILENO) => write!(f, "STDOUT_FILENO"),
|
||||
Self::Fd(libc::STDERR_FILENO) => write!(f, "STDERR_FILENO"),
|
||||
Self::Fd(fd) => write!(f, "{fd}"),
|
||||
Self::MaybeFd(-1) => write!(f, "<none>"),
|
||||
Self::MaybeFd(fd) => write!(f, "{fd}"),
|
||||
Self::Bytes(bytes) => write!(f, "{bytes:p}"),
|
||||
Self::BytesMut(bytes) => write!(f, "{bytes:p}"),
|
||||
Self::Size(size) if *size <= 999 => write!(f, "{size}"),
|
||||
Self::Size(size) => write!(f, "{size:#x}"),
|
||||
Self::Filename(filename) => print_cstring(f, trace, filename.addr()),
|
||||
Self::OpenOptions(opts) => write!(f, "{opts:#x}"),
|
||||
Self::FileMode(mode) => write!(f, "0{mode:o}"),
|
||||
Self::Ptr(ptr) => write!(f, "{ptr:p}"),
|
||||
Self::Long(val) => write!(f, "{val}"),
|
||||
Self::ULong(val) => write!(f, "{val}"),
|
||||
Self::Int(val) => write!(f, "{val}"),
|
||||
Self::UInt(val) => write!(f, "{val}"),
|
||||
Self::Whence(libc::SEEK_SET) => write!(f, "SEEK_SET"),
|
||||
Self::Whence(libc::SEEK_CUR) => write!(f, "SEEK_CUR"),
|
||||
Self::Whence(libc::SEEK_END) => write!(f, "SEEK_END"),
|
||||
Self::Whence(w) => write!(f, "{w}"),
|
||||
Self::Off(off) => write!(f, "{off}"),
|
||||
Self::LOff(off) => write!(f, "{off}"),
|
||||
&Self::IoctlCmd(v) => print_ioctl(f, v),
|
||||
&Self::PrctlCmd(v) => print_prctl(f, v),
|
||||
&Self::AccessMode(m) => print_access_mode(f, m),
|
||||
Self::AtFd(libc::AT_FDCWD) => write!(f, "AT_FDCWD"),
|
||||
Self::AtFd(fd) => write!(f, "{fd}"),
|
||||
}
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_syscall_args {
|
||||
($input:expr, [$($collect:expr),*], $i:expr, [$arg:ident, $($tail:ident),+]) => {
|
||||
impl_syscall_args!($input, [$($collect,)* (SyscallArgument::$arg($input[$i] as _))], $i + 1, [$($tail),+])
|
||||
};
|
||||
($input:expr, [$($collect:expr),*], $i:expr, [$arg:ident]) => {
|
||||
vec![$($collect,)* (SyscallArgument::$arg($input[$i] as _))]
|
||||
};
|
||||
($input:expr, [], $i:expr, []) => {
|
||||
vec![]
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_syscall_parse {
|
||||
($(
|
||||
$nr:literal => $name:ident ( $($arg:ident),* )
|
||||
),+ $(,)?) => {
|
||||
pub fn parse_syscall(nr: u64, args: &[u64]) -> Option<Syscall> {
|
||||
Some(match nr {
|
||||
$($nr => Syscall(
|
||||
stringify!($name),
|
||||
impl_syscall_args!(args, [], 0, [$($arg),*])
|
||||
),)+
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_syscall_parse!(
|
||||
0 => read(Fd, BytesMut, Size),
|
||||
1 => write(Fd, Bytes, Size),
|
||||
2 => open(Filename, OpenOptions, FileMode),
|
||||
3 => close(Fd),
|
||||
4 => stat(Filename, BytesMut),
|
||||
5 => fstat(Fd, BytesMut),
|
||||
6 => lstat(Filename, BytesMut),
|
||||
7 => poll(Ptr, ULong, Long),
|
||||
8 => lseek(Fd, Off, Whence),
|
||||
9 => mmap(Ptr, ULong, ULong, ULong, MaybeFd, ULong),
|
||||
10 => mprotect(Ptr, Size, ULong),
|
||||
11 => munmap(Ptr, Size),
|
||||
12 => brk(Ptr),
|
||||
// TODO rt_...
|
||||
16 => ioctl(Fd, IoctlCmd, ULong),
|
||||
17 => pread64(Fd, BytesMut, Size, LOff),
|
||||
21 => access(Filename, AccessMode),
|
||||
157 => prctl(PrctlCmd, ULong, ULong, ULong),
|
||||
218 => set_tid_address(Ptr),
|
||||
334 => rseq(Ptr, UInt, Int, UInt),
|
||||
257 => openat(AtFd, Filename, OpenOptions, FileMode),
|
||||
262 => newfstatat(AtFd, Filename, Ptr, Int),
|
||||
24 => sched_yield(),
|
||||
);
|
||||
@@ -1,67 +0,0 @@
|
||||
use std::fmt;
|
||||
|
||||
use cross::process::ChildTrace;
|
||||
|
||||
#[cfg(any(rust_analyzer, target_os = "linux"))]
|
||||
#[path = "linux/mod.rs"]
|
||||
pub mod imp;
|
||||
#[cfg(any(rust_analyzer, target_os = "yggdrasil"))]
|
||||
#[path = "yggdrasil.rs"]
|
||||
pub mod imp;
|
||||
|
||||
pub use imp::*;
|
||||
|
||||
pub trait PrintSyscallArgument {
|
||||
fn print_syscall_argument<W: fmt::Write, T: ChildTrace>(&self, f: &mut W, trace: &mut T);
|
||||
}
|
||||
|
||||
pub struct Syscall(pub &'static str, pub Vec<SyscallArgument>);
|
||||
|
||||
impl Syscall {
|
||||
pub fn print<W: fmt::Write, T: ChildTrace>(&self, f: &mut W, trace: &mut T) {
|
||||
write!(f, "{}(", self.0).ok();
|
||||
for (i, arg) in self.1.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(f, ", ").ok();
|
||||
}
|
||||
arg.print_syscall_argument(f, trace);
|
||||
}
|
||||
write!(f, ")").ok();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_string<W: fmt::Write, T: ChildTrace>(
|
||||
f: &mut W,
|
||||
trace: &mut T,
|
||||
address: usize,
|
||||
len: usize,
|
||||
) -> fmt::Result {
|
||||
let mut buffer = [0; 64];
|
||||
let len = len.min(buffer.len());
|
||||
if let Ok(()) = unsafe { trace.peek_bytes(address, &mut buffer[..len]) } {
|
||||
if let Ok(s) = std::str::from_utf8(&buffer[..len]) {
|
||||
write!(f, "{s:?}")
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
} else {
|
||||
write!(f, "<error str {address:#x}>")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_cstring<W: fmt::Write, T: ChildTrace>(
|
||||
f: &mut W,
|
||||
trace: &mut T,
|
||||
address: usize,
|
||||
) -> fmt::Result {
|
||||
let mut buffer = [0; 64];
|
||||
if let Ok(len) = unsafe { trace.peek_cstr(address, &mut buffer) } {
|
||||
if let Ok(s) = std::str::from_utf8(&buffer[..len]) {
|
||||
write!(f, "{s:?}")
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
} else {
|
||||
write!(f, "<error str {address:#x}>")
|
||||
}
|
||||
}
|
||||
@@ -1,335 +0,0 @@
|
||||
use std::fmt;
|
||||
|
||||
use cross::process::ChildTrace;
|
||||
use runtime::{abi::SyscallFunction, abi_lib::SyscallRegister, rt::io::RawFd};
|
||||
|
||||
use crate::definition::{print_string, PrintSyscallArgument, Syscall};
|
||||
|
||||
pub enum SyscallArgument {
|
||||
Bytes(u64, u64),
|
||||
BytesMut(u64, u64),
|
||||
MaybePtr(u64),
|
||||
Fd(u64),
|
||||
OptionFd(u64),
|
||||
Ptr(u64),
|
||||
Size(u64),
|
||||
U64(u64),
|
||||
I64(i64),
|
||||
U32(u32),
|
||||
I32(i32),
|
||||
PtrSystemTime(u64),
|
||||
ClockType(u64),
|
||||
Filename(u64, u64),
|
||||
}
|
||||
|
||||
pub enum ArgType {
|
||||
Bytes,
|
||||
BytesMut,
|
||||
MaybePtr,
|
||||
Fd,
|
||||
OptionFd,
|
||||
Ptr,
|
||||
Size,
|
||||
U64,
|
||||
I64,
|
||||
U32,
|
||||
I32,
|
||||
PtrSystemTime,
|
||||
ClockType,
|
||||
Filename,
|
||||
}
|
||||
|
||||
struct SyscallDefinition(SyscallFunction, &'static str, &'static [ArgType]);
|
||||
|
||||
impl PrintSyscallArgument for SyscallArgument {
|
||||
fn print_syscall_argument<W: fmt::Write, T: ChildTrace>(&self, f: &mut W, trace: &mut T) {
|
||||
match self {
|
||||
&Self::MaybePtr(0) => write!(f, "None"),
|
||||
Self::Fd(fd) => {
|
||||
let fd = unsafe { RawFd::from_raw(*fd as _) };
|
||||
match fd {
|
||||
RawFd::STDIN => write!(f, "STDIN"),
|
||||
RawFd::STDOUT => write!(f, "STDOUT"),
|
||||
RawFd::STDERR => write!(f, "STDERR"),
|
||||
_ => write!(f, "{fd:?}"),
|
||||
}
|
||||
}
|
||||
Self::OptionFd(fd) => match Option::<RawFd>::from_syscall_register(*fd as _) {
|
||||
Some(fd) => write!(f, "Some({fd:?})"),
|
||||
None => write!(f, "None"),
|
||||
},
|
||||
Self::MaybePtr(addr) => write!(f, "Some({addr:#x})"),
|
||||
Self::Ptr(ptr) => write!(f, "{ptr:#x}"),
|
||||
Self::U64(val) => write!(f, "{val}"),
|
||||
Self::I64(val) => write!(f, "{val}"),
|
||||
Self::U32(val) => write!(f, "{val}"),
|
||||
Self::I32(val) => write!(f, "{val}"),
|
||||
Self::Size(val) => write!(f, "{val}"),
|
||||
Self::Bytes(addr, len) => write!(f, "&[{addr:#x}; {len}]"),
|
||||
Self::BytesMut(addr, len) => write!(f, "&mut [{addr:#x}; {len}]"),
|
||||
Self::PtrSystemTime(ptr) => write!(f, "{ptr:#x}"),
|
||||
Self::ClockType(1) => write!(f, "RealTime"),
|
||||
Self::ClockType(2) => write!(f, "Monotonic"),
|
||||
Self::ClockType(v) => write!(f, "{v}"),
|
||||
Self::Filename(ptr, len) => print_string(f, trace, *ptr as _, *len as _),
|
||||
}
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
const DEFS: &[SyscallDefinition] = &const {
|
||||
use ArgType::*;
|
||||
[
|
||||
SyscallDefinition(SyscallFunction::GetRandom, "get_random", &[BytesMut]),
|
||||
SyscallDefinition(
|
||||
SyscallFunction::GetClock,
|
||||
"get_clock",
|
||||
&[ClockType, PtrSystemTime],
|
||||
),
|
||||
SyscallDefinition(
|
||||
SyscallFunction::SetClock,
|
||||
"set_clock",
|
||||
&[ClockType, PtrSystemTime],
|
||||
),
|
||||
SyscallDefinition(SyscallFunction::Mount, "mount", &[Ptr]), // TODO
|
||||
SyscallDefinition(SyscallFunction::Unmount, "unmount", &[Ptr]), // TODO
|
||||
SyscallDefinition(SyscallFunction::LoadModule, "load_module", &[Filename]),
|
||||
SyscallDefinition(
|
||||
SyscallFunction::FilesystemControl,
|
||||
"filesystem_control",
|
||||
&[OptionFd, U32, Size],
|
||||
),
|
||||
SyscallDefinition(
|
||||
SyscallFunction::GetSystemInfo,
|
||||
"get_system_info",
|
||||
&[U32, BytesMut],
|
||||
), // TODO
|
||||
// Memory management
|
||||
SyscallDefinition(
|
||||
SyscallFunction::MapMemory,
|
||||
"map_memory",
|
||||
&[MaybePtr, Size, U32, Ptr],
|
||||
),
|
||||
SyscallDefinition(SyscallFunction::UnmapMemory, "unmap_memory", &[Ptr, Size]),
|
||||
// Process/thread management
|
||||
SyscallDefinition(
|
||||
SyscallFunction::CreateProcessGroup,
|
||||
"create_process_group",
|
||||
&[],
|
||||
),
|
||||
SyscallDefinition(
|
||||
SyscallFunction::GetProcessGroupId,
|
||||
"get_process_group_id",
|
||||
&[],
|
||||
),
|
||||
SyscallDefinition(SyscallFunction::ExitProcess, "exit_process", &[U64]), // TODO
|
||||
SyscallDefinition(SyscallFunction::SpawnProcess, "spawn_process", &[Ptr]), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::WaitProcess,
|
||||
"wait_process",
|
||||
&[Ptr, Ptr, U32],
|
||||
), // TODO
|
||||
SyscallDefinition(SyscallFunction::GetPid, "get_pid", &[]),
|
||||
SyscallDefinition(SyscallFunction::GetTid, "get_tid", &[]),
|
||||
SyscallDefinition(SyscallFunction::SpawnThread, "spawn_thread", &[Ptr]), // TODO
|
||||
SyscallDefinition(SyscallFunction::ExitThread, "exit_thread", &[]),
|
||||
SyscallDefinition(SyscallFunction::WaitThread, "wait_thread", &[U32, Ptr]), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::GetThreadOption,
|
||||
"get_thread_option",
|
||||
&[U32, BytesMut],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::SetThreadOption,
|
||||
"set_thread_option",
|
||||
&[U32, Bytes],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::GetProcessOption,
|
||||
"get_process_option",
|
||||
&[U32, U32, BytesMut],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::SetProcessOption,
|
||||
"set_process_option",
|
||||
&[U32, U32, Bytes],
|
||||
), // TODO
|
||||
SyscallDefinition(SyscallFunction::Nanosleep, "nanosleep", &[Ptr, Ptr]), // TODO
|
||||
SyscallDefinition(SyscallFunction::ExitSignal, "exit_signal", &[Ptr]), // TODO
|
||||
SyscallDefinition(SyscallFunction::SendSignal, "send_signal", &[U32, U32]), // TODO
|
||||
SyscallDefinition(SyscallFunction::Mutex, "mutex", &[Ptr, Ptr]), // TODO
|
||||
SyscallDefinition(SyscallFunction::StartSession, "start_session", &[]),
|
||||
// I/O
|
||||
SyscallDefinition(
|
||||
SyscallFunction::Open,
|
||||
"open",
|
||||
&[OptionFd, Filename, U32, U32],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::CheckAccess,
|
||||
"check_access",
|
||||
&[OptionFd, Filename, U32],
|
||||
), // TODO
|
||||
SyscallDefinition(SyscallFunction::Close, "close", &[Fd]),
|
||||
SyscallDefinition(SyscallFunction::Write, "write", &[Fd, Bytes]),
|
||||
SyscallDefinition(SyscallFunction::Read, "read", &[Fd, BytesMut]),
|
||||
SyscallDefinition(SyscallFunction::Seek, "seek", &[Fd, U32, Ptr]), // TODO
|
||||
SyscallDefinition(SyscallFunction::Truncate, "truncate", &[Fd, U64]), // TODO
|
||||
SyscallDefinition(SyscallFunction::Fsync, "fsync", &[Fd, U32]), // TODO
|
||||
SyscallDefinition(SyscallFunction::ReadAt, "read_at", &[Fd, U64, BytesMut]),
|
||||
SyscallDefinition(SyscallFunction::WriteAt, "write_at", &[Fd, U64, Bytes]),
|
||||
SyscallDefinition(
|
||||
SyscallFunction::GetFileOption,
|
||||
"get_file_option",
|
||||
&[Fd, U32, BytesMut],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::SetFileOption,
|
||||
"set_file_option",
|
||||
&[Fd, U32, Bytes],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::OpenDirectory,
|
||||
"open_directory",
|
||||
&[OptionFd, Filename],
|
||||
),
|
||||
SyscallDefinition(
|
||||
SyscallFunction::ReadDirectoryEntries,
|
||||
"read_directory_entries",
|
||||
&[Fd, Ptr, Size],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::CreateDirectory,
|
||||
"create_directory",
|
||||
&[OptionFd, Filename, U32],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::ReadLink,
|
||||
"read_link",
|
||||
&[OptionFd, Filename, BytesMut],
|
||||
),
|
||||
SyscallDefinition(
|
||||
SyscallFunction::Remove,
|
||||
"remove",
|
||||
&[OptionFd, Filename, U32],
|
||||
), // TODO
|
||||
SyscallDefinition(SyscallFunction::Rename, "rename", &[Ptr]), // TODO
|
||||
SyscallDefinition(SyscallFunction::CloneFd, "clone_fd", &[Fd, OptionFd]),
|
||||
SyscallDefinition(
|
||||
SyscallFunction::UpdateMetadata,
|
||||
"update_metadata",
|
||||
&[OptionFd, Filename, Ptr],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::GetMetadata,
|
||||
"get_metadata",
|
||||
&[OptionFd, Filename, Ptr, U32],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::DeviceRequest,
|
||||
"device_request",
|
||||
&[Fd, U32, BytesMut, Size],
|
||||
), // TODO
|
||||
SyscallDefinition(SyscallFunction::CreateTimer, "create_timer", &[U32]), // TODO
|
||||
SyscallDefinition(SyscallFunction::CreatePid, "create_pid", &[Ptr, U32]), // TODO
|
||||
SyscallDefinition(SyscallFunction::CreatePty, "create_pty", &[Ptr, Ptr, Ptr]), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::CreateSharedMemory,
|
||||
"create_shared_memory",
|
||||
&[Size],
|
||||
),
|
||||
SyscallDefinition(
|
||||
SyscallFunction::CreatePollChannel,
|
||||
"create_poll_channel",
|
||||
&[],
|
||||
),
|
||||
SyscallDefinition(SyscallFunction::CreatePipe, "create_pipe", &[Ptr]), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::PollChannelWait,
|
||||
"poll_channel_wait",
|
||||
&[Fd, Ptr, U32, Ptr],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::PollChannelControl,
|
||||
"poll_channel_control",
|
||||
&[Fd, U32, Fd],
|
||||
), // TODO
|
||||
// Network
|
||||
SyscallDefinition(SyscallFunction::CreateSocket, "create_socket", &[U32]), // TODO
|
||||
SyscallDefinition(SyscallFunction::Bind, "bind", &[Fd, Bytes]), // TODO
|
||||
SyscallDefinition(SyscallFunction::Listen, "listen", &[Fd]),
|
||||
SyscallDefinition(SyscallFunction::Connect, "connect", &[Fd, Bytes]), // TODO
|
||||
SyscallDefinition(SyscallFunction::Accept, "accept", &[Fd, MaybePtr]), // TODO
|
||||
SyscallDefinition(SyscallFunction::Shutdown, "shutdown", &[Fd, U32]), // TODO
|
||||
SyscallDefinition(SyscallFunction::SendTo, "send_to", &[Fd, Bytes, MaybePtr]), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::ReceiveFrom,
|
||||
"receive_from",
|
||||
&[Fd, BytesMut, MaybePtr],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::GetSocketOption,
|
||||
"get_socket_option",
|
||||
&[Fd, U32, BytesMut],
|
||||
), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::SetSocketOption,
|
||||
"set_socket_option",
|
||||
&[Fd, U32, Bytes],
|
||||
), // TODO
|
||||
SyscallDefinition(SyscallFunction::SendMessage, "send_message", &[Fd, Ptr]), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::ReceiveMessage,
|
||||
"receive_message",
|
||||
&[Fd, Ptr],
|
||||
), // TODO
|
||||
// C compat
|
||||
SyscallDefinition(SyscallFunction::Fork, "fork", &[]),
|
||||
SyscallDefinition(SyscallFunction::Execve, "execve", &[Ptr]), // TODO
|
||||
// Debugging
|
||||
SyscallDefinition(SyscallFunction::DebugTrace, "debug_trace", &[U32, Filename]), // TODO
|
||||
SyscallDefinition(
|
||||
SyscallFunction::DebugControl,
|
||||
"debug_control",
|
||||
&[U32, U32, BytesMut, Size],
|
||||
), // TODO
|
||||
]
|
||||
};
|
||||
|
||||
impl SyscallDefinition {
|
||||
fn parse(&self, args: &[u64]) -> Syscall {
|
||||
let mut res = vec![];
|
||||
let mut pos = 0;
|
||||
for arg_def in self.2.iter() {
|
||||
use SyscallArgument::*;
|
||||
|
||||
let (a, l) = match arg_def {
|
||||
ArgType::MaybePtr => (MaybePtr(args[pos]), 1),
|
||||
ArgType::Bytes => (Bytes(args[pos], args[pos + 1]), 2),
|
||||
ArgType::BytesMut => (BytesMut(args[pos], args[pos + 1]), 2),
|
||||
ArgType::Fd => (Fd(args[pos]), 1),
|
||||
ArgType::OptionFd => (OptionFd(args[pos]), 1),
|
||||
ArgType::Ptr => (Ptr(args[pos]), 1),
|
||||
ArgType::Size => (Size(args[pos]), 1),
|
||||
ArgType::U64 => (U64(args[pos]), 1),
|
||||
ArgType::I64 => (I64(args[pos] as _), 1),
|
||||
ArgType::U32 => (U32(args[pos] as _), 1),
|
||||
ArgType::I32 => (I32(args[pos] as _), 1),
|
||||
ArgType::PtrSystemTime => (PtrSystemTime(args[pos]), 1),
|
||||
ArgType::ClockType => (ClockType(args[pos]), 1),
|
||||
ArgType::Filename => (Filename(args[pos], args[pos + 1]), 2),
|
||||
};
|
||||
|
||||
pos += l;
|
||||
res.push(a);
|
||||
}
|
||||
|
||||
Syscall(self.1, res)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_syscall(nr: u64, args: &[u64]) -> Option<Syscall> {
|
||||
let func = SyscallFunction::try_from(nr as usize).ok()?;
|
||||
let def = DEFS.iter().find(|e| e.0 == func)?;
|
||||
Some(def.parse(args))
|
||||
}
|
||||
@@ -1,68 +1,36 @@
|
||||
#![feature(rustc_private)]
|
||||
#![feature(rustc_private, yggdrasil_os, let_chains)]
|
||||
|
||||
use std::{
|
||||
io,
|
||||
process::{Command, ExitCode},
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
|
||||
use clap::Parser;
|
||||
use cross::{
|
||||
process::{ChildTrace, CommandSpawnExt, TraceEvent},
|
||||
signal,
|
||||
};
|
||||
use tracer::CommandTracer;
|
||||
|
||||
use crate::definition::parse_syscall;
|
||||
|
||||
pub mod definition;
|
||||
pub mod format;
|
||||
pub mod tracer;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
struct Args {
|
||||
program: String,
|
||||
command: String,
|
||||
args: Vec<String>,
|
||||
}
|
||||
|
||||
fn run(args: &Args) -> io::Result<()> {
|
||||
let mut command = Command::new(&args.command);
|
||||
command.args(&args.args);
|
||||
let tracer = CommandTracer::spawn(command)?;
|
||||
tracer.run(format::format_syscall)
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let args = Args::parse();
|
||||
|
||||
static ABORT: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
signal::set_sigint_handler(|| {
|
||||
ABORT.store(true, Ordering::Release);
|
||||
eprintln!("SIGINT");
|
||||
});
|
||||
let mut child = Command::new(args.program)
|
||||
.args(args.args)
|
||||
.spawn_with_trace()
|
||||
.unwrap();
|
||||
|
||||
let mut last_syscall: Option<(u64, [u64; 6])> = None;
|
||||
|
||||
while !ABORT.load(Ordering::Relaxed) {
|
||||
let event = child.next_event().unwrap();
|
||||
match event {
|
||||
TraceEvent::Exited => {
|
||||
eprintln!("Exit");
|
||||
break;
|
||||
}
|
||||
TraceEvent::SyscallExit(ret) => match last_syscall {
|
||||
Some((nr, args)) => {
|
||||
let call = parse_syscall(nr, &args);
|
||||
if let Some(call) = call {
|
||||
let mut s = String::new();
|
||||
call.print(&mut s, &mut child);
|
||||
eprintln!("{s} -> {ret}");
|
||||
} else {
|
||||
eprintln!("syscall {nr} {args:?} -> {ret}");
|
||||
}
|
||||
}
|
||||
None => {
|
||||
eprintln!("??? -> {ret}");
|
||||
}
|
||||
},
|
||||
TraceEvent::SyscallEntry(nr, args) => {
|
||||
last_syscall = Some((nr, args));
|
||||
}
|
||||
match run(&args) {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(error) => {
|
||||
eprintln!("{}: {error}", args.command);
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
child.resume().ok();
|
||||
}
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
|
||||
@@ -29,35 +29,38 @@ const PROGRAMS: &[(&str, &str)] = &[
|
||||
// shell
|
||||
("shell", "bin/sh"),
|
||||
// sysutils
|
||||
("echo", "bin/echo"),
|
||||
("mount", "sbin/mount"),
|
||||
("chroot", "sbin/chroot"),
|
||||
("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