Compare commits

...

6 Commits

64 changed files with 3105 additions and 227 deletions
Generated
+68
View File
@@ -2638,6 +2638,70 @@ dependencies = [
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_bsp_arm"
version = "0.1.0"
dependencies = [
"bytemuck",
"device-api",
"device-tree",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_bsp_bcm283x"
version = "0.1.0"
dependencies = [
"bytemuck",
"device-api",
"device-tree",
"futures-util",
"kernel-arch-aarch64",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_bsp_jh7110"
version = "0.1.0"
dependencies = [
"bytemuck",
"device-api",
"device-tree",
"futures-util",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_bsp_riscv"
version = "0.1.0"
dependencies = [
"bytemuck",
"device-api",
"device-tree",
"kernel-arch-riscv64",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_fat32"
version = "0.1.0"
@@ -2951,6 +3015,10 @@ dependencies = [
"yboot-proto",
"ygg_driver_acpi",
"ygg_driver_ahci",
"ygg_driver_bsp_arm",
"ygg_driver_bsp_bcm283x",
"ygg_driver_bsp_jh7110",
"ygg_driver_bsp_riscv",
"ygg_driver_fat32",
"ygg_driver_input",
"ygg_driver_net_core",
+53
View File
@@ -43,3 +43,56 @@ $ booti ${loadaddr} ${initrd_addr_r}:<SIZE-PRINTED-WHEN-LOADING-INITRD> ${fdt_ad
###### (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}
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}
Missing drivers:
No driver for Some("hvs@7e400000") ("brcm,bcm2711-hvs")
No driver for Some("i2c@7e804000") ("brcm,bcm2711-i2c")
also "brcm,bcm2835-i2c"
No driver for Some("usb@7e980000") ("brcm,bcm2835-usb")
No driver for Some("local_intc@40000000") ("brcm,bcm2836-l1-intc")
: avs-monitor@7d5d2000: probed
No driver for Some("thermal") ("brcm,bcm2711-thermal")
No driver for Some("dma@7e007000") ("brcm,bcm2835-dma")
No driver for Some("watchdog@7e100000") ("brcm,bcm2835-pm")
also "brcm,bcm2835-pm-wdt"
No driver for Some("rng@7e104000") ("brcm,bcm2711-rng200")
No driver for Some("pixelvalve@7e206000") ("brcm,bcm2711-pixelvalve0")
No driver for Some("pixelvalve@7e207000") ("brcm,bcm2711-pixelvalve1")
No driver for Some("pixelvalve@7e20a000") ("brcm,bcm2711-pixelvalve2")
No driver for Some("pwm@7e20c800") ("brcm,bcm2835-pwm")
No driver for Some("pixelvalve@7e216000") ("brcm,bcm2711-pixelvalve4")
No driver for Some("clock@7ef00000") ("brcm,brcm2711-dvp")
No driver for Some("interrupt-controller@7ef00100") ("brcm,bcm2711-l2-intc")
also "brcm,l2-intc"
No driver for Some("hdmi@7ef00700") ("brcm,bcm2711-hdmi0")
No driver for Some("i2c@7ef04500") ("brcm,bcm2711-hdmi-i2c")
No driver for Some("hdmi@7ef05700") ("brcm,bcm2711-hdmi1")
No driver for Some("i2c@7ef09500") ("brcm,bcm2711-hdmi-i2c")
No driver for Some("firmware") ("raspberrypi,bcm2835-firmware")
also "simple-mfd"
No driver for Some("clocks") ("raspberrypi,firmware-clocks")
No driver for Some("gpio") ("raspberrypi,firmware-gpio")
No driver for Some("reset") ("raspberrypi,firmware-reset")
No driver for Some("power") ("raspberrypi,bcm2835-power")
No driver for Some("mailbox@7e00b840") ("brcm,bcm2835-vchiq")
No driver for Some("phy") ("usb-nop-xceiv")
No driver for Some("gpu") ("brcm,bcm2711-vc5")
No driver for Some("mmc@7e340000") ("brcm,bcm2711-emmc2")
No driver for Some("arm-pmu") ("arm,cortex-a72-pmu")
also "arm,armv8-pmuv3"
No driver for Some("cpu@0") ("arm,cortex-a72")
No driver for Some("cpu@1") ("arm,cortex-a72")
No driver for Some("cpu@2") ("arm,cortex-a72")
No driver for Some("cpu@3") ("arm,cortex-a72")
No driver for Some("pcie@7d500000") ("brcm,bcm2711-pcie")
No driver for Some("ethernet@7d580000") ("brcm,bcm2711-genet-v5")
No driver for Some("mdio@e14") ("brcm,genet-mdio-v5")
No driver for Some("leds") ("gpio-leds")
No driver for Some("wifi-pwrseq") ("mmc-pwrseq-simple")
No driver for Some("sd_io_1v8_reg") ("regulator-gpio")
No driver for Some("sd_vcc_reg") ("regulator-fixed")
+91
View File
@@ -37,3 +37,94 @@ env set ipaddr 13.0.0.2; env set initrd_addr_r 0x70000000; tftpboot ${initrd_add
dhcp
dhcp; env set initrd_addr_r 0x70000000; tftpboot ${initrd_addr_r} 192.168.88.10:initrd.img; tftpboot ${loadaddr} 192.168.88.10:yggdrasil-kernel.bin; load mmc 1:3 ${fdt_addr_r} dtbs/6.6.20-starfive/starfive/${fdtfile}; fdt resize; booti ${loadaddr} ${initrd_addr_r}:60000000 ${fdt_addr_r}
Missing drivers:
Clock/reset/pin:
No driver for Some("pinctrl@17020000") ("starfive,jh7110-aon-pinctrl")
No driver for Some("clock-controller@19810000") ("starfive,jh7110-ispcrg")
No driver for Some("clock-controller@295c0000") ("starfive,jh7110-voutcrg")
Power/reg/GPIO:
No driver for Some("opp-table-0") ("operating-points-v2")
No driver for Some("pmic@36") ("x-powers,axp15060")
No driver for Some("power-controller@17030000") ("starfive,jh7110-pmu")
No driver for Some("leds") ("gpio-leds")
No driver for Some("gpio-restart") ("gpio-restart")
Serial:
No driver for Some("i2c@10030000") ("snps,designware-i2c")
No driver for Some("i2c@10050000") ("snps,designware-i2c")
No driver for Some("i2c@12050000") ("snps,designware-i2c")
No driver for Some("i2c@12060000") ("snps,designware-i2c")
No driver for Some("spi@10060000") ("arm,pl022")
also "arm,primecell"
Bus:
No driver for Some("usb@10100000") ("starfive,jh7110-usb")
No driver for Some("usb@0") ("cdns,usb3")
No driver for Some("phy@10200000") ("starfive,jh7110-usb-phy")
No driver for Some("phy@10210000") ("starfive,jh7110-pcie-phy")
No driver for Some("phy@10220000") ("starfive,jh7110-pcie-phy")
No driver for Some("pcie@940000000") ("starfive,jh7110-pcie")
No driver for Some("pcie@9c0000000") ("starfive,jh7110-pcie")
Interrupt:
No driver for Some("interrupt-controller") ("riscv,cpu-intc")
No driver for Some("timer@2000000") ("starfive,jh7110-clint")
also "sifive,clint0"
Display/GPU subsystem:
No driver for Some("display-subsystem") ("starfive,jh7110-display")
also "verisilicon,display-subsystem"
No driver for Some("dsi-output") ("starfive,jh7110-display-encoder")
also "verisilicon,dsi-encoder"
No driver for Some("jpu@13090000") ("starfive,jpu")
No driver for Some("vpu_dec@130a0000") ("starfive,vdec")
No driver for Some("vpu_enc@130b0000") ("starfive,venc")
No driver for Some("gpu@18000000") ("img-gpu")
No driver for Some("vin_sysctl@19800000") ("starfive,jh7110-vin")
No driver for Some("phy@19820000") ("starfive,jh7110-dphy-rx")
No driver for Some("dc8200@29400000") ("starfive,jh7110-dc8200")
also "verisilicon,dc8200"
No driver for Some("hdmi@29590000") ("starfive,jh7110-hdmi")
also "inno,hdmi"
No driver for Some("mipi@295d0000") ("starfive,jh7110-mipi_dsi")
also "cdns,dsi"
No driver for Some("mipi-dphy@295e0000") ("starfive,jh7110-mipi-dphy-tx")
also "m31,mipi-dphy-tx"
Misc:
No driver for Some("mailbox_client") ("starfive,mailbox-test")
No driver for Some("cache-controller@2010000") ("starfive,jh7110-ccache")
also "sifive,ccache0"
also "cache"
No driver for Some("pwm@120d0000") ("starfive,jh7110-pwm")
also "opencores,pwm-v1"
No driver for Some("temperature-sensor@120e0000") ("starfive,jh7110-temp")
No driver for Some("timer@13050000") ("starfive,jh7110-timer")
No driver for Some("mailbox@13060000") ("starfive,mail_box")
No driver for Some("watchdog@13070000") ("starfive,jh7110-wdt")
No driver for Some("crypto@16000000") ("starfive,jh7110-crypto")
No driver for Some("rng@1600c000") ("starfive,jh7110-trng")
No driver for Some("mdio") ("snps,dwmac-mdio")
No driver for Some("mdio") ("snps,dwmac-mdio")
No driver for Some("dma-controller@16050000") ("starfive,jh7110-axi-dma")
No driver for Some("dma-controller@16008000") ("arm,pl080")
also "arm,primecell"
No driver for Some("rtc@17040000") ("starfive,jh7110-rtc")
No driver for Some("e24@6e210000") ("starfive,e24")
No driver for Some("linux,cma") ("shared-dma-pool")
Storage:
No driver for Some("spi@13010000") ("starfive,jh7110-qspi")
also "cdns,qspi-nor"
No driver for Some("flash@0") ("jedec,spi-nor")
No driver for Some("partitions") ("fixed-partitions")
No driver for Some("mmc@16010000") ("starfive,jh7110-mmc")
No driver for Some("mmc@16020000") ("starfive,jh7110-mmc")
Audio:
No driver for Some("pwmdac@100b0000") ("starfive,jh7110-pwmdac")
No driver for Some("i2s@120b0000") ("starfive,jh7110-i2stx0")
+9
View File
@@ -56,11 +56,16 @@ git-version = "0.3.9"
aarch64-cpu.workspace = true
device-tree.workspace = true
kernel-arch-aarch64.workspace = true
ygg_driver_bsp_arm.path = "driver/bsp/arm"
ygg_driver_bsp_bcm283x.path = "driver/bsp/bcm283x"
[target.'cfg(target_arch = "riscv64")'.dependencies]
device-tree.workspace = true
kernel-arch-riscv64.workspace = true
ygg_driver_bsp_arm.path = "driver/bsp/arm" # PrimeCell components
ygg_driver_bsp_riscv.path = "driver/bsp/riscv"
ygg_driver_net_stmmac.path = "driver/net/stmmac"
ygg_driver_bsp_jh7110.path = "driver/bsp/jh7110"
[target.'cfg(target_arch = "x86_64")'.dependencies]
yboot-proto.workspace = true
@@ -87,7 +92,11 @@ kernel-arch-aarch64.workspace = true
kernel-arch-riscv64.workspace = true
ygg_driver_acpi.path = "driver/acpi"
ygg_driver_bsp_arm.path = "driver/bsp/arm"
ygg_driver_bsp_riscv.path = "driver/bsp/riscv"
ygg_driver_net_stmmac.path = "driver/net/stmmac"
ygg_driver_bsp_bcm283x.path = "driver/bsp/bcm283x"
ygg_driver_bsp_jh7110.path = "driver/bsp/jh7110"
[features]
default = ["fb_console"]
+6
View File
@@ -44,6 +44,7 @@ pub struct PerCpuData {
}
pub static CPU_COUNT: AtomicUsize = AtomicUsize::new(1);
pub static mut BOOT_HART_ID: u64 = 0;
static IPI_QUEUES: OneTimeInit<Vec<IpiQueue<ArchitectureImpl>>> = OneTimeInit::new();
static HART_TO_QUEUE: IrqSafeSpinlock<ArchitectureImpl, BTreeMap<u32, usize>> =
IrqSafeSpinlock::new(BTreeMap::new());
@@ -60,6 +61,11 @@ impl CpuData for PerCpuData {
}
}
/// Returns the ID of the bootstrap HART
pub fn boot_hart_id() -> u64 {
unsafe { BOOT_HART_ID }
}
#[unsafe(naked)]
extern "C" fn idle_task(_: usize) -> ! {
core::arch::naked_asm!("1: nop; j 1b");
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "ygg_driver_bsp_arm"
version = "0.1.0"
edition = "2024"
[dependencies]
device-tree.workspace = true
device-api.workspace = true
yggdrasil-abi.workspace = true
libk-mm.workspace = true
libk-util.workspace = true
libk.workspace = true
tock-registers.workspace = true
log.workspace = true
bytemuck.workspace = true
+7
View File
@@ -0,0 +1,7 @@
#![allow(unsafe_op_in_unsafe_fn)]
#![no_std]
extern crate alloc;
mod pl011;
mod pl061;
@@ -1,5 +1,3 @@
//! ARM PL011 driver
use abi::{error::Error, io::TerminalOptions};
use alloc::sync::Arc;
use device_api::{
device::{Device, DeviceInitContext},
@@ -18,6 +16,7 @@ use tock_registers::{
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use yggdrasil_abi::{error::Error, io::TerminalOptions};
register_bitfields! {
u32,
+233
View File
@@ -0,0 +1,233 @@
use core::sync::atomic::{AtomicU64, Ordering};
use alloc::{sync::Arc, vec::Vec};
use device_api::{
clock::{ClockHandle, ResetHandle},
device::{Device, DeviceInitContext},
gpio::{
GpioController, GpioInterruptEvent, GpioInterruptMode, GpioPinLevel, PinHandle,
SinglePinDirection,
},
interrupt::{FullIrq, InterruptHandler, IrqVector},
};
use device_tree::{
driver::{
device_tree_driver, util::generic_gpio_config, DeviceTreeGpioPins, DeviceTreePinController,
Node, ProbeContext,
},
DeviceTreePropertyRead, TProp,
};
use libk::{device::external_interrupt_controller, event::signal_gpio_event};
use libk_mm::device::DeviceMemoryIo;
use libk_util::{bit::BitField, sync::IrqSafeSpinlock};
use tock_registers::{
interfaces::{Readable, Writeable},
register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use yggdrasil_abi::error::Error;
register_structs! {
#[allow(non_snake_case)]
Regs {
(0x0000 => GPIODATA: [ReadWrite<u32>; 256]),
(0x0400 => GPIODIR: ReadWrite<u32>),
(0x0404 => GPIOIS: ReadWrite<u32>),
(0x0408 => GPIOIBE: ReadWrite<u32>),
(0x040C => GPIOIEV: ReadWrite<u32>),
(0x0410 => GPIOIE: ReadWrite<u32>),
(0x0414 => GPIORIS: ReadOnly<u32>),
(0x0418 => GPIOMIS: ReadOnly<u32>),
(0x041C => GPIOIC: WriteOnly<u32>),
(0x0420 => GPIOAFSEL: ReadWrite<u32>),
(0x0424 => _0),
(0x0FE0 => GPIOPERIPHID: [ReadOnly<u32>; 4]),
(0x0FF0 => GPIOPCELLID: [ReadOnly<u32>; 4]),
(0x1000 => @END),
}
}
struct Pl061 {
#[allow(unused)]
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
irq: FullIrq,
clocks: Vec<ClockHandle>,
resets: Vec<ResetHandle>,
gpio_events: [AtomicU64; 8],
}
impl Device for Pl061 {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
for clock in self.clocks.iter() {
clock.enable()?;
}
for reset in self.resets.iter() {
reset.deassert()?;
}
Ok(())
}
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)?;
Ok(())
}
fn display_name(&self) -> &str {
"PL061 GPIO Controller"
}
}
impl InterruptHandler for Pl061 {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
let status = {
let lock = self.regs.lock();
let val = lock.GPIOMIS.get();
lock.GPIOIC.set(0xFF);
val
};
for bit in 0..8 {
let ev = self.gpio_events[bit].load(Ordering::Acquire);
if ev != 0 {
signal_gpio_event(ev);
}
}
status != 0
}
}
impl GpioController for Pl061 {
fn write_gpio(&self, _pin: &PinHandle, _value: bool) -> Result<(), Error> {
todo!()
}
fn read_gpio(&self, _pin: &PinHandle) -> Result<bool, Error> {
todo!()
}
fn setup_gpio(&self, pin: &PinHandle, event: Option<GpioInterruptEvent>) -> Result<(), Error> {
let regs = self.regs.lock();
let direction = pin
.config
.force_single_direction()
.ok_or(Error::InvalidArgument)
.inspect_err(|_| {
log::warn!(
"pl061: gpio #{} has invalid direction input={}, output={}",
pin.index,
pin.config.input,
pin.config.output
)
})?;
// Enable software control
regs.GPIOAFSEL.set(regs.GPIOAFSEL.get() & !(1 << pin.index));
match direction {
SinglePinDirection::Output => {
log::info!("pl061: gpio #{} set as output", pin.index);
regs.GPIODIR.set(regs.GPIODIR.get() | (1 << pin.index));
// Disable interrupt
regs.GPIOIE.set(regs.GPIOIE.get() & !(1 << pin.index));
let level = match pin.config.initial_level {
GpioPinLevel::Low => 0,
GpioPinLevel::High => 1,
};
let mut val = regs.GPIODATA[0].get();
val &= !(1 << pin.index);
val |= level << pin.index;
regs.GPIODATA[0].set(val);
}
SinglePinDirection::Input => {
log::info!("pl061: gpio #{} set as input", pin.index);
regs.GPIODIR.set(regs.GPIODIR.get() & !(1 << pin.index));
if let Some(event) = event {
let ibe = event.mode == GpioInterruptMode::BothEdges;
let is = event.mode == GpioInterruptMode::HighLevel
|| event.mode == GpioInterruptMode::LowLevel;
let iev = event.mode == GpioInterruptMode::HighLevel
|| event.mode == GpioInterruptMode::RisingEdge;
self.gpio_events[pin.index as usize].store(event.event, Ordering::Release);
regs.GPIOIS
.set(regs.GPIOIS.get().modify_bit(pin.index as usize, is));
regs.GPIOIBE
.set(regs.GPIOIBE.get().modify_bit(pin.index as usize, ibe));
regs.GPIOIEV
.set(regs.GPIOIEV.get().modify_bit(pin.index as usize, iev));
regs.GPIOIE.set(regs.GPIOIE.get() | (1 << pin.index));
regs.GPIOIC.set(1 << pin.index);
}
}
}
Ok(())
}
}
impl DeviceTreePinController for Pl061 {
fn configure_pin_group(self: Arc<Self>, _pins: &Arc<Node>) -> Result<(), Error> {
// TODO implement this when I get some board with this pinctrl
todo!()
}
fn map_gpio(
self: Arc<Self>,
property: &TProp,
offset: usize,
info: &DeviceTreeGpioPins,
) -> Option<(PinHandle, usize)> {
let (pin, options) = property.read_cells_at(offset, (1, 1))?;
let config = generic_gpio_config(options as u32, info);
Some((
PinHandle {
index: pin as u32,
config,
parent: self,
},
2,
))
}
}
device_tree_driver! {
compatible: ["arm,pl061"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let clocks = if let Some(clocks) = node.clocks() {
clocks.collect()
} else {
Vec::new()
};
let resets = if let Some(resets) = node.resets() {
resets.collect()
} else {
Vec::new()
};
let irq = node.interrupt(0)?;
let base = node.map_base(context, 0)?;
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let pinctrl = Arc::new(Pl061 {
regs: IrqSafeSpinlock::new(regs),
irq,
clocks,
resets,
gpio_events: [const { AtomicU64::new(0) }; 8]
});
node.make_pin_controller(pinctrl.clone());
Some(pinctrl)
}
}
}
+18
View File
@@ -0,0 +1,18 @@
[package]
name = "ygg_driver_bsp_bcm283x"
version = "0.1.0"
edition = "2024"
[dependencies]
device-tree.workspace = true
device-api.workspace = true
yggdrasil-abi.workspace = true
libk-mm.workspace = true
libk-util.workspace = true
libk.workspace = true
kernel-arch-aarch64.workspace = true
tock-registers.workspace = true
log.workspace = true
bytemuck.workspace = true
futures-util.workspace = true
@@ -1,6 +1,3 @@
//! BCM283x AUX peripheral
use aarch64_cpu::registers::ReadWriteable;
use abi::error::Error;
use alloc::sync::Arc;
use device_api::{
clock::{ClockController, ClockHandle},
@@ -13,9 +10,11 @@ use device_tree::{
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use tock_registers::{
interfaces::ReadWriteable,
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite},
};
use yggdrasil_abi::error::Error;
register_bitfields! {
u32,
@@ -1,9 +1,3 @@
//! Broadcom BCM2835 mini-UART driver
use abi::{
error::Error,
io::{TerminalOptions, TerminalOutputOptions},
};
use alloc::sync::Arc;
use device_api::{
clock::ClockHandle,
@@ -23,6 +17,10 @@ use tock_registers::{
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite},
};
use yggdrasil_abi::{
error::Error,
io::{TerminalOptions, TerminalOutputOptions},
};
register_bitfields! {
u32,
+252
View File
@@ -0,0 +1,252 @@
use alloc::sync::Arc;
use device_api::{
device::{Device, DeviceInitContext},
gpio::{GpioController, GpioInterruptEvent, PinHandle},
interrupt::{FullIrq, InterruptHandler, IrqVector},
};
use device_tree::{
driver::{
device_tree_driver, util::generic_gpio_config, DeviceTreeGpioPins, DeviceTreePinController,
Node, ProbeContext,
},
DeviceTreePropertyRead, TProp,
};
use libk::device::external_interrupt_controller;
use libk_mm::device::DeviceMemoryIo;
use libk_util::sync::IrqSafeSpinlock;
use tock_registers::{
interfaces::{Readable, Writeable},
register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use yggdrasil_abi::error::Error;
const PUPD_NONE: u32 = 0b00;
register_structs! {
#[allow(non_snake_case)]
Regs {
// Pin function select
(0x00 => GPFSEL: [ReadWrite<u32>; 6]),
(0x18 => _0),
// Set pin
(0x1C => GPSET: [WriteOnly<u32>; 2]),
(0x24 => _1),
// Clear pin
(0x28 => GPCLR: [WriteOnly<u32>; 2]),
(0x30 => _2),
// Current pin level
(0x34 => GPLEV: [ReadOnly<u32>; 2]),
(0x3C => _3),
// Pin event detect status
(0x40 => GPEDS: [ReadWrite<u32>; 2]),
(0x48 => _4),
// Pin rising edge event enable
(0x4C => GPREN: [ReadWrite<u32>; 2]),
(0x54 => _5),
// Pin falling edge event enable
(0x58 => GPFEN: [ReadWrite<u32>; 2]),
(0x60 => _6),
// Pin high event enable
(0x64 => GPHEN: [ReadWrite<u32>; 2]),
(0x6C => _7),
// Pin low event enable
(0x70 => GPLEN: [ReadWrite<u32>; 2]),
(0x78 => _8),
// Pin async rising edge event enable
(0x7C => GPAREN: [ReadWrite<u32>; 2]),
(0x84 => _9),
// Pin async falling edge event enable
(0x88 => GPAFEN: [ReadWrite<u32>; 2]),
(0x90 => _10),
// Pin pull up/down control
(0xE4 => GPIO_PUP_PDN_CNTRL_REG: [ReadWrite<u32>; 4]),
(0xF4 => _11),
(0x100 => @END),
}
}
struct Bcm2711Gpio {
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
#[allow(unused)]
irqs: [FullIrq; 2],
}
impl Regs {
fn write_fsel(&self, pin: usize, value: u32) {
let fsel_reg = pin / 10;
let fsel_shift = (pin % 10) * 3;
let mut fsel = self.GPFSEL[fsel_reg].get();
fsel &= !(0x7 << fsel_shift);
fsel |= (value & 0x7) << fsel_shift;
self.GPFSEL[fsel_reg].set(fsel);
}
fn write_pupd(&self, pin: usize, value: u32) {
let pupd_reg = pin / 16;
let pupd_shift = (pin % 16) * 2;
let mut pupd = self.GPIO_PUP_PDN_CNTRL_REG[pupd_reg].get();
pupd &= !(0x3 << pupd_shift);
pupd |= (value & 0x3) << pupd_shift;
self.GPIO_PUP_PDN_CNTRL_REG[pupd_reg].set(pupd);
}
fn configure_pin_interrupts(
&self,
pin: usize,
rising_edge: bool,
falling_edge: bool,
level_high: bool,
level_low: bool,
) {
#[inline]
fn modify_reg(reg: &ReadWrite<u32>, bit: u32, set: bool) {
if set {
reg.set(reg.get() | bit);
} else {
reg.set(reg.get() & !bit);
}
}
// TODO use async edge detection (likely have some limitations)?
let reg = pin / 32;
let bit = 1 << (pin % 32);
// Disable async edge events
modify_reg(&self.GPAREN[reg], bit, false);
modify_reg(&self.GPAFEN[reg], bit, false);
modify_reg(&self.GPREN[reg], bit, rising_edge);
modify_reg(&self.GPFEN[reg], bit, falling_edge);
modify_reg(&self.GPHEN[reg], bit, level_high);
modify_reg(&self.GPLEN[reg], bit, level_low);
// Clear interrupt status
self.GPEDS[reg].set(bit);
}
fn configure_pin_function(&self, pin: usize, function: u32, pull: u32) {
self.write_fsel(pin, function);
self.write_pupd(pin, pull);
}
}
impl Device for Bcm2711Gpio {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
let regs = self.regs.lock();
// Disable all interrupts by default
for pin in 0..58 {
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)?;
}
Ok(())
}
fn display_name(&self) -> &str {
"bcm2711-gpio"
}
}
impl InterruptHandler for Bcm2711Gpio {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
log::warn!("TODO: handle bcm2711-gpio interrupts");
false
}
}
impl GpioController for Bcm2711Gpio {
fn setup_gpio(&self, pin: &PinHandle, _event: Option<GpioInterruptEvent>) -> Result<(), Error> {
log::warn!(
"TOOD: bcm2711 gpio pin #{} setup: input={}, output={}",
pin.index,
pin.config.input,
pin.config.output
);
Ok(())
}
fn read_gpio(&self, _pin: &PinHandle) -> Result<bool, Error> {
Ok(false)
}
fn write_gpio(&self, _pin: &PinHandle, _value: bool) -> Result<(), Error> {
Ok(())
}
}
impl DeviceTreePinController for Bcm2711Gpio {
fn configure_pin_group(self: Arc<Self>, group: &Arc<Node>) -> Result<(), Error> {
let pins = group.property("brcm,pins").ok_or(Error::InvalidArgument)?;
let function = group.property("brcm,function").ok_or(Error::DoesNotExist)?;
let pull = group.property("brcm,pull");
if function.is_empty() || pins.is_empty() {
return Ok(());
}
let function = function.read_cell(0, 1).ok_or(Error::InvalidArgument)? as u32;
let regs = self.regs.lock();
for i in 0..pins.len() / 4 {
let pin = pins.read_cell(i, 1).ok_or(Error::InvalidArgument)? as u32;
let pull = if let Some(pull) = pull.as_ref().and_then(|p| p.read_cell(i, 1)) {
pull as u32
} else {
PUPD_NONE
};
log::info!("bcm2711-gpio: gpio{pin} function={function}");
regs.configure_pin_function(pin as usize, function, pull);
}
Ok(())
}
fn map_gpio(
self: Arc<Self>,
property: &TProp,
offset: usize,
info: &DeviceTreeGpioPins,
) -> Option<(PinHandle, usize)> {
let (pin, options) = property.read_cells_at(offset, (1, 1))?;
let config = generic_gpio_config(options as u32, info);
Some((
PinHandle {
index: pin as u32,
config,
parent: self,
},
2,
))
}
}
device_tree_driver! {
compatible: ["brcm,bcm2711-gpio"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let irq0 = node.interrupt(0)?;
let irq1 = node.interrupt(1)?;
let base = node.map_base(context, 0)?;
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let gpio = Arc::new(Bcm2711Gpio {
regs: IrqSafeSpinlock::new(regs),
irqs: [irq0, irq1]
});
node.make_pin_controller(gpio.clone());
Some(gpio)
}
}
}
+8
View File
@@ -0,0 +1,8 @@
#![no_std]
extern crate alloc;
mod aux;
mod aux_uart;
mod gpio;
mod mbox;
@@ -1,8 +1,5 @@
#![allow(missing_docs)]
use core::{cell::UnsafeCell, mem::MaybeUninit};
use abi::error::Error;
use alloc::sync::Arc;
use device_api::device::{Device, DeviceInitContext};
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
@@ -25,6 +22,7 @@ use tock_registers::{
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use yggdrasil_abi::error::Error;
register_bitfields! {
u32,
+17
View File
@@ -0,0 +1,17 @@
[package]
name = "ygg_driver_bsp_jh7110"
version = "0.1.0"
edition = "2024"
[dependencies]
device-tree.workspace = true
device-api.workspace = true
yggdrasil-abi.workspace = true
libk-mm.workspace = true
libk-util.workspace = true
libk.workspace = true
tock-registers.workspace = true
log.workspace = true
bytemuck.workspace = true
futures-util.workspace = true
@@ -1,8 +1,5 @@
//! StarFive JH7110 clock drivers
use core::{marker::PhantomData, ops::Index};
use abi::error::Error;
use alloc::sync::Arc;
use device_api::{
clock::{ClockController, ClockHandle, Hertz, ResetController, ResetHandle},
@@ -17,6 +14,7 @@ use device_tree::{
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
use libk_util::sync::IrqSafeSpinlock;
use yggdrasil_abi::error::Error;
// SYSCRG clocks
+7
View File
@@ -0,0 +1,7 @@
#![allow(unsafe_op_in_unsafe_fn)]
#![no_std]
extern crate alloc;
mod clocks;
mod pinctrl;
+445
View File
@@ -0,0 +1,445 @@
use alloc::{sync::Arc, vec::Vec};
use device_api::{
clock::{ClockHandle, ResetHandle},
device::{Device, DeviceInitContext},
gpio::{
GpioController, GpioInterruptEvent, GpioPinLevel, OutputPinBias, PinHandle,
SinglePinDirection,
},
};
use device_tree::{
driver::{
device_tree_driver,
util::{generic_gpio_config, GenericPinctrlBiasConfig, GenericPinctrlConfig},
DeviceTreeGpioPins, DeviceTreePinController, Node, ProbeContext,
},
DeviceTreePropertyRead, TProp,
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
use libk_util::{bit::BitField, sync::IrqSafeSpinlock, OneTimeInit};
use yggdrasil_abi::error::Error;
#[derive(Debug)]
struct PinMuxConfig {
pin: u8,
function: u8,
doen: u8,
dout: u8,
din: u8,
}
struct SysRegs(DeviceMemoryIoMut<'static, [u32]>);
struct AonRegs(DeviceMemoryIoMut<'static, [u32]>);
unsafe impl Send for SysRegs {}
unsafe impl Sync for SysRegs {}
unsafe impl Send for AonRegs {}
unsafe impl Sync for AonRegs {}
struct Gpio<R: GpioRegs> {
regs: IrqSafeSpinlock<R>,
init: OneTimeInit<Result<(), Error>>,
clocks: Vec<ClockHandle>,
resets: Vec<ResetHandle>,
}
trait GpioRegs: Sized + Send + 'static {
const NAME: &'static str;
const DOUT_OFFSET: usize;
const DOEN_OFFSET: usize;
const GPI_OFFSET: usize;
const DOUT_MASK: u32;
const DOEN_MASK: u32;
const GPI_MASK: u32;
unsafe fn map(base: PhysicalAddress) -> Result<Self, Error>;
fn reg_mut(&mut self, index: usize) -> *mut u32;
fn pin_functions(&self, pin: usize) -> Option<(usize, usize, u32)>;
fn write_output_pin(&mut self, pin: usize, value: bool) {
let reg = pin / 4;
let shift = (pin % 4) * 8;
let reg_dout = self.reg_mut(Self::DOUT_OFFSET + reg);
unsafe {
let mut val = reg_dout.read_volatile();
val.set_bit(shift, value);
reg_dout.write_volatile(val);
}
}
fn configure_pin(&mut self, pin: usize, doen: u32, dout: u32, din: u32) {
let reg = pin / 4;
let shift = (pin % 4) * 8;
let reg_doen = self.reg_mut(Self::DOEN_OFFSET + reg);
let reg_dout = self.reg_mut(Self::DOUT_OFFSET + reg);
unsafe {
let mut val = reg_dout.read_volatile();
val &= !(Self::DOUT_MASK << shift);
val |= dout << shift;
reg_dout.write_volatile(val);
let mut val = reg_doen.read_volatile();
val &= !(Self::DOEN_MASK << shift);
val |= doen << shift;
reg_doen.write_volatile(val);
if din != 0xFF {
let din_reg = din as usize / 4;
let din_shift = (din % 4) * 8;
let reg_din = self.reg_mut(Self::GPI_OFFSET + din_reg);
let mut val = reg_din.read_volatile();
val &= !(Self::GPI_MASK << din_shift);
val |= (pin as u32 + 2) << din_shift;
reg_din.write_volatile(val);
}
}
}
fn configure_pin_function(&mut self, pin: usize, func: u32) {
let Some((offset, shift, max)) = self.pin_functions(pin) else {
return;
};
if func > max {
return;
}
log::info!("jh7110-sys-pinctrl: set pad{pin} function={func}");
let reg = self.reg_mut(offset / 4);
unsafe {
let mut val = reg.read_volatile();
val &= !(0x3 << shift);
val |= func << shift;
reg.write_volatile(val);
}
}
fn set_input_enabled(&mut self, _pin: usize, _enabled: bool) {}
fn set_output_enabled(&mut self, _pin: usize, _enabled: bool) {}
fn set_bias(&mut self, _pin: usize, _bias: GenericPinctrlBiasConfig) {}
}
// sys-pinctrl pads
const fn pad_gpio(n: usize) -> usize {
n
}
const PAD_GMAC1_RXC: usize = 82;
const SYS_PIN_COUNT: usize = 95;
const SYS_PIN_FUNCTIONS: [Option<(usize, usize, u32)>; SYS_PIN_COUNT] = const {
let mut t = [const { None }; SYS_PIN_COUNT];
t[PAD_GMAC1_RXC] = Some((0x29C, 0, 1));
t[pad_gpio(10)] = Some((0x29C, 2, 3));
t[pad_gpio(11)] = Some((0x29C, 5, 3));
t[pad_gpio(12)] = Some((0x29C, 8, 3));
t[pad_gpio(13)] = Some((0x29C, 11, 3));
t[pad_gpio(14)] = Some((0x29C, 14, 3));
t[pad_gpio(15)] = Some((0x29C, 17, 3));
t[pad_gpio(16)] = Some((0x29C, 20, 3));
t[pad_gpio(17)] = Some((0x29C, 23, 3));
t[pad_gpio(18)] = Some((0x29C, 26, 3));
t[pad_gpio(19)] = Some((0x29C, 29, 3));
t[pad_gpio(20)] = Some((0x2A0, 0, 3));
t[pad_gpio(21)] = Some((0x2A0, 3, 3));
t[pad_gpio(22)] = Some((0x2A0, 6, 3));
t[pad_gpio(23)] = Some((0x2A0, 9, 3));
t[pad_gpio(24)] = Some((0x2A0, 12, 3));
t[pad_gpio(25)] = Some((0x2A0, 15, 3));
t[pad_gpio(26)] = Some((0x2A0, 18, 3));
t[pad_gpio(27)] = Some((0x2A0, 21, 3));
t[pad_gpio(28)] = Some((0x2A0, 24, 3));
t[pad_gpio(29)] = Some((0x2A0, 27, 3));
t[pad_gpio(30)] = Some((0x2A4, 0, 3));
t[pad_gpio(31)] = Some((0x2A4, 3, 3));
t[pad_gpio(32)] = Some((0x2A4, 6, 3));
t[pad_gpio(33)] = Some((0x2A4, 9, 3));
t[pad_gpio(34)] = Some((0x2A4, 12, 3));
t[pad_gpio(35)] = Some((0x2A4, 15, 3));
t[pad_gpio(36)] = Some((0x2A4, 17, 3));
t[pad_gpio(37)] = Some((0x2A4, 20, 3));
t[pad_gpio(38)] = Some((0x2A4, 23, 3));
t[pad_gpio(39)] = Some((0x2A4, 26, 3));
t[pad_gpio(40)] = Some((0x2A4, 29, 3));
t[pad_gpio(41)] = Some((0x2A8, 0, 3));
t[pad_gpio(42)] = Some((0x2A8, 3, 3));
t[pad_gpio(43)] = Some((0x2A8, 6, 3));
t[pad_gpio(44)] = Some((0x2A8, 9, 3));
t[pad_gpio(45)] = Some((0x2A8, 12, 3));
t[pad_gpio(46)] = Some((0x2A8, 15, 3));
t[pad_gpio(47)] = Some((0x2A8, 18, 3));
t[pad_gpio(48)] = Some((0x2A8, 21, 3));
t[pad_gpio(49)] = Some((0x2A8, 24, 3));
t[pad_gpio(50)] = Some((0x2A8, 27, 3));
t[pad_gpio(51)] = Some((0x2A8, 30, 3));
t[pad_gpio(52)] = Some((0x2AC, 0, 3));
t[pad_gpio(53)] = Some((0x2AC, 2, 3));
t[pad_gpio(54)] = Some((0x2AC, 4, 3));
t[pad_gpio(55)] = Some((0x2AC, 6, 3));
t[pad_gpio(56)] = Some((0x2AC, 9, 3));
t[pad_gpio(57)] = Some((0x2AC, 12, 3));
t[pad_gpio(58)] = Some((0x2AC, 15, 3));
t[pad_gpio(59)] = Some((0x2AC, 18, 3));
t[pad_gpio(60)] = Some((0x2AC, 21, 3));
t[pad_gpio(61)] = Some((0x2AC, 24, 3));
t[pad_gpio(62)] = Some((0x2AC, 27, 3));
t[pad_gpio(63)] = Some((0x2AC, 30, 3));
t[pad_gpio(6)] = Some((0x2B0, 0, 3));
t[pad_gpio(7)] = Some((0x2B0, 2, 3));
t[pad_gpio(8)] = Some((0x2B0, 5, 3));
t[pad_gpio(9)] = Some((0x2B0, 8, 3));
t
};
impl PinMuxConfig {
fn from_fdt(fdt: u32) -> Self {
let pin = fdt as u8;
let function = ((fdt >> 8) & 0x3) as u8;
let doen = ((fdt >> 10) & 0x3F) as u8;
let dout = (fdt >> 16) as u8;
let din = (fdt >> 24) as u8;
Self {
pin,
function,
doen,
dout,
din,
}
}
}
impl<R: GpioRegs> Gpio<R> {
fn from_device_tree(node: &Arc<Node>, context: &mut ProbeContext) -> Result<Arc<Self>, Error> {
let base = node.map_base(context, 0).ok_or(Error::DoesNotExist)?;
let regs = unsafe { R::map(base) }?;
let clocks = node.clocks().map(|c| c.collect()).unwrap_or_default();
let resets = node.resets().map(|c| c.collect()).unwrap_or_default();
let gpio = Arc::new(Self {
regs: IrqSafeSpinlock::new(regs),
init: OneTimeInit::new(),
clocks,
resets,
});
Ok(gpio)
}
fn configure_pinmux(&self, mux: PinMuxConfig, cfg: &GenericPinctrlConfig) {
let mut regs = self.regs.lock();
regs.configure_pin_function(mux.pin as _, mux.function as _);
regs.configure_pin(mux.pin as _, mux.doen as _, mux.dout as _, mux.din as _);
if let Some(enable) = cfg.input_enable {
regs.set_input_enabled(mux.pin as _, enable);
}
if let Some(enable) = cfg.output_enable {
regs.set_output_enabled(mux.pin as _, enable);
}
if let Some(bias) = cfg.bias {
regs.set_bias(mux.pin as _, bias);
}
}
fn configure_pin_output(
&self,
pin: usize,
_active_level: GpioPinLevel,
initial_level: GpioPinLevel,
_output_bias: Option<OutputPinBias>,
) -> Result<(), Error> {
// TODO configure pull up/pull down?
let mut regs = self.regs.lock();
let dout_val = initial_level == GpioPinLevel::High;
regs.configure_pin_function(pin, 0);
regs.configure_pin(pin, 0, dout_val as u32, 0);
Ok(())
}
fn ensure_init(&self) -> Result<(), Error> {
let res = self.init.or_init_with(|| {
for clock in self.clocks.iter() {
clock.enable()?;
}
for reset in self.resets.iter() {
reset.deassert()?;
}
// TODO disable all pin IRQs
Ok(())
});
*res
}
}
impl<R: GpioRegs> Device for Gpio<R> {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
self.ensure_init()
}
fn display_name(&self) -> &str {
R::NAME
}
}
impl<R: GpioRegs> GpioController for Gpio<R> {
fn setup_gpio(&self, pin: &PinHandle, _event: Option<GpioInterruptEvent>) -> Result<(), Error> {
log::warn!(
"TODO: setup gpio #{} input={}, output={}",
pin.index,
pin.config.input,
pin.config.output
);
// TODO bidi pins?
match pin.config.force_single_direction() {
Some(SinglePinDirection::Input) => todo!(),
Some(SinglePinDirection::Output) => self.configure_pin_output(
pin.index as usize,
pin.config.active_level,
pin.config.initial_level,
pin.config.output_bias,
),
None => {
todo!("{}: support for bidi gpio", R::NAME);
}
}
}
fn read_gpio(&self, _pin: &PinHandle) -> Result<bool, Error> {
Err(Error::NotImplemented)
}
fn write_gpio(&self, pin: &PinHandle, value: bool) -> Result<(), Error> {
self.regs.lock().write_output_pin(pin.index as _, value);
Ok(())
}
}
impl<R: GpioRegs> DeviceTreePinController for Gpio<R> {
fn configure_pin_group(self: Arc<Self>, pins: &Arc<Node>) -> Result<(), Error> {
self.ensure_init()?;
for child in pins.children() {
let pinmux = child.property("pinmux").ok_or(Error::DoesNotExist)?;
let cfg = GenericPinctrlConfig::from_node(child);
for i in 0..pinmux.len() / 4 {
let mux = pinmux.read_cell(i, 1).unwrap() as u32;
let mux = PinMuxConfig::from_fdt(mux);
self.configure_pinmux(mux, &cfg);
}
}
Ok(())
}
fn map_gpio(
self: Arc<Self>,
property: &TProp,
offset: usize,
info: &DeviceTreeGpioPins,
) -> Option<(PinHandle, usize)> {
let (pin, options) = property.read_cells_at(offset, (1, 1))?;
let config = generic_gpio_config(options as u32, info);
Some((
PinHandle {
index: pin as u32,
config,
parent: self,
},
2,
))
}
}
impl GpioRegs for SysRegs {
const NAME: &'static str = "jh7110-sys-pinctrl";
const DOEN_OFFSET: usize = 0x00;
const DOUT_OFFSET: usize = 0x40 / 4;
const GPI_OFFSET: usize = 0x80 / 4;
const DOEN_MASK: u32 = 0x1F;
const DOUT_MASK: u32 = 0x3F;
const GPI_MASK: u32 = 0x3F;
unsafe fn map(base: PhysicalAddress) -> Result<Self, Error> {
DeviceMemoryIoMut::map_slice(base, 192, Default::default()).map(Self)
}
fn pin_functions(&self, pin: usize) -> Option<(usize, usize, u32)> {
SYS_PIN_FUNCTIONS.get(pin).copied()?
}
fn reg_mut(&mut self, index: usize) -> *mut u32 {
&raw mut self.0[index]
}
}
impl GpioRegs for AonRegs {
const NAME: &'static str = "jh7110-aon-pinctrl";
const DOEN_OFFSET: usize = 0x00;
const DOUT_OFFSET: usize = 0x04 / 4;
const GPI_OFFSET: usize = 0x08 / 4;
const DOEN_MASK: u32 = 0x7;
const DOUT_MASK: u32 = 0xF;
const GPI_MASK: u32 = 0xF;
unsafe fn map(base: PhysicalAddress) -> Result<Self, Error> {
DeviceMemoryIoMut::map_slice(base, 64, Default::default()).map(Self)
}
fn pin_functions(&self, _pin: usize) -> Option<(usize, usize, u32)> {
None
}
fn reg_mut(&mut self, index: usize) -> *mut u32 {
&raw mut self.0[index]
}
}
device_tree_driver! {
compatible: ["starfive,jh7110-sys-pinctrl"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let gpio = Gpio::<SysRegs>::from_device_tree(node, context)
.inspect_err(|e| log::error!("jh7110-sys-pinctrl: probe error: {e:?}"))
.ok()?;
node.make_pin_controller(gpio.clone());
Some(gpio)
}
}
}
device_tree_driver! {
compatible: ["starfive,jh7110-aon-pinctrl"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let gpio = Gpio::<AonRegs>::from_device_tree(node, context)
.inspect_err(|e| log::error!("jh7110-aon-pinctrl: probe error: {e:?}"))
.ok()?;
node.make_pin_controller(gpio.clone());
Some(gpio)
}
}
}
+17
View File
@@ -0,0 +1,17 @@
[package]
name = "ygg_driver_bsp_riscv"
version = "0.1.0"
edition = "2024"
[dependencies]
device-tree.workspace = true
device-api.workspace = true
yggdrasil-abi.workspace = true
libk-mm.workspace = true
libk-util.workspace = true
libk.workspace = true
kernel-arch-riscv64.workspace = true
tock-registers.workspace = true
log.workspace = true
bytemuck.workspace = true
+6
View File
@@ -0,0 +1,6 @@
#![allow(unsafe_op_in_unsafe_fn)]
#![no_std]
extern crate alloc;
mod plic;
@@ -1,6 +1,3 @@
//! RISC-V PLIC driver
use abi::{error::Error, primitive_enum};
use alloc::{sync::Arc, vec::Vec};
use device_api::{
device::{Device, DeviceInitContext},
@@ -15,6 +12,7 @@ use device_tree::{
},
DeviceTreePropertyRead, TProp,
};
use kernel_arch_riscv64::boot_hart_id;
use libk::{arch::Cpu, device::register_external_interrupt_controller};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
@@ -23,8 +21,7 @@ use tock_registers::{
register_structs,
registers::{ReadOnly, ReadWrite},
};
use crate::arch::riscv64::boot::boot_hart_id;
use yggdrasil_abi::{error::Error, primitive_enum};
const MAX_IRQS: usize = 1024;
+96
View File
@@ -0,0 +1,96 @@
use alloc::{boxed::Box, sync::Arc};
use yggdrasil_abi::error::Error;
use crate::device::Device;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InputPinBias {
PullUp,
PullDown,
Floating,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OutputPinBias {
PushPull,
OpenDrain,
HighZ,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GpioPinLevel {
High,
Low,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SinglePinDirection {
Output,
Input,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GpioInterruptMode {
RisingEdge,
FallingEdge,
BothEdges,
HighLevel,
LowLevel,
}
pub struct GpioInterruptEvent {
pub mode: GpioInterruptMode,
pub event: u64,
}
pub struct GpioInterrupt {
pub mode: GpioInterruptMode,
pub handler: Box<dyn Fn() + Send>,
}
pub struct GpioPinConfig {
pub input: bool,
pub output: bool,
pub input_bias: Option<InputPinBias>,
pub output_bias: Option<OutputPinBias>,
pub active_level: GpioPinLevel,
pub initial_level: GpioPinLevel,
}
pub struct PinHandle {
pub index: u32,
pub config: GpioPinConfig,
pub parent: Arc<dyn GpioController>,
}
pub trait GpioController: Device {
fn write_gpio(&self, pin: &PinHandle, value: bool) -> Result<(), Error>;
fn read_gpio(&self, pin: &PinHandle) -> Result<bool, Error>;
fn setup_gpio(&self, pin: &PinHandle, event: Option<GpioInterruptEvent>) -> Result<(), Error>;
}
pub trait GpioOutput: Sync + Send {
fn write(&self, value: bool) -> Result<(), Error>;
}
impl PinHandle {
pub fn configure(&self) -> Result<(), Error> {
self.parent.setup_gpio(self, None)
}
pub fn write(&self, value: bool) -> Result<(), Error> {
self.parent.write_gpio(self, value)
}
}
impl GpioPinConfig {
pub fn force_single_direction(&self) -> Option<SinglePinDirection> {
match (self.input, self.output) {
(true, false) => Some(SinglePinDirection::Input),
(false, true) => Some(SinglePinDirection::Output),
(false, false) => None,
(true, true) => None,
}
}
}
+1
View File
@@ -7,6 +7,7 @@ extern crate alloc;
pub mod bus;
pub mod clock;
pub mod device;
pub mod gpio;
pub mod interrupt;
pub mod serial;
pub mod timer;
@@ -2,11 +2,12 @@
use alloc::sync::Arc;
use device_api::{
clock::{ClockHandle, ResetHandle},
gpio::PinHandle,
interrupt::FullIrq,
};
use fdt_rs::spec::Phandle;
use crate::{DeviceTreePropertyRead, TProp};
use crate::{driver::DeviceTreeGpioPins, DeviceTreePropertyRead, TProp};
use super::{lookup_phandle, Node};
@@ -20,6 +21,12 @@ pub(crate) struct ResetIter<'dt> {
pub(crate) offset: usize,
}
pub(crate) struct GpioIter<'o, 'dt> {
pub(crate) options: &'o DeviceTreeGpioPins,
pub(crate) gpios: TProp<'dt>,
pub(crate) offset: usize,
}
impl Iterator for ClockIter<'_> {
type Item = ClockHandle;
@@ -52,6 +59,22 @@ impl Iterator for ResetIter<'_> {
}
}
impl Iterator for GpioIter<'_, '_> {
type Item = PinHandle;
fn next(&mut self) -> Option<Self::Item> {
if self.offset >= self.gpios.len() {
return None;
}
let phandle = self.gpios.read_cell(self.offset, 1)? as Phandle;
let gpioc = lookup_phandle(phandle, true)?;
let gpioc = gpioc.as_pin_controller()?;
let (pin, len) = gpioc.map_gpio(&self.gpios, self.offset + 1, self.options)?;
self.offset += len + 1;
Some(pin)
}
}
// Interrupt controller handling
/// Reads interrupt information, as interpreted by `interrupt_controller`, from `property` at a
+13 -2
View File
@@ -1,6 +1,7 @@
//! Device tree-based driver definitions
use alloc::sync::Arc;
use device_api::gpio::GpioPinLevel;
use yggdrasil_abi::error::Error;
mod controller;
@@ -17,11 +18,21 @@ pub use macros::device_tree_driver;
pub use registry::{lookup_phandle, register_driver};
pub use syscon::DeviceTreeSyscon;
pub use traits::{
DeviceTreeClockController, DeviceTreeInterruptController, DeviceTreeResetController, Driver,
ProbeContext,
DeviceTreeClockController, DeviceTreeInterruptController, DeviceTreePinController,
DeviceTreeResetController, Driver, ProbeContext,
};
pub use tree::{find_node, unflatten_device_tree, walk_device_tree, Node};
/// Contextual information about a GPIO pin being configured
pub struct DeviceTreeGpioPins {
/// Whether to enable input on the pin
pub input: bool,
/// Whether to enable output on the pin
pub output: bool,
/// Initial level to configure the GPIO with
pub initial_level: GpioPinLevel,
}
/// Specifies initialization sequence requirement for a driver
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum InitSequence {
+17 -1
View File
@@ -6,10 +6,12 @@ use device_api::{
bus::Bus,
clock::{ClockHandle, ResetHandle},
device::Device,
gpio::PinHandle,
interrupt::{ExternalInterruptController, FullIrq},
};
use libk::error::Error;
use crate::TProp;
use crate::{driver::DeviceTreeGpioPins, TProp};
use super::{InitSequence, Node};
@@ -51,6 +53,20 @@ pub struct ProbeContext {
pub sequence: Option<InitSequence>,
}
/// `-pinctrl`-type devices, used for configuring pin groups
pub trait DeviceTreePinController {
/// Configure a pin group
fn configure_pin_group(self: Arc<Self>, pins: &Arc<Node>) -> Result<(), Error>;
/// Reads GPIO pin information from `property` at given `offset` and maps it to a
/// [PinHandle], returning the handle + the size of the GPIO entry.
fn map_gpio(
self: Arc<Self>,
property: &TProp,
offset: usize,
info: &DeviceTreeGpioPins,
) -> Option<(PinHandle, usize)>;
}
impl ProbeContext {
/// See [Node::map_range]
pub fn map_range(&self, range: Range<u64>) -> Option<Range<u64>> {
+69 -3
View File
@@ -9,6 +9,7 @@ use device_api::{
bus::Bus,
clock::{ClockController, ClockHandle, ResetHandle},
device::{Device, DeviceInitContext},
gpio::PinHandle,
interrupt::{ExternalInterruptController, FullIrq, MessageInterruptController},
};
use fdt_rs::spec::Phandle;
@@ -18,7 +19,10 @@ use libk_util::OneTimeInit;
use yggdrasil_abi::error::Error;
use crate::{
driver::DeviceTreeSyscon, DeviceTree, DeviceTreeNodeExt, DeviceTreePropertyRead, TNode, TProp,
driver::{
controller::GpioIter, traits::DeviceTreePinController, DeviceTreeGpioPins, DeviceTreeSyscon,
},
tree, DeviceTree, DeviceTreeNodeExt, DeviceTreePropertyRead, TNode, TProp,
};
use super::{
@@ -55,6 +59,7 @@ pub struct Node {
pub(crate) clock_controler: OneTimeInit<Arc<dyn DeviceTreeClockController>>,
pub(crate) reset_controller: OneTimeInit<Arc<dyn DeviceTreeResetController>>,
pub(crate) system_controller: OneTimeInit<Arc<dyn DeviceTreeSyscon>>,
pub(crate) pin_controller: OneTimeInit<Arc<dyn DeviceTreePinController>>,
}
pub(crate) struct ProbedDevice {
@@ -90,6 +95,7 @@ impl NodeDevice {
impl Node {
fn probe_single(node: &Arc<Node>, cx: &mut ProbeContext) -> Option<ProbedDevice> {
let name = node.name();
let compatible = node.compatible.as_ref()?;
let drivers = DRIVERS.read();
@@ -100,7 +106,6 @@ impl Node {
if libk::config::get().device_tree.log_missing {
if driver.is_none() {
let name = node.name();
// FIXME don't spam virtio missing stuff
if !name.map_or(false, |n| n.starts_with("virtio_mmio")) {
for (i, compatible) in compatible.as_str_list().enumerate() {
@@ -115,6 +120,11 @@ impl Node {
}
let driver = driver?;
// Initialize default pinctrl before probing
node.setup_pins()
.inspect_err(|e| log::error!("{name:?}: pinctrl init error {e:?}"))
.ok()?;
let device = driver.imp.probe(node, cx);
// Move to early init unless driver wants to sleep
@@ -212,6 +222,11 @@ impl Node {
self.system_controller.init(syscon);
}
/// Informs the node of its capability as a pin controller.
pub fn make_pin_controller(&self, pinctrl: Arc<dyn DeviceTreePinController>) {
self.pin_controller.init(pinctrl);
}
/// Returns the device driver associated with this node, if any was probed.
pub fn driver(&self) -> Option<&'static dyn Driver> {
let probed = self.device.try_get()?.as_probed()?;
@@ -337,6 +352,24 @@ impl Node {
.map(|range| PhysicalAddress::from_u64(range.start))
}
/// Returns an iterator over the node's defined `gpios`
pub fn gpios<'a>(
&self,
options: &'a DeviceTreeGpioPins,
) -> Option<impl Iterator<Item = PinHandle> + 'a> {
let gpios = self.property("gpios")?;
Some(GpioIter {
gpios,
options,
offset: 0,
})
}
/// Returns a `gpios` entry with given index
pub fn gpio(&self, options: &DeviceTreeGpioPins, index: usize) -> Option<PinHandle> {
self.gpios(options)?.nth(index)
}
/// Returns an input `clock` handle for a given reset name
pub fn named_clock(&self, name: &str) -> Option<ClockHandle> {
self.property("clock-names")?
@@ -409,11 +442,16 @@ impl Node {
.map(|e| e.clone().as_interrupt_controller())
}
/// Attempts to get an system controller represented by this node, if any
/// Attempts to get a system controller represented by this node, if any
pub fn as_system_controller(self: &Arc<Self>) -> Option<Arc<dyn DeviceTreeSyscon>> {
self.system_controller.try_get().cloned()
}
/// Attempts to get a pin controller represented by this node, if any
pub fn as_pin_controller(&self) -> Option<Arc<dyn DeviceTreePinController>> {
self.pin_controller.try_get().cloned()
}
/// Returns the `#address-cells` value of the node's parent bus
pub fn bus_address_cells(&self) -> usize {
self.bus_address_cells
@@ -444,6 +482,11 @@ impl Node {
self.dt_node.property(name)
}
/// Returns `true` if the node has a property `name` inside
pub fn has_property(&self, name: &str) -> bool {
self.dt_node.has_property(name)
}
/// Interprets property `name` as a single cell and casts it to usize
pub fn prop_usize(&self, name: &str) -> Option<usize> {
self.dt_node.prop_cell_usize(name)
@@ -479,6 +522,28 @@ impl Node {
Some(node)
}
/// Dump the contents of the device tree node into Info-level log
pub fn dump(&self) {
tree::dump(self.dt_node.clone(), 0);
}
fn setup_pins(&self) -> Result<(), Error> {
// TODO lookup pin state by name
let Some(pinctrl0) = self.prop_usize("pinctrl-0") else {
return Ok(());
};
let pinctrl0 = lookup_phandle(pinctrl0 as Phandle, false).ok_or(Error::DoesNotExist)?;
// Find pinctrl-group's parent pin controller
let pin_controller = pinctrl0.parent().ok_or(Error::DoesNotExist)?;
pin_controller.probe().ok_or(Error::DoesNotExist)?;
let pin_controller = pin_controller
.as_pin_controller()
.ok_or(Error::DoesNotExist)?;
pin_controller.configure_pin_group(&pinctrl0)
}
}
impl fmt::Debug for Node {
@@ -531,6 +596,7 @@ fn unflatten_node(
clock_controler: OneTimeInit::new(),
reset_controller: OneTimeInit::new(),
system_controller: OneTimeInit::new(),
pin_controller: OneTimeInit::new(),
});
if let Some(phandle) = phandle {
+119 -2
View File
@@ -1,17 +1,48 @@
//! General helpers and utilities for device tree drivers
use alloc::sync::Arc;
use device_api::interrupt::{FullIrq, MessageInterruptController};
use device_api::{
gpio::{GpioPinConfig, GpioPinLevel, InputPinBias, OutputPinBias},
interrupt::{FullIrq, MessageInterruptController},
};
use fdt_rs::prelude::PropReader;
use yggdrasil_abi::net::MacAddress;
use crate::{
driver::{lookup_phandle, map_interrupt_at},
driver::{lookup_phandle, map_interrupt_at, DeviceTreeGpioPins},
DeviceTreePropertyRead, TProp,
};
use super::Node;
/// Defines how push-pull is configured for a pin
#[derive(Debug, Clone, Copy)]
pub enum GenericPinctrlBiasConfig {
/// Disable push-pull
Disable,
/// Pull pin up
PullUp,
/// Pull pin down
PullDown,
}
/// Represents pin config parameters most commonly used in FDTs
#[derive(Debug, Clone, Copy)]
pub struct GenericPinctrlConfig {
/// Whether and how to configure the push-pull on the pin
pub bias: Option<GenericPinctrlBiasConfig>,
/// Drive strength in pinmux-specific units
pub drive_strength: Option<u32>,
/// Whether to enable the pin as an input
pub input_enable: Option<bool>,
/// Whether to enable the Schmitt trigger on the pin
pub input_schmitt_enable: Option<bool>,
/// Whether to enable the pin as an output
pub output_enable: Option<bool>,
/// GPIO rise/fall time
pub slew_rate: Option<u32>,
}
/// Represents an entry in a PCIe-controller's `interrupt-map` field
#[derive(Debug)]
pub struct PcieInterruptEntry {
@@ -58,6 +89,55 @@ pub struct PcieMsiMapIter<'a> {
offset: usize,
}
impl GenericPinctrlConfig {
fn tristate(enable: bool, disable: bool) -> Option<bool> {
if enable {
Some(true)
} else if disable {
Some(false)
} else {
None
}
}
/// Retrieves a generic pin configuration from a device tree node
pub fn from_node(node: &Node) -> Self {
let bias_disable = node.has_property("bias-disable");
let bias_pull_up = node.has_property("bias-pull-up");
let bias_pull_down = node.has_property("bias-pull-down");
let drive_strength = node.prop_usize("drive-strength").map(|p| p as u32);
let input_disable = node.has_property("input-disable");
let input_enable = node.has_property("input-enable");
let input_schmitt_enable = node.has_property("input-schmitt-enable");
let input_schmitt_disable = node.has_property("input-schmitt-disable");
let output_disable = node.has_property("output-disable");
let output_enable = node.has_property("output-enable");
let slew_rate = node.prop_usize("slew-rate").map(|p| p as u32);
let bias = if bias_disable {
Some(GenericPinctrlBiasConfig::Disable)
} else if bias_pull_up {
Some(GenericPinctrlBiasConfig::PullUp)
} else if bias_pull_down {
Some(GenericPinctrlBiasConfig::PullDown)
} else {
None
};
let input_enable = Self::tristate(input_enable, input_disable);
let input_schmitt_enable = Self::tristate(input_schmitt_enable, input_schmitt_disable);
let output_enable = Self::tristate(output_enable, output_disable);
Self {
bias,
drive_strength,
input_enable,
input_schmitt_enable,
output_enable,
slew_rate,
}
}
}
impl Iterator for PcieInterruptMapIter<'_> {
type Item = PcieInterruptEntry;
@@ -195,3 +275,40 @@ pub fn read_mac_address(node: &Arc<Node>) -> Option<MacAddress> {
let mac_bytes: [u8; 6] = property.raw().try_into().ok()?;
Some(MacAddress::from(mac_bytes))
}
/// Parse a generic gpio configuration from the device tree 1-cell format
pub fn generic_gpio_config(cell: u32, gpio: &DeviceTreeGpioPins) -> GpioPinConfig {
const GPIO_ACTIVE_LOW: u32 = 1 << 0;
const GPIO_SINGLE_ENDED: u32 = 1 << 1;
const GPIO_LINE_OPEN_DRAIN: u32 = 1 << 2;
const GPIO_PULL_UP: u32 = 1 << 4;
const GPIO_PULL_DOWN: u32 = 1 << 5;
const GPIO_PULL_DISABLE: u32 = 1 << 6;
const GPIO_OPEN_DRAIN: u32 = GPIO_LINE_OPEN_DRAIN | GPIO_SINGLE_ENDED;
let active_level = if cell & GPIO_ACTIVE_LOW != 0 {
GpioPinLevel::Low
} else {
GpioPinLevel::High
};
let output_bias = match cell & (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_DRAIN) {
0 => Some(OutputPinBias::PushPull),
GPIO_OPEN_DRAIN | GPIO_SINGLE_ENDED => Some(OutputPinBias::OpenDrain),
_ => None,
};
let input_bias = match cell & (GPIO_PULL_UP | GPIO_PULL_DOWN | GPIO_PULL_DISABLE) {
GPIO_PULL_DISABLE => Some(InputPinBias::Floating),
GPIO_PULL_UP => Some(InputPinBias::PullUp),
GPIO_PULL_DOWN => Some(InputPinBias::PullDown),
_ => None,
};
GpioPinConfig {
input: gpio.input,
output: gpio.output,
initial_level: gpio.initial_level,
input_bias,
output_bias,
active_level,
}
}
+20 -20
View File
@@ -197,7 +197,7 @@ impl<'a> DeviceTree<'a> {
unsafe impl Sync for DeviceTree<'_> {}
fn indent(amount: usize) {
log::info!(target: "raw", "{0:1$}", "", amount * 2);
log::info!(target: ":raw", "{0:1$}", "", amount * 2);
}
fn dump_property(node: &TNode, prop: TProp, level: usize) {
@@ -211,14 +211,14 @@ fn dump_property(node: &TNode, prop: TProp, level: usize) {
let name = prop.name().unwrap_or("<unknown-prop>");
indent(level);
log::info!(target: "raw", "{name}");
log::info!(target: ":raw", "{name}");
if prop.len() == 0 {
log::info!(target: "raw", ";\n");
log::info!(target: ":raw", ";\n");
return;
}
log::info!(target: "raw", " = ");
log::info!(target: ":raw", " = ");
let ty = match name {
"model" | "compatible" | "clock-output-names" | "clock-names" | "device_type"
@@ -234,48 +234,48 @@ fn dump_property(node: &TNode, prop: TProp, level: usize) {
// "..."
for (i, string) in prop.as_str_list().enumerate() {
if i != 0 {
log::info!(target: "raw", ", ");
log::info!(target: ":raw", ", ");
}
log::info!(target: "raw", "{string:?}");
log::info!(target: ":raw", "{string:?}");
}
}
Type::Cells => {
// <...>
log::info!(target: "raw", "<");
log::info!(target: ":raw", "<");
let mut i = 0;
while let Some(value) = prop.read_cell(i, 1) {
if i != 0 {
log::info!(target: "raw", ", ");
log::info!(target: ":raw", ", ");
}
log::info!(target: "raw", "{value:#x}");
log::info!(target: ":raw", "{value:#x}");
i += 1;
}
log::info!(target: "raw", ">");
log::info!(target: ":raw", ">");
}
Type::Bytes => {
// <...>
log::info!(target: "raw", "<");
log::info!(target: ":raw", "<");
for (i, byte) in prop.raw().iter().enumerate() {
if i != 0 {
log::info!(target: "raw", ", ");
log::info!(target: ":raw", ", ");
}
log::info!(target: "raw", "{byte:#x}");
log::info!(target: ":raw", "{byte:#x}");
}
log::info!(target: "raw", ">");
log::info!(target: ":raw", ">");
}
}
log::info!(target: "raw", ";\n");
log::info!(target: ":raw", ";\n");
}
fn dump(node: TNode, level: usize) {
pub(crate) fn dump(node: TNode, level: usize) {
let name = node.name().unwrap_or("<unknown>");
indent(level);
if name.is_empty() {
log::info!(target: "raw", "{{\n");
log::info!(target: ":raw", "{{\n");
} else {
log::info!(target: "raw", "{name}: {{\n");
log::info!(target: ":raw", "{name}: {{\n");
}
let mut do_break = false;
@@ -286,12 +286,12 @@ fn dump(node: TNode, level: usize) {
for child in node.children() {
if do_break {
log::info!(target: "raw", "\n");
log::info!(target: ":raw", "\n");
do_break = false;
}
dump(child, level + 1);
}
indent(level);
log::info!(target: "raw", "}}\n");
log::info!(target: ":raw", "}}\n");
}
+35
View File
@@ -0,0 +1,35 @@
pub trait BitField: Sized {
fn modify_bit(&self, bit: usize, value: bool) -> Self;
fn set_bit(&mut self, bit: usize, value: bool);
fn get_bit(&self, bit: usize) -> bool;
}
macro_rules! impl_bit_field {
($($ty:ty),+ $(,)?) => {
$(
impl BitField for $ty {
#[inline]
fn modify_bit(&self, bit: usize, value: bool) -> Self {
if value {
*self | (1 << bit)
} else {
*self & !(1 << bit)
}
}
#[inline]
fn set_bit(&mut self, bit: usize, value: bool) {
*self = self.modify_bit(bit, value);
}
#[inline]
fn get_bit(&self, bit: usize) -> bool {
*self & (1 << bit) != 0
}
}
)+
};
}
impl_bit_field!(u8, u16, u32, u64, usize);
impl_bit_field!(i8, i16, i32, i64, isize);
+1
View File
@@ -18,6 +18,7 @@ use core::{
panic,
};
pub mod bit;
pub mod event;
pub mod ext;
pub mod hash_table;
+81
View File
@@ -0,0 +1,81 @@
use core::{
sync::atomic::{AtomicU64, Ordering},
time::Duration,
};
use alloc::{boxed::Box, collections::BTreeMap, sync::Arc, vec::Vec};
use device_api::gpio::{GpioInterrupt, GpioInterruptEvent, GpioOutput, PinHandle};
use libk_util::{ring::LossyRingQueue, sync::spin_rwlock::IrqSafeRwLock};
use yggdrasil_abi::error::Error;
use crate::task::runtime;
static GPIO_EVENT_QUEUE: LossyRingQueue<u64> = LossyRingQueue::with_capacity(64);
static GPIO_EVENT_MAP: IrqSafeRwLock<BTreeMap<u64, Box<dyn Fn() + Send>>> =
IrqSafeRwLock::new(BTreeMap::new());
static GPIO_EVENT_ID: AtomicU64 = AtomicU64::new(1);
static GPIO_HEARTBEATS: IrqSafeRwLock<Vec<Arc<dyn GpioOutput>>> = IrqSafeRwLock::new(Vec::new());
pub trait GpioEvent {
fn configure_with_interrupt(&self, int: GpioInterrupt) -> Result<(), Error>;
}
impl GpioEvent for PinHandle {
fn configure_with_interrupt(&self, int: GpioInterrupt) -> Result<(), Error> {
let event = bind_gpio_event(int.handler);
self.parent.setup_gpio(
self,
Some(GpioInterruptEvent {
mode: int.mode,
event,
}),
)
}
}
pub async fn gpio_event_handler_task() {
loop {
let id = GPIO_EVENT_QUEUE.read().await;
let lock = GPIO_EVENT_MAP.read();
if let Some(handler) = lock.get(&id) {
handler();
}
}
}
pub async fn heartbeat_handler_task() {
const LEVELS: &[(Duration, bool)] = &[
(Duration::from_millis(50), true),
(Duration::from_millis(200), false),
(Duration::from_millis(50), true),
(Duration::from_millis(700), false),
];
let mut index = 0;
loop {
let (timeout, level) = LEVELS[index];
for pin in GPIO_HEARTBEATS.read().iter() {
pin.write(level).ok();
}
runtime::sleep(timeout).await;
index = (index + 1) % LEVELS.len();
}
}
pub fn register_heartbeat_gpio(gpio: Arc<dyn GpioOutput>) {
GPIO_HEARTBEATS.write().push(gpio);
}
pub fn bind_gpio_event(handler: Box<dyn Fn() + Send>) -> u64 {
let id = GPIO_EVENT_ID.fetch_add(1, Ordering::Acquire);
GPIO_EVENT_MAP.write().insert(id, handler);
id
}
pub fn signal_gpio_event(ev: u64) {
GPIO_EVENT_QUEUE.write(ev);
}
+1
View File
@@ -36,6 +36,7 @@ pub mod arch;
pub mod config;
pub mod debug;
pub mod device;
pub mod event;
pub mod fs;
pub mod module;
pub mod random;
+8 -1
View File
@@ -350,6 +350,7 @@ 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,
@@ -513,7 +514,11 @@ impl Thread {
/// Returns `true` if the thread is a tracee of given process
pub fn is_tracee_of(&self, pid: ProcessId) -> bool {
self.debug.lock().tracer == Some(pid)
{
let tracer = self.debug.lock().tracer;
// log::info!("{:?}'s tracer: {:?}", self.id, tracer);
tracer == Some(pid)
}
}
pub fn attach_trace(&self, tracer: ProcessId) -> Result<(), Error> {
@@ -577,11 +582,13 @@ 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)
}
+1 -7
View File
@@ -7,7 +7,7 @@ use core::{
use elf::{abi, relocation::Elf64_Rela};
use kernel_arch::{Architecture, ArchitectureImpl};
use kernel_arch_riscv64::{mem, CPU_COUNT};
use kernel_arch_riscv64::{mem, BOOT_HART_ID, CPU_COUNT};
use libk::{
debug,
fs::{devfs, sysfs},
@@ -28,7 +28,6 @@ const BOOT_STACK_SIZE: usize = 65536;
static mut KERNEL_LOAD_BASE: u64 = 0;
static mut DTB_ADDRESS: PhysicalAddress = PhysicalAddress::ZERO;
static mut BOOT_HART_ID: u64 = 0;
static mut BOOT_STACK: BootStack<BOOT_STACK_SIZE> = BootStack::zeroed();
#[repr(C, align(0x10))]
@@ -58,11 +57,6 @@ unsafe extern "C" fn relocate_kernel(image_base: i64, rela_start: usize, rela_en
}
}
/// Returns the ID of the bootstrap HART
pub fn boot_hart_id() -> u64 {
unsafe { BOOT_HART_ID }
}
unsafe fn long_jump(pc: usize, sp: usize, a0: usize) -> ! {
core::arch::asm!("mv sp, {sp}; jr {pc}", in("a0") a0, sp = in(reg) sp, pc = in(reg) pc, options(noreturn))
}
+2 -2
View File
@@ -6,7 +6,7 @@ use abi::error::Error;
use device_api::interrupt::{IpiDeliveryTarget, IpiMessage};
use device_tree::{DeviceTree, DeviceTreeNodeExt};
use kernel_arch_riscv64::{
mem::auto_lower_address, registers::SIP, sbi, ArchitectureImpl, CPU_COUNT,
boot_hart_id, mem::auto_lower_address, registers::SIP, sbi, ArchitectureImpl, CPU_COUNT,
};
use libk::arch::Cpu;
use libk_mm::{
@@ -15,7 +15,7 @@ use libk_mm::{
};
use tock_registers::interfaces::ReadWriteable;
use crate::{arch::riscv64::boot::boot_hart_id, panic};
use crate::panic;
#[allow(missing_docs)]
pub const SECONDARY_STACK_SIZE: usize = 32768;
-6
View File
@@ -1,10 +1,4 @@
//! Clock controller drivers
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
pub mod bcm2835_aux;
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
pub mod jh7110_clocks;
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))]
pub mod fixed;
+165
View File
@@ -0,0 +1,165 @@
//! GPIO drivers
use abi::error::Error;
use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
use device_api::{
device::{Device, DeviceInitContext},
gpio::{GpioInterrupt, GpioInterruptMode, GpioOutput, GpioPinLevel, PinHandle},
};
use device_tree::driver::{device_tree_driver, DeviceTreeGpioPins, Node, ProbeContext};
use libk::event::{self, GpioEvent};
enum LedFunction {
Heartbeat,
Other,
}
struct LedPin {
name: String,
function: LedFunction,
handle: PinHandle,
}
struct KeyPin {
name: String,
handle: PinHandle,
}
trait Pin: Sized + Send + Sync + 'static {
fn from_device_tree(node: &Arc<Node>) -> Option<Arc<Self>>;
fn configure(self: &Arc<Self>) -> Result<(), Error>;
fn name(&self) -> &str;
}
struct PinGroup<P: Pin> {
name: String,
pins: Vec<Arc<P>>,
}
impl<P: Pin> PinGroup<P> {
fn from_device_tree(node: &Arc<Node>) -> Option<Arc<dyn Device>> {
let pins = node
.children()
.filter_map(P::from_device_tree)
.collect::<Vec<_>>();
if pins.is_empty() {
return None;
}
let name = node.name().unwrap_or("pin-group");
Some(Arc::new(Self {
pins,
name: name.into(),
}))
}
}
impl<P: Pin> Device for PinGroup<P> {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
for pin in self.pins.iter() {
if let Err(error) = pin.configure() {
log::error!("gpio {} setup error: {:?}", pin.name(), error);
}
}
Ok(())
}
fn display_name(&self) -> &str {
&self.name
}
}
impl Pin for LedPin {
fn from_device_tree(node: &Arc<Node>) -> Option<Arc<Self>> {
let handle = node.gpio(
&DeviceTreeGpioPins {
input: false,
output: true,
initial_level: GpioPinLevel::Low,
},
0,
)?;
let function = match node.prop_str("function") {
Some("heartbeat") => LedFunction::Heartbeat,
_ => LedFunction::Other,
};
let name = node.name().unwrap_or("gpio-led").into();
Some(Arc::new(Self {
name,
function,
handle,
}))
}
fn configure(self: &Arc<Self>) -> Result<(), Error> {
self.handle.configure()?;
match self.function {
LedFunction::Heartbeat => {
log::info!("Register led {:?} as heartbeat indicator", self.name);
event::register_heartbeat_gpio(self.clone())
}
_ => (),
}
Ok(())
}
fn name(&self) -> &str {
&self.name
}
}
impl GpioOutput for LedPin {
fn write(&self, value: bool) -> Result<(), Error> {
self.handle.write(value)
}
}
impl Pin for KeyPin {
fn from_device_tree(node: &Arc<Node>) -> Option<Arc<Self>> {
let handle = node.gpio(
&DeviceTreeGpioPins {
input: true,
output: false,
initial_level: GpioPinLevel::Low,
},
0,
)?;
let name = node.name().unwrap_or("gpio-key").into();
Some(Arc::new(Self { name, handle }))
}
fn configure(self: &Arc<Self>) -> Result<(), Error> {
let key = self.clone();
self.handle.configure_with_interrupt(GpioInterrupt {
mode: GpioInterruptMode::FallingEdge,
handler: Box::new(move || {
log::info!("GPIO key triggered: {:?}", key.name);
}),
})
}
fn name(&self) -> &str {
&self.name
}
}
device_tree_driver! {
compatible: ["gpio-leds"],
driver: {
fn probe(&self, node: &Arc<Node>, _context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
PinGroup::<LedPin>::from_device_tree(node)
}
}
}
device_tree_driver! {
compatible: ["gpio-keys"],
driver: {
fn probe(&self, node: &Arc<Node>, _context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
PinGroup::<KeyPin>::from_device_tree(node)
}
}
}
-4
View File
@@ -1,4 +0,0 @@
//! Interrupt controller drivers
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
pub mod riscv_plic;
-4
View File
@@ -1,4 +0,0 @@
//! Mailbox interfaces
#[cfg(any(rust_analyzer, target_arch = "aarch64"))]
mod bcm2835_mbox;
+3 -3
View File
@@ -6,12 +6,12 @@ use libk_util::OneTimeInit;
pub mod bus;
pub mod clock;
pub mod display;
pub mod interrupt;
pub mod mbox;
pub mod pinctrl;
pub mod power;
pub mod serial;
// pub mod timer;
#[cfg(any(rust_analyzer, not(target_arch = "x86_64")))]
pub mod gpio;
/// Generic machine description string
pub static MACHINE_NAME: OneTimeInit<String> = OneTimeInit::new();
@@ -1,49 +0,0 @@
use abi::error::Error;
use alloc::sync::Arc;
use device_api::{
clock::ClockHandle,
device::{Device, DeviceInitContext},
};
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
use libk_mm::device::DeviceMemoryIoMut;
use libk_util::sync::IrqSafeSpinlock;
struct Jh7110SysPinctrl {
#[allow(unused)]
regs: IrqSafeSpinlock<DeviceMemoryIoMut<'static, [u32]>>,
clock: ClockHandle,
}
unsafe impl Send for Jh7110SysPinctrl {}
unsafe impl Sync for Jh7110SysPinctrl {}
impl Device for Jh7110SysPinctrl {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
self.clock.enable()?;
Ok(())
}
fn display_name(&self) -> &str {
"jh7110-sys-pinctrl"
}
}
device_tree_driver! {
compatible: ["starfive,jh7110-sys-pinctrl"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let clock = node.clock(0).unwrap();
let base = node.map_base(context, 0)?;
let regs = unsafe { DeviceMemoryIoMut::map_slice(base, 0x2B8 / 4, Default::default()) }.ok()?;
let pinctrl = Arc::new(Jh7110SysPinctrl {
clock,
regs: IrqSafeSpinlock::new(regs)
});
Some(pinctrl)
}
}
}
-4
View File
@@ -1,4 +0,0 @@
//! GPIO/Pin controller, multiplexer drivers
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
mod jh7110_pinctrl;
-6
View File
@@ -1,11 +1,5 @@
//! Serial device interfaces
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
pub mod bcm2835_aux_uart;
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
pub mod pl011;
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
pub mod ns16550a;
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
+4
View File
@@ -37,6 +37,7 @@ pub fn kinit() -> Result<(), Error> {
#[cfg(any(rust_analyzer, target_arch = "riscv64", target_arch = "aarch64"))]
{
use device_tree::driver::InitSequence;
use libk::event;
device_tree::driver::lazy_init(
|_| (),
@@ -55,6 +56,9 @@ pub fn kinit() -> Result<(), Error> {
},
InitSequence::Late,
);
runtime::spawn(event::gpio_event_handler_task()).ok();
runtime::spawn(event::heartbeat_handler_task()).ok();
}
// Initialize PCI devices
if let Err(error) = PciBusManager::setup_bus_devices(false) {
+6
View File
@@ -74,7 +74,13 @@ extern crate ygg_driver_virtio_net;
cfg_if::cfg_if! {
if #[cfg(target_arch = "x86_64")] {
extern crate ygg_driver_net_igbe;
} else if #[cfg(target_arch = "aarch64")] {
extern crate ygg_driver_bsp_arm;
extern crate ygg_driver_bsp_bcm283x;
} else if #[cfg(target_arch = "riscv64")] {
extern crate ygg_driver_bsp_riscv;
extern crate ygg_driver_bsp_jh7110;
extern crate ygg_driver_net_stmmac;
}
}
+2
View File
@@ -47,6 +47,8 @@ struct SpawnOptions<'a> {
pub environment: &'a [&'a str],
/// Working directory
pub directory: Option<&'a str>,
/// Root path
pub root: Option<&'a str>,
/// Optional arguments to specify details of the creation
pub optional: &'a [SpawnOption],
/// Extra flags controlling the new process behavior
+2
View File
@@ -2970,6 +2970,8 @@ name = "strace"
version = "0.1.0"
dependencies = [
"clap",
"cross",
"libc",
"runtime",
]
+1
View File
@@ -0,0 +1 @@
+1
View File
@@ -4,6 +4,7 @@
pub(crate) mod sys;
pub mod debug;
pub mod fs;
pub mod io;
pub mod mem;
+58
View File
@@ -1,6 +1,64 @@
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>;
}
+175 -2
View File
@@ -10,10 +10,12 @@ pub mod time;
pub mod timer;
use std::{
ffi::c_int,
ffi::{c_int, c_void},
io,
mem::MaybeUninit,
os::{fd::RawFd, unix::process::CommandExt},
process::Command,
ptr::null_mut,
sync::Mutex,
};
@@ -26,7 +28,126 @@ pub use socket::{BorrowedAddressImpl, LocalPacketSocketImpl, OwnedAddressImpl};
pub use term::RawStdinImpl;
pub use timer::TimerFdImpl;
use crate::process::CommandSpawnExt;
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);
}
}
}
fn dummy_sigint() {}
@@ -61,6 +182,8 @@ 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))
}
@@ -69,4 +192,54 @@ 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 })
}
}
}
}
}
+146 -3
View File
@@ -11,8 +11,20 @@ pub mod timer;
use std::{
io,
os::{fd::RawFd, yggdrasil::process::CommandExt},
process::Command,
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},
};
pub use mem::{FileMappingImpl, SharedMemoryImpl};
@@ -24,7 +36,131 @@ pub use socket::{BorrowedAddressImpl, LocalPacketSocketImpl, OwnedAddressImpl};
pub use term::RawStdinImpl;
pub use timer::TimerFdImpl;
use crate::process::CommandSpawnExt;
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);
}
}
}
}
}
pub fn set_sigint_handler(_handler: fn()) {}
@@ -45,6 +181,13 @@ 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))
+4
View File
@@ -41,6 +41,10 @@ path = "src/lib.rs"
name = "mount"
path = "src/mount.rs"
[[bin]]
name = "chroot"
path = "src/chroot.rs"
[[bin]]
name = "login"
path = "src/login.rs"
+23
View File
@@ -0,0 +1,23 @@
use std::process::ExitCode;
fn usage(cmd: &str) {
eprintln!("Usage: {cmd} PATH PROGRAM [ARGS...]");
}
fn main() -> ExitCode {
let mut args = std::env::args();
let cmd = args.next().unwrap();
let Some(path) = args.next() else {
usage(&cmd);
return ExitCode::FAILURE;
};
let Some(program) = args.next() else {
usage(&cmd);
return ExitCode::FAILURE;
};
let cmd_args = args.collect::<Vec<_>>();
println!("Run {program:?} at {path:?} with args {cmd_args:?}");
ExitCode::SUCCESS
}
-65
View File
@@ -1,68 +1,3 @@
#![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();
// }
+11 -1
View File
@@ -4,9 +4,19 @@ version = "0.1.0"
edition = "2021"
[dependencies]
runtime.workspace = true
cross.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
@@ -0,0 +1,175 @@
#[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}")
}
@@ -0,0 +1,123 @@
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(),
);
@@ -0,0 +1,67 @@
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}>")
}
}
@@ -0,0 +1,335 @@
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))
}
+51 -19
View File
@@ -1,36 +1,68 @@
#![feature(rustc_private, yggdrasil_os, let_chains)]
#![feature(rustc_private)]
use std::{
io,
process::{Command, ExitCode},
sync::atomic::{AtomicBool, Ordering},
};
use clap::Parser;
use tracer::CommandTracer;
use cross::{
process::{ChildTrace, CommandSpawnExt, TraceEvent},
signal,
};
pub mod format;
pub mod tracer;
use crate::definition::parse_syscall;
pub mod definition;
#[derive(Debug, Parser)]
struct Args {
command: String,
program: 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();
match run(&args) {
Ok(()) => ExitCode::SUCCESS,
Err(error) => {
eprintln!("{}: {error}", args.command);
ExitCode::FAILURE
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));
}
}
child.resume().ok();
}
ExitCode::SUCCESS
}
+1
View File
@@ -31,6 +31,7 @@ const PROGRAMS: &[(&str, &str)] = &[
// sysutils
("echo", "bin/echo"),
("mount", "sbin/mount"),
("chroot", "sbin/chroot"),
("login", "sbin/login"),
("strace", "bin/strace"),
("ls", "bin/ls"),