diff --git a/Cargo.lock b/Cargo.lock index 5e7f2918c2..89ed2b983e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1055,6 +1055,7 @@ dependencies = [ "bitflags 2.13.0", "const-str", "irq-framework", + "rdif-intc", "rdrive", "spin 0.12.0", ] diff --git a/components/irq-framework/src/action.rs b/components/irq-framework/src/action.rs index ec50c1171c..fba96d975a 100644 --- a/components/irq-framework/src/action.rs +++ b/components/irq-framework/src/action.rs @@ -4,7 +4,7 @@ use core::{ sync::atomic::AtomicBool, }; -use crate::{CpuId, CpuMask, IrqRequest, IrqScope, RawIrqHandler}; +use crate::{AutoEnable, CpuId, CpuMask, IrqRequest, IrqScope, RawIrqHandler}; pub(crate) struct Action { pub(crate) id: u64, @@ -29,7 +29,7 @@ impl Action { handler: request.handler, data: request.data, scope: request.scope, - enabled: AtomicBool::new(false), + enabled: AtomicBool::new(request.auto_enable == AutoEnable::Yes), detached: AtomicBool::new(false), pending_enable: UnsafeCell::new(CpuMask::empty()), next: ptr::null_mut(), diff --git a/components/irq-framework/src/registry.rs b/components/irq-framework/src/registry.rs index f72069f18e..48a8f1f9d5 100644 --- a/components/irq-framework/src/registry.rs +++ b/components/irq-framework/src/registry.rs @@ -6,8 +6,8 @@ use core::{ }; use crate::{ - AutoEnable, CpuId, IrqContext, IrqError, IrqHandle, IrqNumber, IrqOps, IrqOutcome, IrqRequest, - IrqReturn, IrqScope, IrqStatus, + CpuId, IrqContext, IrqError, IrqHandle, IrqNumber, IrqOps, IrqOutcome, IrqRequest, IrqReturn, + IrqScope, IrqStatus, action::Action, descriptor::{Descriptor, action_matches_cpu, recompute_scope_line_desired}, lock::MetadataLock, @@ -51,6 +51,7 @@ impl Registry { pub fn request(&self, irq: IrqNumber, request: IrqRequest) -> Result { self.validate_request(&request)?; + let snapshot = self.snapshot_and_disable_scope_line(irq, request.scope)?; let id = self.next_id.fetch_add(1, Ordering::Relaxed); let action = Box::new(Action::new(id, &request)); let action = Box::into_raw(action); @@ -58,17 +59,18 @@ impl Registry { let result = self.insert_action_locked(irq, &request, action); self.lock.unlock(&self.ops, irq_state); + let restore_result = self.restore_scope_line_snapshot(irq, request.scope, &snapshot); + if let Err(err) = result { unsafe { drop(Box::from_raw(action)); } + let _ = restore_result; return Err(err); } let handle = IrqHandle { irq, id }; - if request.auto_enable == AutoEnable::Yes - && let Err(err) = self.enable(handle) - { + if let Err(err) = restore_result { self.drop_detached_action(handle); return Err(err); } @@ -266,6 +268,7 @@ impl Registry { (*action).next = descriptor.head; } descriptor.head = action; + recompute_scope_line_desired(descriptor, request.scope); Ok(()) } @@ -409,6 +412,97 @@ impl Registry { } } + fn snapshot_and_disable_scope_line( + &self, + irq: IrqNumber, + scope: IrqScope, + ) -> Result { + let mut snapshot = LineStateSnapshot::new(scope); + match scope { + IrqScope::Global => { + snapshot.global = self.snapshot_and_disable_line(irq, None)?; + } + IrqScope::PerCpu { cpus } => { + for cpu in cpus.iter() { + if !self.ops.cpu_online(cpu) { + continue; + } + match self.snapshot_and_disable_line(irq, Some(cpu)) { + Ok(was_enabled) => snapshot.percpu.push((cpu, was_enabled)), + Err(err) => { + let _ = self.restore_scope_line_snapshot(irq, scope, &snapshot); + return Err(err); + } + } + } + } + } + Ok(snapshot) + } + + fn snapshot_and_disable_line( + &self, + irq: IrqNumber, + cpu: Option, + ) -> Result { + let was_enabled = self.controller_line_enabled(irq, cpu)?; + self.set_controller_enabled(irq, cpu, false)?; + self.set_line_applied_if_present(irq, cpu, false)?; + Ok(was_enabled) + } + + fn restore_scope_line_snapshot( + &self, + irq: IrqNumber, + scope: IrqScope, + snapshot: &LineStateSnapshot, + ) -> Result<(), IrqError> { + match scope { + IrqScope::Global => { + self.restore_line_snapshot(irq, None, snapshot.global)?; + } + IrqScope::PerCpu { cpus } => { + for cpu in cpus.iter() { + if let Some((_, was_enabled)) = snapshot + .percpu + .iter() + .find(|(snapshot_cpu, _)| *snapshot_cpu == cpu) + { + self.restore_line_snapshot(irq, Some(cpu), *was_enabled)?; + } + } + } + } + Ok(()) + } + + fn restore_line_snapshot( + &self, + irq: IrqNumber, + cpu: Option, + was_enabled: bool, + ) -> Result<(), IrqError> { + if was_enabled { + self.set_controller_enabled(irq, cpu, true)?; + } + self.set_line_applied_if_present(irq, cpu, was_enabled)?; + Ok(()) + } + + fn controller_line_enabled( + &self, + irq: IrqNumber, + cpu: Option, + ) -> Result { + match self.ops.is_enabled(irq, cpu) { + Ok(enabled) => Ok(enabled), + Err(IrqError::Unsupported) => { + Ok(self.framework_line_enabled(irq, cpu).unwrap_or(false)) + } + Err(err) => Err(err), + } + } + fn apply_line_state(&self, irq: IrqNumber, cpu: Option) -> Result<(), IrqError> { loop { if let Some(cpu) = cpu @@ -552,6 +646,28 @@ impl Registry { result } + fn set_line_applied_if_present( + &self, + irq: IrqNumber, + cpu: Option, + enabled: bool, + ) -> Result<(), IrqError> { + let irq_state = self.lock.lock(&self.ops); + let result = { + let state = unsafe { &mut *self.state.get() }; + if let Some(descriptor) = state + .descriptors + .iter_mut() + .find(|descriptor| descriptor.irq == irq) + { + descriptor.set_line_applied(cpu, enabled); + } + Ok(()) + }; + self.lock.unlock(&self.ops, irq_state); + result + } + fn framework_line_enabled(&self, irq: IrqNumber, cpu: Option) -> Result { let irq_state = self.lock.lock(&self.ops); let result = (|| { @@ -581,6 +697,23 @@ impl Registry { } } +struct LineStateSnapshot { + global: bool, + percpu: Vec<(CpuId, bool)>, +} + +impl LineStateSnapshot { + fn new(scope: IrqScope) -> Self { + Self { + global: false, + percpu: match scope { + IrqScope::Global => Vec::new(), + IrqScope::PerCpu { cpus } => Vec::with_capacity(cpus.iter().count()), + }, + } + } +} + struct DispatchGuard<'a, O: IrqOps> { registry: &'a Registry, irq: IrqNumber, diff --git a/components/irq-framework/tests/std_sim.rs b/components/irq-framework/tests/std_sim.rs index be2817d19a..da47e57db4 100644 --- a/components/irq-framework/tests/std_sim.rs +++ b/components/irq-framework/tests/std_sim.rs @@ -23,6 +23,7 @@ struct MockInner { in_irq: AtomicBool, unsupported_status: AtomicBool, online: Mutex>, + line_enabled: Mutex, bool)>>, calls: Mutex>, fail_set_enabled: Mutex, bool)>>, remote_calls: AtomicUsize, @@ -85,9 +86,37 @@ impl MockOps { .push((irq, cpu, enabled)); } + fn set_line_enabled(&self, irq: usize, cpu: Option, enabled: bool) { + let mut states = self.inner.line_enabled.lock().unwrap(); + if let Some((_, _, state)) = states + .iter_mut() + .find(|(entry_irq, entry_cpu, _)| *entry_irq == irq && *entry_cpu == cpu) + { + *state = enabled; + } else { + states.push((irq, cpu, enabled)); + } + } + fn calls(&self) -> Vec { self.inner.calls.lock().unwrap().clone() } + + fn clear_calls(&self) { + self.inner.calls.lock().unwrap().clear(); + } + + fn set_line_state_from_calls(&self, irq: usize, cpu: Option, enabled: bool) { + let mut states = self.inner.line_enabled.lock().unwrap(); + if let Some((_, _, state)) = states + .iter_mut() + .find(|(entry_irq, entry_cpu, _)| *entry_irq == irq && *entry_cpu == cpu) + { + *state = enabled; + } else { + states.push((irq, cpu, enabled)); + } + } } impl IrqOps for MockOps { @@ -147,6 +176,7 @@ impl IrqOps for MockOps { )) { return Err(IrqError::Controller); } + self.set_line_state_from_calls(irq.0, cpu.map(|cpu| cpu.0), enabled); Ok(()) } @@ -158,7 +188,17 @@ impl IrqOps for MockOps { if self.inner.unsupported_status.load(Ordering::SeqCst) { return Err(IrqError::Unsupported); } - Ok(true) + Ok(self + .inner + .line_enabled + .lock() + .unwrap() + .iter() + .find(|(entry_irq, entry_cpu, _)| { + *entry_irq == irq.0 && *entry_cpu == cpu.map(|cpu| cpu.0) + }) + .map(|(_, _, enabled)| *enabled) + .unwrap_or(true)) } fn is_pending(&self, irq: IrqNumber, cpu: Option) -> Result { @@ -188,6 +228,316 @@ impl IrqOps for MockOps { } } +#[test] +fn request_restores_enabled_line_without_hal_enable() { + let ops = MockOps::with_cpus(1); + let registry = Registry::new(ops.clone()); + let counter = AtomicUsize::new(0); + let data = NonNull::from(&counter).cast(); + + let handle = registry + .request(IrqNumber(30), IrqRequest::new(count_handler, data)) + .unwrap(); + + assert_eq!( + ops.calls(), + vec![ + OpCall::IsEnabled { irq: 30, cpu: None }, + OpCall::SetEnabled { + irq: 30, + cpu: None, + enabled: false, + }, + OpCall::SetEnabled { + irq: 30, + cpu: None, + enabled: true, + }, + ] + ); + ops.set_unsupported_status(true); + let status = registry.status(handle).unwrap(); + assert!(status.action_enabled); + assert!(status.line_enabled); + assert_eq!(registry.dispatch(IrqNumber(30), CpuId(0)).called, 1); +} + +#[test] +fn request_restores_disabled_line_without_hal_enable() { + let ops = MockOps::with_cpus(1); + ops.set_line_enabled(31, None, false); + let registry = Registry::new(ops.clone()); + let counter = AtomicUsize::new(0); + let data = NonNull::from(&counter).cast(); + + let handle = registry + .request(IrqNumber(31), IrqRequest::new(count_handler, data)) + .unwrap(); + + assert!(!ops.calls().contains(&OpCall::SetEnabled { + irq: 31, + cpu: None, + enabled: true, + })); + ops.set_unsupported_status(true); + let status = registry.status(handle).unwrap(); + assert!(status.action_enabled); + assert!(!status.line_enabled); + assert_eq!(registry.dispatch(IrqNumber(31), CpuId(0)).called, 1); +} + +#[test] +fn request_auto_enable_no_restores_line_but_keeps_action_disabled() { + let ops = MockOps::with_cpus(1); + let registry = Registry::new(ops.clone()); + let counter = AtomicUsize::new(0); + let data = NonNull::from(&counter).cast(); + + let handle = registry + .request( + IrqNumber(32), + IrqRequest::new(count_handler, data).auto_enable(AutoEnable::No), + ) + .unwrap(); + + assert_eq!( + ops.calls(), + vec![ + OpCall::IsEnabled { irq: 32, cpu: None }, + OpCall::SetEnabled { + irq: 32, + cpu: None, + enabled: false, + }, + OpCall::SetEnabled { + irq: 32, + cpu: None, + enabled: true, + }, + ] + ); + ops.set_unsupported_status(true); + let status = registry.status(handle).unwrap(); + assert!(!status.action_enabled); + assert!(status.line_enabled); + assert_eq!(registry.dispatch(IrqNumber(32), CpuId(0)).called, 0); +} + +#[test] +fn shared_request_temporarily_disables_existing_line_and_restores_it() { + let ops = MockOps::with_cpus(1); + let registry = Registry::new(ops.clone()); + let first = AtomicUsize::new(0); + let second = AtomicUsize::new(0); + + registry + .request( + IrqNumber(33), + IrqRequest::new(count_handler, NonNull::from(&first).cast()) + .share_mode(ShareMode::Shared), + ) + .unwrap(); + ops.clear_calls(); + + registry + .request( + IrqNumber(33), + IrqRequest::new(count_handler, NonNull::from(&second).cast()) + .share_mode(ShareMode::Shared), + ) + .unwrap(); + + assert_eq!( + ops.calls(), + vec![ + OpCall::IsEnabled { irq: 33, cpu: None }, + OpCall::SetEnabled { + irq: 33, + cpu: None, + enabled: false, + }, + OpCall::SetEnabled { + irq: 33, + cpu: None, + enabled: true, + }, + ] + ); + let outcome = registry.dispatch(IrqNumber(33), CpuId(0)); + assert!(outcome.handled); + assert_eq!(outcome.called, 2); +} + +#[test] +fn failed_request_restores_line_and_drops_new_action() { + let ops = MockOps::with_cpus(1); + let registry = Registry::new(ops.clone()); + let first = AtomicUsize::new(0); + let rejected = AtomicUsize::new(0); + + registry + .request( + IrqNumber(34), + IrqRequest::new(count_handler, NonNull::from(&first).cast()), + ) + .unwrap(); + ops.clear_calls(); + + let err = registry + .request( + IrqNumber(34), + IrqRequest::new(count_handler, NonNull::from(&rejected).cast()) + .share_mode(ShareMode::Shared), + ) + .unwrap_err(); + + assert_eq!(err, IrqError::Busy); + assert_eq!( + ops.calls(), + vec![ + OpCall::IsEnabled { irq: 34, cpu: None }, + OpCall::SetEnabled { + irq: 34, + cpu: None, + enabled: false, + }, + OpCall::SetEnabled { + irq: 34, + cpu: None, + enabled: true, + }, + ] + ); + assert_eq!(registry.dispatch(IrqNumber(34), CpuId(0)).called, 1); + assert_eq!(first.load(Ordering::SeqCst), 1); + assert_eq!(rejected.load(Ordering::SeqCst), 0); +} + +#[test] +fn failed_restore_after_request_drops_new_action() { + let ops = MockOps::with_cpus(1); + ops.fail_set_enabled(36, None, true); + let registry = Registry::new(ops.clone()); + let counter = AtomicUsize::new(0); + let data = NonNull::from(&counter).cast(); + + let err = registry + .request(IrqNumber(36), IrqRequest::new(count_handler, data)) + .unwrap_err(); + + assert_eq!(err, IrqError::Controller); + assert_eq!( + ops.calls(), + vec![ + OpCall::IsEnabled { irq: 36, cpu: None }, + OpCall::SetEnabled { + irq: 36, + cpu: None, + enabled: false, + }, + OpCall::SetEnabled { + irq: 36, + cpu: None, + enabled: true, + }, + ] + ); + assert_eq!(registry.dispatch(IrqNumber(36), CpuId(0)).called, 0); +} + +#[test] +fn failed_percpu_snapshot_restores_already_disabled_cpu_lines() { + let ops = MockOps::with_cpus(2); + ops.fail_set_enabled(37, Some(1), false); + let registry = Registry::new(ops.clone()); + let counter = AtomicUsize::new(0); + let data = NonNull::from(&counter).cast(); + let mut cpus = CpuMask::empty(); + cpus.insert(CpuId(0)); + cpus.insert(CpuId(1)); + + let err = registry + .request( + IrqNumber(37), + IrqRequest::new(count_handler, data).scope(IrqScope::PerCpu { cpus }), + ) + .unwrap_err(); + + assert_eq!(err, IrqError::Controller); + assert_eq!( + ops.calls(), + vec![ + OpCall::IsEnabled { + irq: 37, + cpu: Some(0), + }, + OpCall::SetEnabled { + irq: 37, + cpu: Some(0), + enabled: false, + }, + OpCall::IsEnabled { + irq: 37, + cpu: Some(1), + }, + OpCall::SetEnabled { + irq: 37, + cpu: Some(1), + enabled: false, + }, + OpCall::SetEnabled { + irq: 37, + cpu: Some(0), + enabled: true, + }, + ] + ); + assert_eq!(registry.dispatch(IrqNumber(37), CpuId(0)).called, 0); + assert_eq!(registry.dispatch(IrqNumber(37), CpuId(1)).called, 0); +} + +#[test] +fn percpu_request_temporarily_disables_and_restores_online_target_cpu_line() { + let ops = MockOps::with_cpus(4); + ops.set_current_cpu(0); + let registry = Registry::new(ops.clone()); + let counter = AtomicUsize::new(0); + let data = NonNull::from(&counter).cast(); + + registry + .request( + IrqNumber(35), + IrqRequest::new(count_handler, data) + .scope(IrqScope::PerCpu { + cpus: CpuMask::from_cpu(CpuId(2)), + }) + .auto_enable(AutoEnable::No), + ) + .unwrap(); + + assert_eq!( + ops.calls(), + vec![ + OpCall::IsEnabled { + irq: 35, + cpu: Some(2), + }, + OpCall::SetEnabled { + irq: 35, + cpu: Some(2), + enabled: false, + }, + OpCall::SetEnabled { + irq: 35, + cpu: Some(2), + enabled: true, + }, + ] + ); + assert_eq!(ops.inner.remote_calls.load(Ordering::SeqCst), 2); + assert_eq!(registry.dispatch(IrqNumber(35), CpuId(2)).called, 0); +} + unsafe fn count_handler(ctx: IrqContext, data: NonNull<()>) -> IrqReturn { assert!(ctx.irq.0 > 0); let counter = unsafe { data.cast::().as_ref() }; @@ -393,6 +743,7 @@ fn per_cpu_action_dispatches_only_on_matching_cpu() { fn remote_per_cpu_enable_uses_run_on_cpu_sync() { let ops = MockOps::with_cpus(4); ops.set_current_cpu(0); + ops.set_line_enabled(12, Some(2), false); let registry = Registry::new(ops.clone()); let counter = AtomicUsize::new(0); let data = NonNull::from(&counter).cast(); @@ -407,6 +758,8 @@ fn remote_per_cpu_enable_uses_run_on_cpu_sync() { .auto_enable(AutoEnable::No), ) .unwrap(); + ops.inner.remote_calls.store(0, Ordering::SeqCst); + ops.clear_calls(); registry.enable(handle).unwrap(); @@ -422,6 +775,7 @@ fn remote_per_cpu_enable_uses_run_on_cpu_sync() { fn failed_per_cpu_enable_rolls_back_action_state() { let ops = MockOps::with_cpus(4); ops.set_current_cpu(0); + ops.set_line_enabled(18, Some(2), false); ops.fail_set_enabled(18, Some(2), true); let registry = Registry::new(ops.clone()); let counter = AtomicUsize::new(0); @@ -650,12 +1004,16 @@ impl BlockingLineOps { inner: Arc::new(BlockingLineInner { false_entered: Barrier::new(2), false_release: Barrier::new(2), - block_false_once: AtomicBool::new(true), + block_false_once: AtomicBool::new(false), line_enabled: AtomicBool::new(false), calls: Mutex::new(Vec::new()), }), } } + + fn block_next_disable(&self) { + self.inner.block_false_once.store(true, Ordering::SeqCst); + } } impl IrqOps for BlockingLineOps { @@ -736,6 +1094,7 @@ fn stale_disable_does_not_override_concurrent_enable() { .share_mode(ShareMode::Shared), ) .unwrap(); + registry.enable(first).unwrap(); let second = registry .request( IrqNumber(21), @@ -745,6 +1104,7 @@ fn stale_disable_does_not_override_concurrent_enable() { ) .unwrap(); + ops.block_next_disable(); let disable_registry = registry.clone(); let disable_thread = thread::spawn(move || disable_registry.disable(first)); ops.inner.false_entered.wait(); @@ -777,6 +1137,7 @@ fn disabling_one_shared_action_keeps_line_enabled_until_last_action() { .share_mode(ShareMode::Shared), ) .unwrap(); + ops.clear_calls(); registry.disable(first).unwrap(); assert!(!ops.calls().contains(&OpCall::SetEnabled { diff --git a/docs/docs/architecture/rdrive-rdif.md b/docs/docs/architecture/rdrive-rdif.md index 2a01665ff5..4f7fe86f9a 100644 --- a/docs/docs/architecture/rdrive-rdif.md +++ b/docs/docs/architecture/rdrive-rdif.md @@ -115,7 +115,7 @@ pub enum ProbeKind { | --- | --- | --- | --- | --- | | `probe::static_` | `System { probed_names }` | `ProbeKind::Static` register name | `PlatformDevice` | 平台 crate 自己注册静态 model driver 并在回调中使用平台常量 | | `probe::fdt` | `System { fdt, phandle_map, probed }` | compatible + node status | `FdtInfo` + `PlatformDevice` | 保留当前 FDT 能力 | -| `probe::acpi` | `System { root, probed }` | HID/CID + ACPI device | `AcpiInfo` + `PlatformDevice` | API 存在,返回 unsupported | +| `probe::acpi` | `System { root, routing, pci, probed }` | HID/CID + ACPI device,或空 `ids` 的全局 table probe | `AcpiInfo` + `PlatformDevice` | ACPI source 初始化、MCFG/GSI controller routing、PCI `_PRT` 和普通设备 IRQ route | | `probe::pci` | PCIe controller enumerator | vendor/device/class | endpoint + `PlatformDevice` | 保留当前 PCIe 二阶段 probe | `probe_pre_kernel()` 只运行 `ProbeLevel::PreKernel`,并通过 backend 分发器执行 Static、FDT、ACPI 中的早期 probe。PCI endpoint 枚举依赖已注册的 PCIe controller,因此仍在普通 probe 阶段触发。 @@ -159,6 +159,63 @@ sequenceDiagram `ax-runtime` 不再拆 `AllDevices.block/net/display/input/vsock` 后逐个传给模块。它只触发 probe 和领域 service 初始化。 +## IRQ 解析与统一注册模型 + +修改后的 IRQ 路径把平台中断解析收敛到 `ax-driver` probe / 注册阶段。FDT、ACPI、PCI、manual/static 注册都会先得到一个 `BindingInfo`,再经 `register_*_with_info` 注册到 `rdrive`。`BindingInfo` 是纯注册载荷,只公开已经解析好的 `Option` IRQ number,不再携带 FDT/ACPI/PCI IRQ source 描述,也不直接执行平台解析副作用;解析和 interrupt-controller setup 由 `ax-driver` 的 binding resolver / PCI resolver 完成。`rdrive` 只把 ACPI/FDT probe metadata 交给 resolver,不保存平台 IRQ route/source 记录。 + +```mermaid +sequenceDiagram + autonumber + participant Probe as rdrive probe + participant Resolver as ax-driver binding resolver + participant Binding as BindingInfo payload + participant Pci as ax-driver pci resolver + participant Intc as rdif-intc device + participant Registry as rdrive typed registry + participant Runtime as ax-runtime / domain runtime + participant Hal as ax-hal irq + + alt FDT device + Probe->>Resolver: binding_info_from_fdt(FdtInfo) + Resolver->>Probe: read first interrupts() entry + Resolver->>Registry: get Intc by interrupt_parent phandle + Registry-->>Resolver: rdif_intc::Intc + Resolver->>Intc: setup_irq_by_fdt(specifier) + Intc-->>Resolver: irq number + else ACPI device + Probe->>Resolver: binding_info_from_acpi(AcpiInfo) + Resolver->>Probe: read first AcpiGsiRoute + Resolver->>Registry: get matching ACPI GSI Intc + Registry-->>Resolver: rdif_intc::Intc + Resolver->>Intc: setup_irq_by_acpi(route) + Intc-->>Resolver: irq number + else PCI endpoint + Probe->>Resolver: binding_info_from_pci(PciInfo, requirement) + Resolver->>Pci: resolve_intx_irq(PciInfo) + Pci->>Intc: setup_irq_by_acpi/_by_fdt(route) + Pci-->>Resolver: Option + else Manual / Static + Probe->>Binding: BindingInfo::empty() / with_irq(...) + end + + Resolver-->>Probe: BindingInfo(irq = Option usize) + Probe->>Registry: register_*_with_info(device, BindingInfo) + Registry-->>Probe: Option + Runtime->>Registry: get PlatformDevice + Registry-->>Runtime: device + irq_num() + Runtime->>Hal: request_shared_irq(irq, handler) +``` + +这个边界让平台解析和中断控制器 setup 留在 `ax-driver` 侧: + +- FDT 设备读取第一个 `interrupts()` 项,通过 `interrupt_parent` phandle 找到 `rdif-intc`,并在 probe 时调用 `setup_irq_by_fdt()`。 +- ACPI 设备从 `AcpiInfo` 读取首个 `AcpiGsiRoute`,通过能匹配该 route 的 `rdif-intc` 调用 `setup_irq_by_acpi()`,把 trigger/polarity/vector 等控制器参数配置好后只返回数字 IRQ。 +- PCI 设备先在枚举阶段计算 INTx swizzle route,再由 `ax-driver::pci::resolve_intx_irq()` 按 ACPI route、FDT `interrupt-map`、已注册 legacy route、`interrupt_line` fallback 的顺序解析;ACPI/FDT 命中后仍交给对应 `rdif-intc` 完成 setup。 +- 无中断的设备注册为 `None`;声明了中断但无法解析的 FDT 设备返回 probe error;PCI required IRQ 最终无结果时返回 probe error,optional IRQ 允许注册为 `None`。 +- `ax-runtime`、`ax-hal`、`ax-net-ng`、StarryOS usbfs 等上层只消费 `Option` / `usize`,不再处理 FDT/ACPI/PCI IRQ source。 + +网络 IRQ 的 runtime 适配也遵循同一方向。`ax-net-ng` 只暴露网络领域自己的 `EthernetIrqAction`、`EthernetIrqOutcome` 和注册错误类型,不再在公开 registrar trait 中泄漏 `ax-hal::irq::{RawIrqHandler, IrqContext, IrqReturn, IrqError}`。`ax-runtime` 持有 HAL IRQ registration,并把 HAL raw handler trampoline 适配到 `EthernetIrqAction`;因此网络 runtime 只描述“是否需要唤醒 poll 方”,HAL ABI 留在 ArceOS runtime 边界内。 + ## Capability Boundary `rdif-*` 是能力边界,只定义某类设备向上暴露什么能力,不负责设备发现、iomap、IRQ 注册、任务调度或系统启动顺序。块设备已移除原 runtime crate,`rdif-block` 直接承载设备 LBA 语义的 submit/poll block capability boundary;其它领域如网络仍可按需保留 runtime wrapper,负责 waker、poll、blocking API、buffer pool 等运行时行为。 @@ -174,7 +231,7 @@ sequenceDiagram `rdif-block` 的块请求不暴露 Linux block layer 的 512B sector 公共单位,而使用真实设备的 `lba` / `block_count` / `logical_block_size`。OS glue 负责把上层 byte offset、FS block、Linux-like sector 或分区 region 转换成设备 LBA。接口保留 blk-mq 风格的结构能力:设备可报告 `QueueTopology`,OS 可创建一个或多个 queue,每个 queue 使用 queue-local `RequestId`/tag,经 `submit_request()` 提交、经 `poll_request()` 回收完成。 -块 IRQ 事件按 source 和 queue 分离。`Interface::irq_sources()` 返回可用 IRQ source 列表,每个 `IrqSourceInfo { id, queues }` 描述该 source 可能影响的 queue mask;`take_irq_handler(source_id)` 只能取走对应 handler。单 INTx/legacy 设备使用 source `0`,未来 NVMe MSI-X 可以把不同 vector 暴露为不同 source。当前 ArceOS `ax-driver` / `ax-runtime` glue 仍只消费 legacy source `0`,保持一个 block 设备一个 IRQ handler 的注册方式。 +块设备内部的 IRQ 事件仍按 source 和 queue 分离。`Interface::irq_sources()` 返回的是 `rdif-block` 能力边界内的事件 source 列表,每个 `IrqSourceInfo { id, queues }` 描述该硬件事件 source 可能影响的 queue mask;它不是平台 FDT/PCI IRQ source,也不写入 `rdrive` 或 `BindingInfo`。当前 ArceOS `ax-driver` glue 只取 legacy source `0` 的 handler,并把它绑定到 `BindingInfo::irq_num()` 已解析出的数字 IRQ;`ax-runtime` 最终只看到 `(irq, handler)`。 `IrqHandler::handle_irq()` 只确认中断源并返回可 poll 的 queue mask,不做 OS wake、不阻塞、不持有 OS 锁,也不在中断上下文推进慢路径完成。收到事件后,runtime 或 task-side wrapper 再对相应 queue 调用 `poll_request()`。 @@ -234,7 +291,7 @@ IRQ 路径只返回稳定事件和唤醒等待方;不能在 IRQ handler 中执 | --- | --- | --- | --- | | Driver Core | `drivers//` | `no_std`、寄存器/队列/描述符、`mmio-api`、`dma-api` 小边界 | `ax-driver`、`ax-hal`、`axplat-dyn`、`rdrive::PlatformDevice` | | Capability Boundary | `drivers/interface/rdif-*` | `rdif-base`、小型错误和事件类型 | 平台、runtime、任务调度 | -| OS Glue | `platforms/axplat-dyn/src/drivers/*` 或平台 crate | `rdrive::module_driver!`、FDT/PCI/Static probe、iomap、IRQ 注册、DMA op | 上层 FS/NET 策略 | +| OS Glue | `drivers/ax-driver` 或平台 crate | `rdrive::module_driver!`、FDT/PCI/Static probe、iomap、IRQ setup、DMA op | 上层 FS/NET 策略 | | Runtime | `drivers/*/rd-*`,块设备除外 | `rdif-*`、waker、poll/blocking wrapper、buffer pool | probe、设备树、ACPI、平台选择 | Driver Core 只推进硬件状态机。OS Glue 将硬件实例包装成 `rdif-*::Interface` 后通过 `PlatformDevice::register(...)` 注册。除块设备外,Runtime wrapper 从 `rdif-*::Interface` 构建领域运行时对象,供服务层和上层模块使用;块设备服务直接基于 `rdif-block` 的 submit/poll 能力边界组织 volume 和文件系统入口。 @@ -333,7 +390,7 @@ src/ Phase 1: `rdrive` backend 分发 - 增加 `PlatformSource::{Static,Fdt,Acpi}` 和 `ProbeKind::{Static,Fdt,Acpi,Pci}`。 -- 新增 `probe::static_` 与 `probe::acpi` 模块;ACPI 第一版返回 unsupported error。 +- 新增 `probe::static_` 与 `probe::acpi` 模块;ACPI 初始化提供 MCFG、GSI controller routing、PCI `_PRT` 和普通设备 IRQ metadata。 - `probe_pre_kernel()` 和 `probe_all()` 改为 backend 分发,保留当前 FDT 与 PCI 能力。 - `Manager` 保持只管理 register 和 typed device registry。 diff --git a/drivers/ax-driver/Cargo.toml b/drivers/ax-driver/Cargo.toml index 0420c3b92d..2f3498e663 100644 --- a/drivers/ax-driver/Cargo.toml +++ b/drivers/ax-driver/Cargo.toml @@ -16,6 +16,7 @@ default = [] plat-static = [] plat-dyn = ["dep:fdt-edit"] irq = [] +pci = ["dep:pcie", "dep:rdif-pcie"] block = ["dep:ax-errno", "dep:ax-kspin", "dep:rdif-block"] net = ["dep:rd-net"] @@ -25,26 +26,26 @@ vsock = ["dep:ax-errno", "dep:rdif-vsock"] virtio-core = ["dep:ax-alloc", "dep:virtio-drivers", "virtio-drivers/alloc"] virtio = ["virtio-core"] -virtio-blk = ["block", "virtio"] -virtio-net = ["net", "virtio", "dep:ax-kernel-guard"] -virtio-gpu = ["display", "virtio"] -virtio-input = ["input", "virtio"] -virtio-socket = ["vsock", "virtio"] +virtio-blk = ["block", "virtio", "pci"] +virtio-net = ["net", "virtio", "pci", "dep:ax-kernel-guard"] +virtio-gpu = ["display", "virtio", "pci"] +virtio-input = ["input", "virtio", "pci"] +virtio-socket = ["vsock", "virtio", "pci"] ramdisk = ["block", "dep:ramdisk"] bcm2835-sdhci = ["block", "dep:bcm2835-sdhci"] -ahci = ["block", "dep:simple-ahci"] -nvme = ["block", "dep:nvme-driver"] +ahci = ["block", "pci", "dep:simple-ahci"] +nvme = ["block", "pci", "dep:nvme-driver"] cvsd = ["block", "plat-dyn", "dep:sg200x-bsp"] -ixgbe = ["net", "dep:ax-alloc", "dep:ixgbe-driver"] +ixgbe = ["net", "pci", "dep:ax-alloc", "dep:ixgbe-driver"] fxmac = ["net", "dep:ax-alloc", "dep:ax-crate-interface", "dep:fxmac_rs"] -intel-net = ["net", "dep:eth-intel"] -realtek-rtl8125 = ["net", "dep:realtek-rtl8125"] +intel-net = ["net", "pci", "dep:eth-intel"] +realtek-rtl8125 = ["net", "pci", "dep:realtek-rtl8125"] usb = ["dep:crab-usb"] rockchip-dwc-xhci = ["usb", "plat-dyn", "rockchip-soc", "rockchip-pm"] xhci-mmio = ["usb", "plat-dyn"] -xhci-pci = ["usb"] +xhci-pci = ["usb", "pci"] serial = ["plat-dyn", "dep:some-serial"] sg2002-placeholder = ["plat-dyn"] rknpu = ["plat-dyn", "rockchip-pm", "rockchip-soc", "dep:rockchip-npu"] @@ -85,6 +86,7 @@ rockchip-dwmmc = [ "sdmmc-protocol/sdio", ] rk3588-pcie = [ + "pci", "plat-dyn", "rockchip-soc", "rockchip-pm", @@ -92,7 +94,7 @@ rk3588-pcie = [ ] rockchip-soc = ["plat-dyn", "dep:rdif-clk", "dep:rockchip-soc"] rockchip-pm = ["rockchip-soc", "dep:rockchip-pm"] -list-pci-devices = [] +list-pci-devices = ["pci"] pci-list-devices = ["list-pci-devices"] [dependencies] @@ -116,7 +118,7 @@ ixgbe-driver = { workspace = true, optional = true } log.workspace = true mmio-api.workspace = true nvme-driver = { workspace = true, optional = true } -pcie.workspace = true +pcie = { workspace = true, optional = true } ramdisk = { workspace = true, optional = true } rd-net = { workspace = true, optional = true } rdif-block = { workspace = true, optional = true } @@ -124,7 +126,7 @@ rdif-clk = { workspace = true, optional = true } rdif-display = { workspace = true, optional = true } rdif-input = { workspace = true, optional = true } rdif-intc.workspace = true -rdif-pcie.workspace = true +rdif-pcie = { workspace = true, optional = true } rdif-vsock = { workspace = true, optional = true } realtek-rtl8125 = { workspace = true, optional = true } rdrive.workspace = true diff --git a/drivers/ax-driver/build.rs b/drivers/ax-driver/build.rs index 72c5c9924b..897e8664f8 100644 --- a/drivers/ax-driver/build.rs +++ b/drivers/ax-driver/build.rs @@ -6,17 +6,6 @@ const VIRTIO_DEV_FEATURES: &[&str] = &[ "virtio-socket", ]; -const PCI_DYN_INTX_ROUTE_FEATURES: &[&str] = &[ - "intel-net", - "ixgbe", - "realtek-rtl8125", - "virtio-net", - "xhci-pci", -]; - -const PCI_DYN_ACPI_INTX_ROUTE_FEATURES: &[&str] = - &["intel-net", "ixgbe", "realtek-rtl8125", "virtio-net"]; - fn has_feature(feature: &str) -> bool { std::env::var(format!( "CARGO_FEATURE_{}", @@ -49,12 +38,6 @@ fn main() { if has_virtio_core || has_virtio_dev { enable_cfg_flag("virtio_dev"); } - if has_plat_dyn && has_any_feature(PCI_DYN_INTX_ROUTE_FEATURES) { - enable_cfg_flag("pci_dyn_intx_route"); - } - if has_plat_dyn && has_any_feature(PCI_DYN_ACPI_INTX_ROUTE_FEATURES) { - enable_cfg_flag("pci_dyn_acpi_intx_route"); - } if has_any_feature(&["ahci", "bcm2835-sdhci"]) || (has_feature("cvsd") && target_has_cvsd) { enable_cfg_flag("sync_block_dev"); } @@ -62,7 +45,5 @@ fn main() { println!("cargo::rustc-check-cfg=cfg(plat_static)"); println!("cargo::rustc-check-cfg=cfg(plat_dyn)"); println!("cargo::rustc-check-cfg=cfg(virtio_dev)"); - println!("cargo::rustc-check-cfg=cfg(pci_dyn_intx_route)"); - println!("cargo::rustc-check-cfg=cfg(pci_dyn_acpi_intx_route)"); println!("cargo::rustc-check-cfg=cfg(sync_block_dev)"); } diff --git a/drivers/ax-driver/src/binding_info.rs b/drivers/ax-driver/src/binding_info.rs new file mode 100644 index 0000000000..6a227cc036 --- /dev/null +++ b/drivers/ax-driver/src/binding_info.rs @@ -0,0 +1,25 @@ +#[derive(Clone, Debug, Default)] +pub struct BindingInfo { + irq: Option, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[cfg(feature = "pci")] +pub enum PciIrqRequirement { + Optional, + Required, +} + +impl BindingInfo { + pub const fn empty() -> Self { + Self { irq: None } + } + + pub const fn with_irq(irq: Option) -> Self { + Self { irq } + } + + pub fn irq_num(&self) -> Option { + self.irq + } +} diff --git a/drivers/ax-driver/src/binding_resolver.rs b/drivers/ax-driver/src/binding_resolver.rs new file mode 100644 index 0000000000..3975b45438 --- /dev/null +++ b/drivers/ax-driver/src/binding_resolver.rs @@ -0,0 +1,104 @@ +use alloc::format; + +use rdrive::{ + probe::{OnProbeError, acpi::AcpiInfo}, + register::FdtInfo, +}; + +use crate::BindingInfo; + +pub fn binding_info_from_fdt(info: &FdtInfo<'_>) -> Result { + Ok(BindingInfo::with_irq(resolve_fdt_irq(info)?)) +} + +pub fn binding_info_from_acpi(info: &AcpiInfo<'_>) -> Result { + Ok(BindingInfo::with_irq(resolve_acpi_irq(info)?)) +} + +pub fn binding_info_from_acpi_route( + path: &str, + route: Option, +) -> Result { + Ok(BindingInfo::with_irq(match route { + Some(route) => Some(setup_acpi_irq(path, &route)?), + None => None, + })) +} + +fn resolve_fdt_irq(info: &FdtInfo<'_>) -> Result, OnProbeError> { + let Some(interrupt) = info.interrupts().into_iter().next() else { + return Ok(None); + }; + + let interrupt_parent = info + .phandle_to_device_id(interrupt.interrupt_parent) + .ok_or_else(|| { + OnProbeError::other(format!( + "failed to resolve interrupt parent {:?} for {}", + interrupt.interrupt_parent, + info.node.path() + )) + })?; + let intc = rdrive::get::(interrupt_parent).map_err(|err| { + OnProbeError::other(format!( + "failed to get interrupt controller {:?} for {}: {err:?}", + interrupt_parent, + info.node.path() + )) + })?; + let mut intc = intc.lock().map_err(|err| { + OnProbeError::other(format!( + "failed to lock interrupt controller {:?} for {}: {err:?}", + interrupt_parent, + info.node.path() + )) + })?; + Ok(Some(intc.setup_irq_by_fdt(&interrupt.specifier).into())) +} + +fn resolve_acpi_irq(info: &AcpiInfo<'_>) -> Result, OnProbeError> { + let Some(route) = info.irq_route() else { + return Ok(None); + }; + setup_acpi_irq(info.path, &route).map(Some) +} + +fn setup_acpi_irq( + path: &str, + route: &rdrive::probe::acpi::AcpiGsiRoute, +) -> Result { + let intc = rdrive::get_list::() + .into_iter() + .find(|intc| { + intc.try_lock() + .map(|intc| intc.supports_acpi_gsi(route)) + .unwrap_or(false) + }) + .ok_or_else(|| { + OnProbeError::other(format!( + "ACPI interrupt controller for route {:?} is not registered for {path}", + route.controller + )) + })?; + let mut intc = intc.lock().map_err(|err| { + OnProbeError::other(format!( + "failed to lock ACPI interrupt controller for {path}: {err:?}" + )) + })?; + Ok(intc.setup_irq_by_acpi(route).into()) +} + +#[cfg(feature = "pci")] +pub fn binding_info_from_pci( + info: rdrive::probe::pci::PciInfo, + requirement: crate::PciIrqRequirement, +) -> Result { + let irq = crate::pci::resolve_intx_irq(info)?; + if irq.is_none() && requirement == crate::PciIrqRequirement::Required { + return Err(OnProbeError::other(format!( + "failed to resolve IRQ for PCI endpoint {}", + info.address + ))); + } + Ok(BindingInfo::with_irq(irq)) +} diff --git a/drivers/ax-driver/src/block/ahci.rs b/drivers/ax-driver/src/block/ahci.rs index 00e621b688..f051ef06a6 100644 --- a/drivers/ax-driver/src/block/ahci.rs +++ b/drivers/ax-driver/src/block/ahci.rs @@ -3,12 +3,9 @@ extern crate alloc; use alloc::format; use pcie::CommandRegister; -use rdrive::{ - PlatformDevice, - probe::{ - OnProbeError, - pci::{EndpointRc, FnOnProbe}, - }, +use rdrive::probe::{ + OnProbeError, + pci::{FnOnProbe, ProbePci}, }; use simple_ahci::{AhciDriver as SimpleAhciDriver, Hal as AhciHal}; @@ -39,7 +36,8 @@ impl AhciHal for AxAhciHal { fn flush_dcache() {} } -fn probe_pci(endpoint: &mut EndpointRc, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe_pci(mut probe: ProbePci<'_>) -> Result<(), OnProbeError> { + let endpoint = probe.endpoint_mut(); let class = endpoint.revision_and_class(); if (class.base_class, class.sub_class) != (0x01, 0x06) { return Err(OnProbeError::NotMatch); @@ -60,7 +58,7 @@ fn probe_pci(endpoint: &mut EndpointRc, plat_dev: PlatformDevice) -> Result<(), else { return Err(OnProbeError::other("failed to initialize AHCI controller")); }; - register_sync_block(plat_dev, AhciBlock(driver)); + register_sync_block(probe.into_platform_device(), AhciBlock(driver)); Ok(()) } diff --git a/drivers/ax-driver/src/block/binding.rs b/drivers/ax-driver/src/block/binding.rs index eda2ba5e41..712bc2243a 100644 --- a/drivers/ax-driver/src/block/binding.rs +++ b/drivers/ax-driver/src/block/binding.rs @@ -1,9 +1,13 @@ +#[cfg(feature = "irq")] +use alloc::sync::Arc; use alloc::{ boxed::Box, string::{String, ToString}, vec::Vec, }; use core::alloc::Layout; +#[cfg(feature = "irq")] +use core::sync::atomic::{AtomicU64, Ordering}; use ax_errno::{AxError, AxResult}; use ax_kspin::SpinNoIrq; @@ -13,11 +17,18 @@ use rdif_block::{ BlkError, Buffer, IQueue, Interface, QueueInfo, Request, RequestFlags, RequestId, RequestOp, RequestStatus, TransferChunk, TransferPlan, TransferPlanner, TransferRuntimeCaps, }; -use rdrive::Device; +use rdrive::{Device, probe::OnProbeError}; + +use crate::{ + BindingInfo, binding_info_from_acpi, binding_info_from_fdt, + registration::{BoundDevice, register_bound_device}, +}; +#[cfg(feature = "pci")] +use crate::{PciIrqRequirement, binding_info_from_pci}; pub struct Block { name: String, - irq_num: Option, + info: BindingInfo, irq_enabled: bool, #[cfg(feature = "irq")] irq_handler: Option, @@ -28,6 +39,8 @@ pub struct Block { struct BlockQueues { queue: Box, pool: BlockBufferPool, + #[cfg(feature = "irq")] + irq_events: Arc, } struct BlockBufferPool { @@ -40,17 +53,17 @@ struct BlockBufferPool { pub struct PlatformBlockDevice { name: String, interface: Option>, - irq_num: Option, + info: BindingInfo, } const MAX_BLOCK_BUFFER_SIZE: usize = 16 * 1024; impl PlatformBlockDevice { - fn new(name: String, interface: Box, irq_num: Option) -> Self { + fn new(name: String, interface: Box, info: BindingInfo) -> Self { Self { name, interface: Some(interface), - irq_num, + info, } } } @@ -61,32 +74,81 @@ impl rdrive::DriverGeneric for PlatformBlockDevice { } } +impl BoundDevice for PlatformBlockDevice { + fn binding_info(&self) -> &BindingInfo { + &self.info + } +} + #[cfg(feature = "irq")] pub struct BlockIrqHandler { handler: Box, + events: Arc, } #[cfg(feature = "irq")] impl BlockIrqHandler { - fn new(handler: Box) -> Self { - Self { handler } + fn new(handler: Box, events: Arc) -> Self { + Self { handler, events } } pub fn handle(&self) -> rdif_block::Event { - self.handler.handle_irq() + let event = self.handler.handle_irq(); + self.events.record(event); + event } } #[cfg(not(feature = "irq"))] pub struct BlockIrqHandler; +#[cfg(feature = "irq")] +#[derive(Default)] +struct BlockIrqEvents { + queues: AtomicU64, +} + +#[cfg(feature = "irq")] +impl BlockIrqEvents { + fn record(&self, event: rdif_block::Event) { + let queues = event.queues.bits(); + if queues != 0 { + self.queues.fetch_or(queues, Ordering::Release); + } + } + + fn take_queue(&self, id: usize) -> bool { + if id >= u64::BITS as usize { + return false; + } + let mask = 1_u64 << id; + let mut current = self.queues.load(Ordering::Acquire); + while current & mask != 0 { + match self.queues.compare_exchange_weak( + current, + current & !mask, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => return true, + Err(next) => current = next, + } + } + false + } +} + impl Block { pub fn name(&self) -> &str { &self.name } - pub const fn irq_num(&self) -> Option { - self.irq_num + pub fn irq_num(&self) -> Option { + self.info.irq_num() + } + + pub fn binding_info(&self) -> &BindingInfo { + &self.info } pub fn enable_irq(&mut self) { @@ -105,9 +167,9 @@ impl Block { #[cfg(feature = "irq")] pub fn take_irq_handler(&mut self) -> Option<(usize, BlockIrqHandler)> { - let irq_num = self.irq_num.take()?; + let irq = self.info.irq_num()?; let handler = self.irq_handler.take()?; - Some((irq_num, handler)) + Some((irq, handler)) } #[cfg(not(feature = "irq"))] @@ -199,7 +261,10 @@ impl Block { } impl BlockQueues { - fn new(queue: Box) -> AxResult { + fn new( + queue: Box, + #[cfg(feature = "irq")] irq_events: Arc, + ) -> AxResult { let info = queue.info(); let block_size = info.device.logical_block_size; if block_size == 0 { @@ -215,6 +280,8 @@ impl BlockQueues { size: layout.size(), align: layout.align(), }, + #[cfg(feature = "irq")] + irq_events, }) } @@ -225,8 +292,18 @@ impl BlockQueues { .poll_request(request) .map_err(map_blk_err_to_ax_err)? { - RequestStatus::Complete => return Ok(()), - RequestStatus::Pending => core::hint::spin_loop(), + RequestStatus::Complete => { + #[cfg(feature = "irq")] + let _ = self.irq_events.take_queue(self.queue.id()); + return Ok(()); + } + RequestStatus::Pending => { + #[cfg(feature = "irq")] + if self.irq_events.take_queue(self.queue.id()) { + continue; + } + core::hint::spin_loop(); + } } } } @@ -247,30 +324,42 @@ impl TryFrom> for Block { fn try_from(base: Device) -> Result { let mut dev = base.lock().map_err(|_| AxError::BadState)?; let name = dev.name.clone(); - let irq_num = dev.irq_num; + let info = dev.info.clone(); + let irq = info.irq_num(); let mut interface = dev.interface.take().ok_or(AxError::BadState)?; let queue = interface.create_queue().ok_or(AxError::BadState)?; - let queues = BlockQueues::new(queue)?; + #[cfg(feature = "irq")] + let irq_events = Arc::new(BlockIrqEvents::default()); + let queues = BlockQueues::new( + queue, + #[cfg(feature = "irq")] + Arc::clone(&irq_events), + )?; #[cfg(feature = "irq")] - let irq_handler = irq_num + let irq_handler = irq + .as_ref() .and_then(|_| take_legacy_irq_handler(interface.as_mut())) - .map(BlockIrqHandler::new); + .map(|handler| BlockIrqHandler::new(handler, irq_events)); drop(dev); #[cfg(feature = "irq")] - let irq_num = if irq_handler.is_some() { irq_num } else { None }; + let info = if irq_handler.is_some() { + info + } else { + BindingInfo::empty() + }; #[cfg(feature = "irq")] let irq_handler = irq_handler; #[cfg(not(feature = "irq"))] - let irq_num = { - let _ = irq_num; - None + let info = { + let _ = irq; + BindingInfo::empty() }; Ok(Self { name, - irq_num, + info, irq_enabled: interface.is_irq_enabled(), #[cfg(feature = "irq")] irq_handler, @@ -281,38 +370,87 @@ impl TryFrom> for Block { } pub trait PlatformDeviceBlock { - fn register_block(self, dev: T); - fn register_block_with_irq(self, dev: T, irq_num: Option); + fn register_block(self, dev: T) -> Option; + fn register_block_with_info(self, dev: T, info: BindingInfo) -> Option; } impl PlatformDeviceBlock for rdrive::PlatformDevice { - fn register_block(self, dev: T) { - self.register_block_with_irq(dev, None); + fn register_block(self, dev: T) -> Option { + self.register_block_with_info(dev, BindingInfo::empty()) } - fn register_block_with_irq(self, dev: T, irq_num: Option) { - let name = dev.name().to_string(); - self.register(PlatformBlockDevice::new(name, Box::new(dev), irq_num)); + fn register_block_with_info(self, dev: T, info: BindingInfo) -> Option { + register_block_with_info(self, dev, info) } } -pub fn decode_fdt_irq(interrupts: &[rdrive::probe::fdt::InterruptRef]) -> Option { - let interrupt = interrupts.first()?; - decode_irq_cells(&interrupt.specifier) +pub trait ProbeFdtBlock { + fn register_block(self, dev: T) -> Result, OnProbeError>; } -fn decode_irq_cells(specifier: &[u32]) -> Option { - match specifier { - [irq] => Some(*irq as usize), - [kind, irq, ..] => match *kind { - 0 => Some(*irq as usize + 32), - 1 => Some(*irq as usize + 16), - _ => Some(*irq as usize), - }, - _ => None, +impl ProbeFdtBlock for rdrive::probe::fdt::ProbeFdt<'_> { + fn register_block(self, dev: T) -> Result, OnProbeError> { + let info = binding_info_from_fdt(self.info())?; + Ok(register_block_with_info( + self.into_platform_device(), + dev, + info, + )) + } +} + +pub trait ProbeAcpiBlock { + fn register_block(self, dev: T) -> Result, OnProbeError>; +} + +impl ProbeAcpiBlock for rdrive::probe::acpi::ProbeAcpi<'_> { + fn register_block(self, dev: T) -> Result, OnProbeError> { + let info = binding_info_from_acpi(self.info())?; + Ok(register_block_with_info( + self.into_platform_device(), + dev, + info, + )) + } +} + +#[cfg(feature = "pci")] +pub trait ProbePciBlock { + fn register_block( + self, + dev: T, + requirement: PciIrqRequirement, + ) -> Result, OnProbeError>; +} + +#[cfg(feature = "pci")] +impl ProbePciBlock for rdrive::probe::pci::ProbePci<'_> { + fn register_block( + self, + dev: T, + requirement: PciIrqRequirement, + ) -> Result, OnProbeError> { + let info = binding_info_from_pci(self.info(), requirement)?; + Ok(register_block_with_info( + self.into_platform_device(), + dev, + info, + )) } } +fn register_block_with_info( + plat_dev: rdrive::PlatformDevice, + dev: T, + info: BindingInfo, +) -> Option { + let name = dev.name().to_string(); + register_bound_device( + plat_dev, + PlatformBlockDevice::new(name, Box::new(dev), info), + ) +} + pub fn take_block_devices() -> Vec { rdrive::get_list::() .into_iter() @@ -585,6 +723,19 @@ mod tests { } } + #[test] + fn platform_block_device_exposes_binding_info_irq_num() { + let irq = 47; + let device = PlatformBlockDevice::new( + "test-block".into(), + Box::new(TestInterface), + BindingInfo::with_irq(Some(irq)), + ); + + assert_eq!(BoundDevice::binding_info(&device).irq_num(), Some(irq)); + assert_eq!(BoundDevice::irq_num(&device), Some(irq)); + } + struct TestQueue { dma: &'static TrackingDma, } @@ -680,7 +831,7 @@ mod tests { let layout = block_buffer_layout(info, planner.chunk_size()).unwrap(); Block { name: String::from("test-block"), - irq_num: None, + info: BindingInfo::empty(), irq_enabled: false, #[cfg(feature = "irq")] irq_handler: None, @@ -693,6 +844,8 @@ mod tests { size: layout.size(), align: layout.align(), }, + #[cfg(feature = "irq")] + irq_events: Arc::new(BlockIrqEvents::default()), }), } } diff --git a/drivers/ax-driver/src/block/cvsd.rs b/drivers/ax-driver/src/block/cvsd.rs index 082821bb29..8a1f94647e 100644 --- a/drivers/ax-driver/src/block/cvsd.rs +++ b/drivers/ax-driver/src/block/cvsd.rs @@ -1,5 +1,5 @@ #[cfg(plat_dyn)] -use rdrive::register::FdtInfo; +use rdrive::register::ProbeFdt; use rdrive::{PlatformDevice, probe::OnProbeError}; #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] use {alloc::format, sg200x_bsp::sdmmc::Sdmmc}; @@ -23,7 +23,8 @@ crate::model_register!( ); #[cfg(plat_dyn)] -fn probe_fdt(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe_fdt(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (info, plat_dev) = probe.into_parts(); let sdmmc = info.node.regs().into_iter().next().ok_or_else(|| { OnProbeError::other(alloc::format!("[{}] has no reg", info.node.name())) diff --git a/drivers/ax-driver/src/block/k230_sdhci.rs b/drivers/ax-driver/src/block/k230_sdhci.rs index 306d9f829c..f0617ee198 100644 --- a/drivers/ax-driver/src/block/k230_sdhci.rs +++ b/drivers/ax-driver/src/block/k230_sdhci.rs @@ -22,7 +22,11 @@ use core::{ use dma_api::DeviceDma; use log::{info, warn}; -use rdrive::{DriverGeneric, PlatformDevice, probe::OnProbeError, register::FdtInfo}; +use rdrive::{ + DriverGeneric, + probe::OnProbeError, + register::{FdtInfo, ProbeFdt}, +}; use sdhci_host::{BlockRequest, BlockRequestSlot, RequestId, Sdhci}; use sdmmc_protocol::{ BlockPoll, BlockTransferMode, Error, OperationPoll, @@ -31,7 +35,7 @@ use sdmmc_protocol::{ }; use crate::{ - block::{PlatformDeviceBlock, SharedDriver, decode_fdt_irq}, + block::{ProbeFdtBlock, SharedDriver}, mmio::iomap, }; @@ -52,7 +56,8 @@ crate::model_register!( ], ); -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let info = probe.info(); let base_reg = info .node .regs() @@ -82,7 +87,7 @@ fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError info!("k230-sdhci: initialize card"); let mut card = SdioSdmmc::new(host); - let card_info = poll_card_init(&mut card, card_init_preference(&info)) + let card_info = poll_card_init(&mut card, card_init_preference(info)) .map_err(|e| card_init_error(base_reg.address, mmio_size, e))?; info!( "SDHCI card: kind={:?} high_capacity={} rca={} ocr={:#010x} capacity_blocks={:?} cid={} \ @@ -96,7 +101,6 @@ fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError card_info.ext_csd.is_some() ); - let irq_num = decode_fdt_irq(&info.interrupts()); let raw = SharedDriver::new(card); let dev = BlockDevice { raw: Some(raw.clone()), @@ -105,8 +109,8 @@ fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError queue_created: false, irq_handler_taken: false, }; - plat_dev.register_block_with_irq(dev, irq_num); - info!("k230-sdhci block device registered irq={:?}", irq_num); + let irq = probe.register_block(dev)?; + info!("k230-sdhci block device registered irq={:?}", irq); Ok(()) } diff --git a/drivers/ax-driver/src/block/mod.rs b/drivers/ax-driver/src/block/mod.rs index ca029856a5..0e10d0bcf2 100644 --- a/drivers/ax-driver/src/block/mod.rs +++ b/drivers/ax-driver/src/block/mod.rs @@ -1,11 +1,6 @@ mod binding; -#[cfg(any( - feature = "virtio-blk", - feature = "k230-sdhci", - feature = "phytium-mci", - feature = "rockchip-dwmmc", - feature = "rockchip-sdhci" -))] + +#[allow(unused)] mod shared; #[cfg(feature = "ahci")] @@ -38,13 +33,7 @@ use rdif_block::{ BlkError, DeviceInfo, DriverGeneric, IQueue, Interface, QueueInfo, QueueLimits, Request, RequestId, RequestOp, RequestStatus, validate_request, }; -#[cfg(any( - feature = "virtio-blk", - feature = "k230-sdhci", - feature = "phytium-mci", - feature = "rockchip-dwmmc", - feature = "rockchip-sdhci" -))] +#[allow(unused)] pub(crate) use shared::SharedDriver; #[cfg(sync_block_dev)] use spin::Mutex; diff --git a/drivers/ax-driver/src/block/nvme.rs b/drivers/ax-driver/src/block/nvme.rs index a3961a7e9d..6d0b0ed622 100644 --- a/drivers/ax-driver/src/block/nvme.rs +++ b/drivers/ax-driver/src/block/nvme.rs @@ -5,15 +5,12 @@ use alloc::format; use log::info; use nvme_driver::{Config, Nvme, NvmeBlockDriver}; use pcie::{CommandRegister, DeviceType}; -use rdrive::{ - PlatformDevice, - probe::{ - OnProbeError, - pci::{EndpointRc, FnOnProbe}, - }, +use rdrive::probe::{ + OnProbeError, + pci::{FnOnProbe, ProbePci}, }; -use super::PlatformDeviceBlock; +use crate::{PciIrqRequirement, block::ProbePciBlock}; pub const DEVICE_NAME: &str = "nvme"; const DEFAULT_PAGE_SIZE: usize = 0x1000; @@ -28,7 +25,8 @@ crate::model_register!( }], ); -fn probe_pci(endpoint: &mut EndpointRc, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe_pci(mut probe: ProbePci<'_>) -> Result<(), OnProbeError> { + let endpoint = probe.endpoint_mut(); if endpoint.device_type() != DeviceType::NvmeController { return Err(OnProbeError::NotMatch); } @@ -44,12 +42,10 @@ fn probe_pci(endpoint: &mut EndpointRc, plat_dev: PlatformDevice) -> Result<(), }); let address = endpoint.address(); - let irq = crate::pci::endpoint_legacy_irq(endpoint); info!( - "NVMe PCI endpoint {address}: BAR0={:#x}..{:#x}, irq={:?}, int_pin={}, int_line={}", + "NVMe PCI endpoint {address}: BAR0={:#x}..{:#x}, int_pin={}, int_line={}", bar.start, bar.end, - irq, endpoint.interrupt_pin(), endpoint.interrupt_line() ); @@ -69,6 +65,7 @@ fn probe_pci(endpoint: &mut EndpointRc, plat_dev: PlatformDevice) -> Result<(), let driver = NvmeBlockDriver::from_nvme(nvme).map_err(|err| { OnProbeError::other(format!("failed to create NVMe block driver: {err:?}")) })?; - plat_dev.register_block_with_irq(driver, irq); + let irq = probe.register_block(driver, PciIrqRequirement::Optional)?; + info!("NVMe block device registered at {address} with irq {irq:?}"); Ok(()) } diff --git a/drivers/ax-driver/src/block/phytium_mci.rs b/drivers/ax-driver/src/block/phytium_mci.rs index 921ec7d768..c2d3a523c2 100644 --- a/drivers/ax-driver/src/block/phytium_mci.rs +++ b/drivers/ax-driver/src/block/phytium_mci.rs @@ -9,7 +9,11 @@ use core::{ use dma_api::DeviceDma; use log::{info, warn}; use phytium_mci_host::{BlockRequest, BlockRequestSlot, PhytiumMci, RequestId}; -use rdrive::{DriverGeneric, PlatformDevice, probe::OnProbeError, register::FdtInfo}; +use rdrive::{ + DriverGeneric, + probe::OnProbeError, + register::{FdtInfo, ProbeFdt}, +}; use sdmmc_protocol::{ BlockPoll, BlockTransferMode, Error, OperationPoll, error::Phase, @@ -17,7 +21,7 @@ use sdmmc_protocol::{ }; use crate::{ - block::{PlatformDeviceBlock, SharedDriver, decode_fdt_irq}, + block::{ProbeFdtBlock, SharedDriver}, mmio::iomap, }; @@ -37,7 +41,8 @@ crate::model_register!( ], ); -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let info = probe.info(); let base_reg = info .node .regs() @@ -65,7 +70,7 @@ fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError info!("phytium-mci: initialize card"); let mut card = SdioSdmmc::new(host); card.set_sd_uhs_selection_enabled(false); - let preference = card_init_preference(&info); + let preference = card_init_preference(info); let card_info = poll_card_init(&mut card, preference).map_err(|e| { warn!("phytium-mci: card init failed: {:?}", e); card_init_error(base_reg.address, mmio_size, e) @@ -82,7 +87,6 @@ fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError card_info.ext_csd.is_some() ); - let irq_num = decode_fdt_irq(&info.interrupts()); let raw = SharedDriver::new(card); let dev = MciBlockDevice { raw: Some(raw), @@ -91,8 +95,8 @@ fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError queue_created: false, irq_handler_taken: false, }; - plat_dev.register_block_with_irq(dev, irq_num); - info!("phytium-mci block device registered irq={:?}", irq_num); + let irq = probe.register_block(dev)?; + info!("phytium-mci block device registered irq={:?}", irq); Ok(()) } diff --git a/drivers/ax-driver/src/block/rockchip/sdhci_rk3568.rs b/drivers/ax-driver/src/block/rockchip/sdhci_rk3568.rs index fc086d6973..f4f2a9d4ff 100644 --- a/drivers/ax-driver/src/block/rockchip/sdhci_rk3568.rs +++ b/drivers/ax-driver/src/block/rockchip/sdhci_rk3568.rs @@ -23,7 +23,11 @@ use core::{ use dma_api::DeviceDma; use log::{info, warn}; use rdif_clk::ClockId; -use rdrive::{Device, DriverGeneric, PlatformDevice, probe::OnProbeError, register::FdtInfo}; +use rdrive::{ + Device, DriverGeneric, + probe::OnProbeError, + register::{FdtInfo, ProbeFdt}, +}; use sdhci_host::{BlockRequest, BlockRequestSlot, HostClock, RequestId, Sdhci}; use sdmmc_protocol::{ BlockPoll, BlockTransferMode, Error, OperationPoll, @@ -33,7 +37,7 @@ use sdmmc_protocol::{ use spin::Once; use crate::{ - block::{PlatformDeviceBlock, SharedDriver, decode_fdt_irq}, + block::{ProbeFdtBlock, SharedDriver}, mmio::iomap, }; @@ -110,7 +114,8 @@ crate::model_register!( ], ); -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let info = probe.info(); let base_reg = info .node .regs() @@ -130,7 +135,7 @@ fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError ); let mmio_base = iomap(base_reg.address as usize, mmio_size as usize)?; - init_core_clock(&info)?; + init_core_clock(info)?; let mut host = unsafe { Sdhci::new(mmio_base) }; if CLK_DEV.is_completed() { @@ -163,7 +168,6 @@ fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError card_info.ext_csd.is_some() ); - let irq_num = decode_fdt_irq(&info.interrupts()); let raw = SharedDriver::new(card); let dev = BlockDevice { raw: Some(raw.clone()), @@ -172,10 +176,10 @@ fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError queue_created: false, irq_handler_taken: false, }; - plat_dev.register_block_with_irq(dev, irq_num); + let irq = probe.register_block(dev)?; info!( "rockchip-rk3568-sdhci block device registered irq={:?}", - irq_num + irq ); Ok(()) } diff --git a/drivers/ax-driver/src/block/rockchip_mmc.rs b/drivers/ax-driver/src/block/rockchip_mmc.rs index 2f9aec7de4..5c43a023be 100644 --- a/drivers/ax-driver/src/block/rockchip_mmc.rs +++ b/drivers/ax-driver/src/block/rockchip_mmc.rs @@ -23,7 +23,11 @@ use core::{ use dma_api::DeviceDma; use log::{info, warn}; use rdif_clk::ClockId; -use rdrive::{Device, DriverGeneric, PlatformDevice, probe::OnProbeError, register::FdtInfo}; +use rdrive::{ + Device, DriverGeneric, + probe::OnProbeError, + register::{FdtInfo, ProbeFdt}, +}; use sdhci_host::{BlockRequest, BlockRequestSlot, HostClock, RequestId, Sdhci}; use sdmmc_protocol::{ BlockPoll, BlockTransferMode, Error, OperationPoll, @@ -33,7 +37,7 @@ use sdmmc_protocol::{ use spin::Once; use crate::{ - block::{PlatformDeviceBlock, SharedDriver, decode_fdt_irq}, + block::{ProbeFdtBlock, SharedDriver}, mmio::iomap, }; @@ -63,7 +67,8 @@ crate::model_register!( ], ); -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let info = probe.info(); let base_reg = info .node .regs() @@ -83,7 +88,7 @@ fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError ); let mmio_base = iomap(base_reg.address as usize, mmio_size as usize)?; - init_core_clock(&info)?; + init_core_clock(info)?; let mut host = unsafe { Sdhci::new(mmio_base) }; if CLK_DEV.is_completed() { @@ -115,7 +120,6 @@ fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError card_info.ext_csd.is_some() ); - let irq_num = decode_fdt_irq(&info.interrupts()); let raw = SharedDriver::new(card); let dev = BlockDevice { raw: Some(raw.clone()), @@ -124,8 +128,8 @@ fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError queue_created: false, irq_handler_taken: false, }; - plat_dev.register_block_with_irq(dev, irq_num); - info!("rockchip-sdhci block device registered irq={:?}", irq_num); + let irq = probe.register_block(dev)?; + info!("rockchip-sdhci block device registered irq={:?}", irq); Ok(()) } diff --git a/drivers/ax-driver/src/block/rockchip_sd.rs b/drivers/ax-driver/src/block/rockchip_sd.rs index 1855bde650..886c63176d 100644 --- a/drivers/ax-driver/src/block/rockchip_sd.rs +++ b/drivers/ax-driver/src/block/rockchip_sd.rs @@ -18,7 +18,10 @@ use core::time::Duration; use dwmmc_host::DwMmc; use log::{info, warn}; use rdif_clk::ClockId; -use rdrive::{PlatformDevice, probe::OnProbeError, register::FdtInfo}; +use rdrive::{ + probe::OnProbeError, + register::{FdtInfo, ProbeFdt}, +}; use sdmmc_protocol::{ Error, OperationPoll, error::Phase, @@ -26,7 +29,7 @@ use sdmmc_protocol::{ }; use crate::{ - block::{PlatformDeviceBlock, SharedDriver, decode_fdt_irq}, + block::{ProbeFdtBlock, SharedDriver}, mmio::iomap, soc::scmi, }; @@ -63,7 +66,8 @@ crate::model_register!( ], ); -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let info = probe.info(); let base_reg = info .node .regs() @@ -84,15 +88,15 @@ fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError let mmio_base = iomap(base_reg.address as usize, mmio_size as usize)?; let mut host = unsafe { DwMmc::new(mmio_base) }; - let reference_clock = dwmmc_reference_clock(&info); + let reference_clock = dwmmc_reference_clock(info); if let Some(reference_clock) = reference_clock { info!( "rockchip-dwmmc: using ciu reference clock {} Hz", reference_clock ); host.set_reference_clock(reference_clock); - if is_rk3588_dwmmc(&info) { - init_rk3588_sdmmc_phase(&info, reference_clock)?; + if is_rk3588_dwmmc(info) { + init_rk3588_sdmmc_phase(info, reference_clock)?; } } else { warn!( @@ -125,12 +129,11 @@ fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError ); if let Some(reference_clock) = reference_clock - && is_rk3588_dwmmc(&info) + && is_rk3588_dwmmc(info) { tune_rk3588_sdmmc_sample_phase(&mut sd, reference_clock); } - let irq_num = decode_fdt_irq(&info.interrupts()); let raw = SharedDriver::new(sd); let dev = SdBlockDevice { raw: Some(raw.clone()), @@ -139,8 +142,8 @@ fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError queue_created: false, irq_handler_taken: false, }; - plat_dev.register_block_with_irq(dev, irq_num); - info!("rockchip-sd block device registered irq={:?}", irq_num); + let irq = probe.register_block(dev)?; + info!("rockchip-sd block device registered irq={:?}", irq); Ok(()) } diff --git a/drivers/ax-driver/src/display/binding.rs b/drivers/ax-driver/src/display/binding.rs index e80c0cf061..682eb0da87 100644 --- a/drivers/ax-driver/src/display/binding.rs +++ b/drivers/ax-driver/src/display/binding.rs @@ -2,20 +2,37 @@ use alloc::{boxed::Box, string::String, vec::Vec}; use ax_errno::AxError; use rdif_display::Interface; -use rdrive::{Device, DriverGeneric}; +use rdrive::{DriverGeneric, probe::OnProbeError}; + +use crate::{ + BindingInfo, binding_info_from_acpi, binding_info_from_fdt, + registration::{BoundDevice, TakeRegistered, register_bound_device, take_registered_device}, +}; +#[cfg(feature = "pci")] +use crate::{PciIrqRequirement, binding_info_from_pci}; pub struct PlatformDisplayDevice { name: String, + info: BindingInfo, display: Option>, } impl PlatformDisplayDevice { - fn new(name: String, display: Box) -> Self { + fn new(name: String, display: Box, info: BindingInfo) -> Self { Self { name, + info, display: Some(display), } } + + pub fn binding_info(&self) -> &BindingInfo { + &self.info + } + + pub fn irq_num(&self) -> Option { + self.info.irq_num() + } } impl DriverGeneric for PlatformDisplayDevice { @@ -24,22 +41,131 @@ impl DriverGeneric for PlatformDisplayDevice { } } +impl BoundDevice for PlatformDisplayDevice { + fn binding_info(&self) -> &BindingInfo { + &self.info + } +} + +impl TakeRegistered for PlatformDisplayDevice { + type Output = Box; + + fn take_registered(&mut self) -> Option { + self.display.take() + } +} + pub trait PlatformDeviceDisplay { - fn register_display(self, dev: T) + fn register_display(self, dev: T) -> Option + where + T: Interface + 'static; + + fn register_display_with_info(self, dev: T, info: BindingInfo) -> Option where T: Interface + 'static; } impl PlatformDeviceDisplay for rdrive::PlatformDevice { - fn register_display(self, dev: T) + fn register_display(self, dev: T) -> Option + where + T: Interface + 'static, + { + self.register_display_with_info(dev, BindingInfo::empty()) + } + + fn register_display_with_info(self, dev: T, info: BindingInfo) -> Option + where + T: Interface + 'static, + { + register_display_with_info(self, dev, info) + } +} + +pub trait ProbeFdtDisplay { + fn register_display(self, dev: T) -> Result, OnProbeError> + where + T: Interface + 'static; +} + +impl ProbeFdtDisplay for rdrive::probe::fdt::ProbeFdt<'_> { + fn register_display(self, dev: T) -> Result, OnProbeError> + where + T: Interface + 'static, + { + let info = binding_info_from_fdt(self.info())?; + Ok(register_display_with_info( + self.into_platform_device(), + dev, + info, + )) + } +} + +pub trait ProbeAcpiDisplay { + fn register_display(self, dev: T) -> Result, OnProbeError> + where + T: Interface + 'static; +} + +impl ProbeAcpiDisplay for rdrive::probe::acpi::ProbeAcpi<'_> { + fn register_display(self, dev: T) -> Result, OnProbeError> + where + T: Interface + 'static, + { + let info = binding_info_from_acpi(self.info())?; + Ok(register_display_with_info( + self.into_platform_device(), + dev, + info, + )) + } +} + +#[cfg(feature = "pci")] +pub trait ProbePciDisplay { + fn register_display( + self, + dev: T, + requirement: PciIrqRequirement, + ) -> Result, OnProbeError> + where + T: Interface + 'static; +} + +#[cfg(feature = "pci")] +impl ProbePciDisplay for rdrive::probe::pci::ProbePci<'_> { + fn register_display( + self, + dev: T, + requirement: PciIrqRequirement, + ) -> Result, OnProbeError> where T: Interface + 'static, { - let name = dev.name().into(); - self.register(PlatformDisplayDevice::new(name, Box::new(dev))); + let info = binding_info_from_pci(self.info(), requirement)?; + Ok(register_display_with_info( + self.into_platform_device(), + dev, + info, + )) } } +fn register_display_with_info( + plat_dev: rdrive::PlatformDevice, + dev: T, + info: BindingInfo, +) -> Option +where + T: Interface + 'static, +{ + let name = dev.name().into(); + register_bound_device( + plat_dev, + PlatformDisplayDevice::new(name, Box::new(dev), info), + ) +} + pub fn take_display_devices() -> Result>, AxError> { let mut devices = Vec::new(); for dev in rdrive::get_list::() { @@ -50,8 +176,70 @@ pub fn take_display_devices() -> Result>, AxError> { } fn take_display_device( - device: Device, + device: rdrive::Device, ) -> Result, AxError> { - let mut device = device.lock().map_err(|_| AxError::BadState)?; - device.display.take().ok_or(AxError::BadState) + take_registered_device(device).ok_or(AxError::BadState) +} + +#[cfg(test)] +mod tests { + extern crate std; + + use rdif_display::{DisplayError, DisplayInfo, FrameBuffer, PixelFormat}; + + use super::*; + use crate::BindingInfo; + + struct TestDisplay { + fb: [u8; 16], + } + + impl DriverGeneric for TestDisplay { + fn name(&self) -> &str { + "test-display" + } + } + + impl Interface for TestDisplay { + fn info(&self) -> DisplayInfo { + DisplayInfo { + width: 2, + height: 2, + stride: 8, + format: PixelFormat::Xrgb8888, + fb_size: self.fb.len(), + } + } + + fn framebuffer(&mut self) -> Result, DisplayError> { + Ok(FrameBuffer::from_slice(&mut self.fb)) + } + } + + #[test] + fn platform_display_device_exposes_binding_info_irq_num() { + let irq = 42; + let device = PlatformDisplayDevice::new( + "test-display".into(), + Box::new(TestDisplay { fb: [0; 16] }), + BindingInfo::with_irq(Some(irq)), + ); + + assert_eq!(device.binding_info().irq_num(), Some(irq)); + assert_eq!(device.irq_num(), Some(irq)); + assert_eq!(BoundDevice::irq_num(&device), Some(irq)); + } + + #[test] + fn platform_display_device_empty_binding_has_no_irq_num() { + let device = PlatformDisplayDevice::new( + "test-display".into(), + Box::new(TestDisplay { fb: [0; 16] }), + BindingInfo::empty(), + ); + + assert_eq!(device.binding_info().irq_num(), None); + assert_eq!(device.irq_num(), None); + assert_eq!(BoundDevice::irq_num(&device), None); + } } diff --git a/drivers/ax-driver/src/input/binding.rs b/drivers/ax-driver/src/input/binding.rs index 02c7f03a8d..d1ca9fda32 100644 --- a/drivers/ax-driver/src/input/binding.rs +++ b/drivers/ax-driver/src/input/binding.rs @@ -2,20 +2,37 @@ use alloc::{boxed::Box, string::String, vec::Vec}; use ax_errno::AxError; use rdif_input::Interface; -use rdrive::{Device, DriverGeneric}; +use rdrive::{DriverGeneric, probe::OnProbeError}; + +use crate::{ + BindingInfo, binding_info_from_acpi, binding_info_from_fdt, + registration::{BoundDevice, TakeRegistered, register_bound_device, take_registered_device}, +}; +#[cfg(feature = "pci")] +use crate::{PciIrqRequirement, binding_info_from_pci}; pub struct PlatformInputDevice { name: String, + info: BindingInfo, input: Option>, } impl PlatformInputDevice { - fn new(name: String, input: Box) -> Self { + fn new(name: String, input: Box, info: BindingInfo) -> Self { Self { name, + info, input: Some(input), } } + + pub fn binding_info(&self) -> &BindingInfo { + &self.info + } + + pub fn irq_num(&self) -> Option { + self.info.irq_num() + } } impl DriverGeneric for PlatformInputDevice { @@ -24,22 +41,131 @@ impl DriverGeneric for PlatformInputDevice { } } +impl BoundDevice for PlatformInputDevice { + fn binding_info(&self) -> &BindingInfo { + &self.info + } +} + +impl TakeRegistered for PlatformInputDevice { + type Output = Box; + + fn take_registered(&mut self) -> Option { + self.input.take() + } +} + pub trait PlatformDeviceInput { - fn register_input(self, dev: T) + fn register_input(self, dev: T) -> Option + where + T: Interface + 'static; + + fn register_input_with_info(self, dev: T, info: BindingInfo) -> Option where T: Interface + 'static; } impl PlatformDeviceInput for rdrive::PlatformDevice { - fn register_input(self, dev: T) + fn register_input(self, dev: T) -> Option + where + T: Interface + 'static, + { + self.register_input_with_info(dev, BindingInfo::empty()) + } + + fn register_input_with_info(self, dev: T, info: BindingInfo) -> Option where T: Interface + 'static, { - let name = dev.name().into(); - self.register(PlatformInputDevice::new(name, Box::new(dev))); + register_input_with_info(self, dev, info) } } +pub trait ProbeFdtInput { + fn register_input(self, dev: T) -> Result, OnProbeError> + where + T: Interface + 'static; +} + +impl ProbeFdtInput for rdrive::probe::fdt::ProbeFdt<'_> { + fn register_input(self, dev: T) -> Result, OnProbeError> + where + T: Interface + 'static, + { + let info = binding_info_from_fdt(self.info())?; + Ok(register_input_with_info( + self.into_platform_device(), + dev, + info, + )) + } +} + +pub trait ProbeAcpiInput { + fn register_input(self, dev: T) -> Result, OnProbeError> + where + T: Interface + 'static; +} + +impl ProbeAcpiInput for rdrive::probe::acpi::ProbeAcpi<'_> { + fn register_input(self, dev: T) -> Result, OnProbeError> + where + T: Interface + 'static, + { + let info = binding_info_from_acpi(self.info())?; + Ok(register_input_with_info( + self.into_platform_device(), + dev, + info, + )) + } +} + +#[cfg(feature = "pci")] +pub trait ProbePciInput { + fn register_input( + self, + dev: T, + requirement: PciIrqRequirement, + ) -> Result, OnProbeError> + where + T: Interface + 'static; +} + +#[cfg(feature = "pci")] +impl ProbePciInput for rdrive::probe::pci::ProbePci<'_> { + fn register_input( + self, + dev: T, + requirement: PciIrqRequirement, + ) -> Result, OnProbeError> + where + T: Interface + 'static, + { + let info = binding_info_from_pci(self.info(), requirement)?; + Ok(register_input_with_info( + self.into_platform_device(), + dev, + info, + )) + } +} + +fn register_input_with_info( + plat_dev: rdrive::PlatformDevice, + dev: T, + info: BindingInfo, +) -> Option +where + T: Interface + 'static, +{ + let name = dev.name().into(); + register_bound_device( + plat_dev, + PlatformInputDevice::new(name, Box::new(dev), info), + ) +} + pub fn take_input_devices() -> Result>, AxError> { let mut devices = Vec::new(); for dev in rdrive::get_list::() { @@ -48,7 +174,87 @@ pub fn take_input_devices() -> Result>, AxError> { Ok(devices) } -fn take_input_device(device: Device) -> Result, AxError> { - let mut device = device.lock().map_err(|_| AxError::BadState)?; - device.input.take().ok_or(AxError::BadState) +fn take_input_device( + device: rdrive::Device, +) -> Result, AxError> { + take_registered_device(device).ok_or(AxError::BadState) +} + +#[cfg(test)] +mod tests { + extern crate std; + + use rdif_input::{EventType, InputDeviceId, InputError, InputEvent}; + + use super::*; + use crate::BindingInfo; + + struct TestInput; + + impl DriverGeneric for TestInput { + fn name(&self) -> &str { + "test-input" + } + } + + impl Interface for TestInput { + fn device_id(&self) -> InputDeviceId { + InputDeviceId { + bus_type: 3, + vendor: 1, + product: 2, + version: 1, + } + } + + fn physical_location(&self) -> &str { + "test/input0" + } + + fn unique_id(&self) -> &str { + "input0" + } + + fn get_event_bits(&mut self, _ty: EventType, out: &mut [u8]) -> Result { + if let Some(first) = out.first_mut() { + *first = 1; + } + Ok(!out.is_empty()) + } + + fn read_event(&mut self) -> Result { + Ok(InputEvent { + event_type: EventType::Key as u16, + code: 30, + value: 1, + }) + } + } + + #[test] + fn platform_input_device_exposes_binding_info_irq_num() { + let irq = 43; + let device = PlatformInputDevice::new( + "test-input".into(), + Box::new(TestInput), + BindingInfo::with_irq(Some(irq)), + ); + + assert_eq!(device.binding_info().irq_num(), Some(irq)); + assert_eq!(device.irq_num(), Some(irq)); + assert_eq!(BoundDevice::irq_num(&device), Some(irq)); + } + + #[test] + fn platform_input_device_empty_binding_has_no_irq_num() { + let device = PlatformInputDevice::new( + "test-input".into(), + Box::new(TestInput), + BindingInfo::empty(), + ); + + assert_eq!(device.binding_info().irq_num(), None); + assert_eq!(device.irq_num(), None); + assert_eq!(BoundDevice::irq_num(&device), None); + } } diff --git a/drivers/ax-driver/src/lib.rs b/drivers/ax-driver/src/lib.rs index 2835619f36..73bfa8c32a 100644 --- a/drivers/ax-driver/src/lib.rs +++ b/drivers/ax-driver/src/lib.rs @@ -36,15 +36,26 @@ macro_rules! model_register { }; } -crate::model_register!( +model_register!( name: "ax-driver macro placeholder", level: ProbeLevel::PostKernel, priority: ProbePriority::DEFAULT, probe_kinds: &[], ); +mod binding_info; +mod binding_resolver; pub mod error; pub mod mmio; +#[cfg(any( + feature = "block", + feature = "display", + feature = "input", + feature = "net", + feature = "usb", + feature = "vsock" +))] +mod registration; #[cfg(feature = "block")] pub mod block; @@ -57,6 +68,7 @@ pub mod net; #[cfg(feature = "vsock")] pub mod vsock; +#[cfg(feature = "pci")] pub mod pci; #[cfg(feature = "rknpu")] pub mod rknpu; @@ -76,4 +88,12 @@ pub mod usb; #[cfg(virtio_dev)] pub mod virtio; +pub use binding_info::BindingInfo; +#[cfg(feature = "pci")] +pub use binding_info::PciIrqRequirement; +#[cfg(feature = "pci")] +pub use binding_resolver::binding_info_from_pci; +pub use binding_resolver::{ + binding_info_from_acpi, binding_info_from_acpi_route, binding_info_from_fdt, +}; pub use error::{Error, Result}; diff --git a/drivers/ax-driver/src/net/binding.rs b/drivers/ax-driver/src/net/binding.rs index f59fb130c2..f8e8e9b53a 100644 --- a/drivers/ax-driver/src/net/binding.rs +++ b/drivers/ax-driver/src/net/binding.rs @@ -3,25 +3,40 @@ extern crate alloc; use alloc::boxed::Box; use rd_net::{Interface, NetError}; -use rdrive::{Device, DriverGeneric, probe::pci::EndpointRc}; +use rdrive::{Device, DriverGeneric, probe::OnProbeError}; + +use crate::{ + BindingInfo, binding_info_from_acpi, binding_info_from_fdt, + registration::{BoundDevice, register_bound_device}, +}; +#[cfg(feature = "pci")] +use crate::{PciIrqRequirement, binding_info_from_pci}; pub struct PlatformNetDevice { name: &'static str, - irq_num: Option, + info: BindingInfo, net: Option, } impl PlatformNetDevice { - fn new(name: &'static str, net: rd_net::Net, irq_num: Option) -> Self { + fn new(name: &'static str, net: rd_net::Net, info: BindingInfo) -> Self { Self { name, - irq_num, + info, net: Some(net), } } pub fn take_net(&mut self) -> Option<(rd_net::Net, &'static str, Option)> { - Some((self.net.take()?, self.name, self.irq_num)) + Some((self.net.take()?, self.name, self.info.irq_num())) + } + + pub fn binding_info(&self) -> &BindingInfo { + &self.info + } + + pub fn irq_num(&self) -> Option { + self.info.irq_num() } } @@ -41,114 +56,132 @@ impl DriverGeneric for PlatformNetDevice { } } -pub fn pci_legacy_irq(endpoint: &EndpointRc) -> Option { - pci_irq_candidates( - endpoint.address(), - endpoint.interrupt_pin(), - endpoint.interrupt_line(), - || { - #[cfg(pci_dyn_acpi_intx_route)] - { - let interrupt_pin = endpoint.interrupt_pin(); - if interrupt_pin != 0 { - match crate::pci::acpi_irq_for_endpoint(endpoint.address(), interrupt_pin) { - Ok(Some(irq)) => return Some(irq), - Ok(None) => {} - Err(err) => log::warn!( - "failed to resolve ACPI IRQ for net endpoint {}: {err}", - endpoint.address() - ), - } - } - } - None - }, - || { - #[cfg(pci_dyn_intx_route)] - { - let interrupt_pin = endpoint.interrupt_pin(); - if interrupt_pin != 0 { - match crate::pci::fdt_irq_for_endpoint(endpoint.address(), interrupt_pin) { - Ok(Some(irq)) => return Some(irq), - Ok(None) => {} - Err(err) => log::warn!( - "failed to resolve FDT IRQ for net endpoint {}: {err}", - endpoint.address() - ), - } - } - } - None - }, - ) +impl BoundDevice for PlatformNetDevice { + fn binding_info(&self) -> &BindingInfo { + &self.info + } } -fn pci_irq_candidates( - address: rdrive::probe::pci::PciAddress, - interrupt_pin: u8, - interrupt_line: u8, - acpi: impl FnOnce() -> Option, - fdt: impl FnOnce() -> Option, -) -> Option { - if let Some(irq) = acpi() { - return Some(irq); - } +pub trait PlatformDeviceNet { + fn register_net(self, name: &'static str, dev: T) -> Option + where + T: Interface + 'static; - if let Some(irq) = fdt() { - return Some(irq); + fn register_net_with_info( + self, + name: &'static str, + dev: T, + info: BindingInfo, + ) -> Option + where + T: Interface + 'static; +} + +impl PlatformDeviceNet for rdrive::PlatformDevice { + fn register_net(self, name: &'static str, dev: T) -> Option + where + T: Interface + 'static, + { + self.register_net_with_info(name, dev, BindingInfo::empty()) } - if let Some(irq) = crate::pci::legacy_irq_for_endpoint(address, interrupt_pin) { - return Some(irq); + fn register_net_with_info( + self, + name: &'static str, + dev: T, + info: BindingInfo, + ) -> Option + where + T: Interface + 'static, + { + register_net_with_info(self, name, dev, info) } +} - if interrupt_line == 0 || interrupt_line == u8::MAX { - return None; +pub trait ProbeFdtNet { + fn register_net(self, name: &'static str, dev: T) -> Result, OnProbeError> + where + T: Interface + 'static; +} + +impl ProbeFdtNet for rdrive::probe::fdt::ProbeFdt<'_> { + fn register_net(self, name: &'static str, dev: T) -> Result, OnProbeError> + where + T: Interface + 'static, + { + let info = binding_info_from_fdt(self.info())?; + Ok(register_net_with_info( + self.into_platform_device(), + name, + dev, + info, + )) } - Some(crate::pci::legacy_line_to_irq(interrupt_line)) } -#[cfg(test)] -mod tests { - use rdrive::probe::pci::PciAddress; - - use super::pci_irq_candidates; - - #[test] - fn pci_irq_resolution_prefers_acpi_then_fdt_then_fallback_line() { - let address = PciAddress::new(0, 0, 3, 0); - - assert_eq!( - pci_irq_candidates(address, 1, 9, || Some(0x31), || Some(0x40)), - Some(0x31) - ); - assert_eq!( - pci_irq_candidates(address, 1, 9, || None, || Some(0x40)), - Some(0x40) - ); - let fallback_irq = pci_irq_candidates(address, 1, 9, || None, || None); - if cfg!(all(target_arch = "x86_64", plat_dyn)) { - assert_eq!(fallback_irq, Some(0x39)); - } else if cfg!(target_arch = "x86_64") { - assert_eq!(fallback_irq, Some(0x29)); - } else { - assert_eq!(fallback_irq, Some(9)); - } +pub trait ProbeAcpiNet { + fn register_net(self, name: &'static str, dev: T) -> Result, OnProbeError> + where + T: Interface + 'static; +} + +impl ProbeAcpiNet for rdrive::probe::acpi::ProbeAcpi<'_> { + fn register_net(self, name: &'static str, dev: T) -> Result, OnProbeError> + where + T: Interface + 'static, + { + let info = binding_info_from_acpi(self.info())?; + Ok(register_net_with_info( + self.into_platform_device(), + name, + dev, + info, + )) } } -pub trait PlatformDeviceNet { - fn register_net(self, name: &'static str, dev: T, irq_num: Option) +#[cfg(feature = "pci")] +pub trait ProbePciNet { + fn register_net( + self, + name: &'static str, + dev: T, + requirement: PciIrqRequirement, + ) -> Result, OnProbeError> where T: Interface + 'static; } -impl PlatformDeviceNet for rdrive::PlatformDevice { - fn register_net(self, name: &'static str, dev: T, irq_num: Option) +#[cfg(feature = "pci")] +impl ProbePciNet for rdrive::probe::pci::ProbePci<'_> { + fn register_net( + self, + name: &'static str, + dev: T, + requirement: PciIrqRequirement, + ) -> Result, OnProbeError> where T: Interface + 'static, { - let net = rd_net::Net::new(dev, axklib::dma::op()); - self.register(PlatformNetDevice::new(name, net, irq_num)); + let info = binding_info_from_pci(self.info(), requirement)?; + Ok(register_net_with_info( + self.into_platform_device(), + name, + dev, + info, + )) } } + +fn register_net_with_info( + plat_dev: rdrive::PlatformDevice, + name: &'static str, + dev: T, + info: BindingInfo, +) -> Option +where + T: Interface + 'static, +{ + let net = rd_net::Net::new(dev, axklib::dma::op()); + register_bound_device(plat_dev, PlatformNetDevice::new(name, net, info)) +} diff --git a/drivers/ax-driver/src/net/fxmac.rs b/drivers/ax-driver/src/net/fxmac.rs index f964f83749..4a5496b07b 100644 --- a/drivers/ax-driver/src/net/fxmac.rs +++ b/drivers/ax-driver/src/net/fxmac.rs @@ -20,7 +20,7 @@ const PAGE_SIZE: usize = 0x1000; pub fn register(plat_dev: PlatformDevice) { let dev = FxmacNet::new(); - plat_dev.register_net(DRIVER_NAME, dev, None); + plat_dev.register_net(DRIVER_NAME, dev); log::info!("registered FXmac network device"); } diff --git a/drivers/ax-driver/src/net/intel.rs b/drivers/ax-driver/src/net/intel.rs index 15ee9aff03..b72e7a84c1 100644 --- a/drivers/ax-driver/src/net/intel.rs +++ b/drivers/ax-driver/src/net/intel.rs @@ -1,17 +1,12 @@ -use alloc::format; - use eth_intel::E1000; use log::debug; use pcie::CommandRegister; -use rdrive::{ - PlatformDevice, - probe::{ - OnProbeError, - pci::{EndpointRc, FnOnProbe}, - }, +use rdrive::probe::{ + OnProbeError, + pci::{FnOnProbe, ProbePci}, }; -use crate::net::{PlatformDeviceNet, pci_legacy_irq}; +use crate::{PciIrqRequirement, net::ProbePciNet}; const DRIVER_NAME: &str = "eth-intel-e1000"; @@ -24,14 +19,13 @@ crate::model_register!( }], ); -fn probe(endpoint: &mut EndpointRc, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe(mut probe: ProbePci<'_>) -> Result<(), OnProbeError> { + let endpoint = probe.endpoint_mut(); if !E1000::check_vid_did(endpoint.vendor_id(), endpoint.device_id()) { return Err(OnProbeError::NotMatch); } let address = endpoint.address(); - let irq = pci_legacy_irq(endpoint) - .ok_or_else(|| OnProbeError::other(format!("failed to resolve IRQ for E1000 {address}")))?; let Some(bar) = endpoint.bar_mmio(0) else { return Err(OnProbeError::other("E1000 BAR0 MMIO region missing")); }; @@ -50,9 +44,9 @@ fn probe(endpoint: &mut EndpointRc, plat_dev: PlatformDevice) -> Result<(), OnPr ) .map_err(|err| OnProbeError::other(alloc::format!("failed to create e1000: {err:?}")))?; - plat_dev.register_net(DRIVER_NAME, dev, Some(irq)); + let irq = probe.register_net(DRIVER_NAME, dev, PciIrqRequirement::Required)?; debug!( - "intel e1000 PCI device registered successfully at {} with irq {:#x}", + "intel e1000 PCI device registered successfully at {} with irq {:?}", address, irq ); Ok(()) diff --git a/drivers/ax-driver/src/net/ixgbe.rs b/drivers/ax-driver/src/net/ixgbe.rs index e45544e030..75938fbf4a 100644 --- a/drivers/ax-driver/src/net/ixgbe.rs +++ b/drivers/ax-driver/src/net/ixgbe.rs @@ -9,14 +9,14 @@ use ixgbe_driver::{ use pcie::CommandRegister; use rd_net::{DmaBuffer, Event, IRxQueue, ITxQueue, NetError, QueueConfig}; use rdrive::{ - DriverGeneric, PlatformDevice, + DriverGeneric, probe::{ OnProbeError, - pci::{EndpointRc, FnOnProbe}, + pci::{FnOnProbe, ProbePci}, }, }; -use crate::net::{PlatformDeviceNet, pci_legacy_irq}; +use crate::{PciIrqRequirement, net::ProbePciNet}; const DRIVER_NAME: &str = "ixgbe"; const QUEUE_SIZE: usize = 512; @@ -36,14 +36,13 @@ crate::model_register!( }], ); -fn probe_pci(endpoint: &mut EndpointRc, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe_pci(mut probe: ProbePci<'_>) -> Result<(), OnProbeError> { + let endpoint = probe.endpoint_mut(); if endpoint.vendor_id() != INTEL_VEND || endpoint.device_id() != INTEL_82599 { return Err(OnProbeError::NotMatch); } let address = endpoint.address(); - let irq = pci_legacy_irq(endpoint) - .ok_or_else(|| OnProbeError::other(format!("failed to resolve IRQ for ixgbe {address}")))?; let Some(bar) = endpoint.bar_mmio(0) else { return Err(OnProbeError::other("ixgbe BAR0 MMIO region missing")); }; @@ -61,8 +60,8 @@ fn probe_pci(endpoint: &mut EndpointRc, plat_dev: PlatformDevice) -> Result<(), let dev = IxgbeNet::new(mmio.as_ptr() as usize, bar_len) .map_err(|err| OnProbeError::other(format!("failed to initialize ixgbe: {err:?}")))?; - plat_dev.register_net(DRIVER_NAME, dev, Some(irq)); - log::info!("registered ixgbe PCI network device at {address} with irq {irq:#x}"); + let irq = probe.register_net(DRIVER_NAME, dev, PciIrqRequirement::Required)?; + log::info!("registered ixgbe PCI network device at {address} with irq {irq:?}"); Ok(()) } diff --git a/drivers/ax-driver/src/net/realtek.rs b/drivers/ax-driver/src/net/realtek.rs index 73f68acd8b..f3400283a8 100644 --- a/drivers/ax-driver/src/net/realtek.rs +++ b/drivers/ax-driver/src/net/realtek.rs @@ -2,16 +2,13 @@ use core::sync::atomic::{AtomicBool, Ordering}; use log::{debug, info, warn}; use pcie::CommandRegister; -use rdrive::{ - PlatformDevice, - probe::{ - OnProbeError, - pci::{EndpointRc, FnOnProbe}, - }, +use rdrive::probe::{ + OnProbeError, + pci::{EndpointRc, FnOnProbe, ProbePci}, }; use realtek_rtl8125::Rtl8125; -use crate::net::{PlatformDeviceNet, pci_legacy_irq}; +use crate::{PciIrqRequirement, net::ProbePciNet}; const DRIVER_NAME: &str = "realtek-rtl8125"; const RTL8125_DMA_MASK: u64 = u32::MAX as u64; @@ -26,7 +23,8 @@ crate::model_register!( }], ); -fn probe(endpoint: &mut EndpointRc, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe(mut probe: ProbePci<'_>) -> Result<(), OnProbeError> { + let endpoint = probe.endpoint_mut(); if !Rtl8125::check_vid_did(endpoint.vendor_id(), endpoint.device_id()) { return Err(OnProbeError::NotMatch); } @@ -37,18 +35,13 @@ fn probe(endpoint: &mut EndpointRc, plat_dev: PlatformDevice) -> Result<(), OnPr return Err(OnProbeError::NotMatch); } - let irq = pci_legacy_irq(endpoint).ok_or_else(|| { - OnProbeError::other(alloc::format!( - "failed to resolve IRQ for RTL8125 {address}" - )) - })?; let Some((bar_index, bar)) = first_mmio_bar(endpoint) else { warn!("RTL8125 at {address} left unused: no PCI MMIO BAR found"); return Err(OnProbeError::NotMatch); }; info!( - "RTL8125 PCI endpoint {address}: BAR{bar_index}={:#x}..{:#x}, irq={irq}, int_pin={}, \ - int_line={}, command={:?}, status={:?}", + "RTL8125 PCI endpoint {address}: BAR{bar_index}={:#x}..{:#x}, int_pin={}, int_line={}, \ + command={:?}, status={:?}", bar.start, bar.end, endpoint.interrupt_pin(), @@ -89,8 +82,8 @@ fn probe(endpoint: &mut EndpointRc, plat_dev: PlatformDevice) -> Result<(), OnPr return Err(OnProbeError::NotMatch); } - plat_dev.register_net(DRIVER_NAME, dev, Some(irq)); - debug!("RTL8125 PCI network device registered at {address} with irq {irq:#x}"); + let irq = probe.register_net(DRIVER_NAME, dev, PciIrqRequirement::Required)?; + debug!("RTL8125 PCI network device registered at {address} with irq {irq:?}"); Ok(()) } diff --git a/drivers/ax-driver/src/pci/acpi.rs b/drivers/ax-driver/src/pci/acpi.rs index 0f73293a7a..1b4cc1f42d 100644 --- a/drivers/ax-driver/src/pci/acpi.rs +++ b/drivers/ax-driver/src/pci/acpi.rs @@ -1,18 +1,16 @@ extern crate alloc; -#[cfg(pci_dyn_acpi_intx_route)] +#[cfg(plat_dyn)] use alloc::format; use log::debug; -#[cfg(pci_dyn_acpi_intx_route)] -use rdrive::probe::acpi::AcpiGsiRoute; -#[cfg(pci_dyn_acpi_intx_route)] -use rdrive::probe::pci::PciAddress; +#[cfg(plat_dyn)] +use rdrive::probe::pci::PciInfo; use rdrive::{ PlatformDevice, probe::{ OnProbeError, - acpi::{AcpiId, AcpiInfo}, + acpi::{AcpiId, ProbeAcpi}, }, }; @@ -31,7 +29,8 @@ crate::model_register!( ], ); -fn probe_acpi_ecam(info: AcpiInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe_acpi_ecam(probe: ProbeAcpi<'_>) -> Result<(), OnProbeError> { + let (info, plat_dev) = probe.into_parts(); let mut registered = false; for region in info.root.pci_ecam_regions() { debug!("ACPI MCFG PCI ECAM region: {region:?}"); @@ -54,13 +53,9 @@ fn probe_acpi_ecam(info: AcpiInfo<'_>, plat_dev: PlatformDevice) -> Result<(), O } } -#[cfg(pci_dyn_acpi_intx_route)] -pub(crate) fn acpi_irq_for_endpoint( - address: PciAddress, - interrupt_pin: u8, -) -> Result, OnProbeError> { - let Some(result) = - rdrive::probe::acpi::with_acpi(|acpi| acpi.pci_irq_for_endpoint(address, interrupt_pin)) +#[cfg(plat_dyn)] +pub(crate) fn acpi_irq_for_endpoint(info: PciInfo) -> Result, OnProbeError> { + let Some(result) = rdrive::probe::acpi::with_acpi(|acpi| acpi.pci_irq_for_endpoint(info)) else { return Ok(None); }; @@ -69,27 +64,18 @@ pub(crate) fn acpi_irq_for_endpoint( return Ok(None); }; - let irq = setup_acpi_intx_irq(&route.gsi)?; + let irq = crate::binding_info_from_acpi_route("PCI endpoint", Some(route.gsi))? + .irq_num() + .expect("Some ACPI GSI route must resolve to an IRQ"); log::info!( - "ACPI PCI INTx route: endpoint {} pin {} -> GSI {} IOAPIC {} input {} vector {:#x}", - address, - interrupt_pin, + "ACPI PCI INTx route: endpoint {} pin {} -> GSI {} {:?} {} input {} vector {:#x}", + info.address, + route.intx_route.root_pin, route.gsi.gsi, + route.gsi.controller, route.gsi.controller_id, route.gsi.controller_input, - usize::from(irq) + irq ); - Ok(Some(usize::from(irq))) -} - -#[cfg(pci_dyn_acpi_intx_route)] -fn setup_acpi_intx_irq(route: &AcpiGsiRoute) -> Result { - let intc = rdrive::get_list::() - .into_iter() - .find(|intc| intc.descriptor().name.starts_with("ACPI IOAPIC")) - .ok_or_else(|| OnProbeError::other("ACPI IOAPIC interrupt controller is not registered"))?; - let mut intc = intc - .lock() - .map_err(|_| OnProbeError::other("ACPI IOAPIC interrupt controller is locked"))?; - Ok(intc.setup_irq_by_acpi(route)) + Ok(Some(irq)) } diff --git a/drivers/ax-driver/src/pci/fdt.rs b/drivers/ax-driver/src/pci/fdt/mod.rs similarity index 56% rename from drivers/ax-driver/src/pci/fdt.rs rename to drivers/ax-driver/src/pci/fdt/mod.rs index 04357731fb..575ae41cd9 100644 --- a/drivers/ax-driver/src/pci/fdt.rs +++ b/drivers/ax-driver/src/pci/fdt/mod.rs @@ -1,26 +1,24 @@ extern crate alloc; use alloc::format; -#[cfg(pci_dyn_intx_route)] +#[cfg(plat_dyn)] use alloc::vec::Vec; -#[cfg(pci_dyn_intx_route)] -use fdt_edit::Fdt; +#[cfg(plat_dyn)] +use fdt_edit::{Fdt, PciInterruptMap}; use fdt_edit::{NodeType, PciRange, PciSpace}; use log::{debug, trace, warn}; -#[cfg(pci_dyn_intx_route)] -use rdrive::probe::pci::PciAddress; +#[cfg(plat_dyn)] +use rdrive::probe::pci::{PciInfo, PciIntxRoute}; use rdrive::{ - PlatformDevice, probe::{ OnProbeError, pci::{PciMem32, PciMem64, PcieController, new_driver_generic}, }, - register::FdtInfo, + register::{FdtInfo, ProbeFdt}, }; #[cfg(feature = "rk3588-pcie")] -#[path = "rk3588.rs"] mod rk3588; crate::model_register!( @@ -35,7 +33,8 @@ crate::model_register!( ], ); -fn probe_generic_ecam(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe_generic_ecam(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (info, plat_dev) = probe.into_parts(); let NodeType::Pci(node) = info.node else { return Err(OnProbeError::NotMatch); }; @@ -131,32 +130,21 @@ pub(super) fn register_fdt_legacy_irq(info: &FdtInfo<'_>, logical_bus_end: u8) { super::register_legacy_irq_route(0, logical_bus_end, irq); } -#[cfg(pci_dyn_intx_route)] -pub fn fdt_irq_for_endpoint( - address: PciAddress, - interrupt_pin: u8, -) -> Result, OnProbeError> { - let Some(result) = - rdrive::with_fdt(|fdt| resolve_pci_irq_from_fdt(fdt, address, interrupt_pin)) - else { +#[cfg(plat_dyn)] +pub fn fdt_irq_for_endpoint(info: PciInfo) -> Result, OnProbeError> { + let Some(result) = rdrive::with_fdt(|fdt| resolve_pci_irq_from_fdt(fdt, info)) else { return Ok(None); }; result.map(Some) } -#[cfg(pci_dyn_intx_route)] -fn resolve_pci_irq_from_fdt( - fdt: &Fdt, - address: PciAddress, - interrupt_pin: u8, -) -> Result { - if interrupt_pin == 0 { - return Err(OnProbeError::other(format!( - "PCI endpoint {address} has no interrupt pin" - ))); - } +#[cfg(plat_dyn)] +fn resolve_pci_irq_from_fdt(fdt: &Fdt, info: PciInfo) -> Result { + let route = info.intx_route.ok_or_else(|| { + OnProbeError::other(format!("PCI endpoint {} has no INTx route", info.address)) + })?; - let bus = address.bus(); + let bus = info.address.bus(); let mut candidates = Vec::new(); let mut exact_range_matches = Vec::new(); for node in fdt.all_nodes() { @@ -178,59 +166,101 @@ fn resolve_pci_irq_from_fdt( exact_range_matches[0] } else if exact_range_matches.len() > 1 { return Err(OnProbeError::other(format!( - "multiple PCI host nodes in FDT match endpoint {address} with the same bus-range" + "multiple PCI host nodes in FDT match endpoint {} with the same bus-range", + info.address ))); } else if candidates.len() == 1 { candidates[0] } else if candidates.is_empty() { return Err(OnProbeError::other(format!( - "no PCI host node in FDT matches endpoint {address}" + "no PCI host node in FDT matches endpoint {}", + info.address ))); } else { return Err(OnProbeError::other(format!( - "multiple PCI host nodes in FDT match endpoint {address} without a unique bus-range \ - match" + "multiple PCI host nodes in FDT match endpoint {} without a unique bus-range match", + info.address + ))); + }; + + let bus = pci_host + .bus_range() + .map(|range| range.start as u8) + .unwrap_or(0); + let Some(interrupt) = pci_interrupt_map_entry(pci_host, bus, route) else { + return Err(OnProbeError::other(format!( + "failed to resolve PCI interrupt-map entry for endpoint {}", + info.address ))); }; - let irq = pci_host - .child_interrupts( - address.bus(), - address.device(), - address.function(), - interrupt_pin, - ) - .map_err(|err| { - OnProbeError::other(format!( - "failed to resolve PCI interrupt-map entry for endpoint {address}: {err:?}" - )) - })?; - - decode_irq_cells(&irq.irqs).ok_or_else(|| { + let parent = rdrive::fdt_phandle_to_device_id(interrupt.interrupt_parent).ok_or_else(|| { OnProbeError::other(format!( - "unsupported PCI interrupt specifier {:?} for endpoint {address}", - irq.irqs + "failed to resolve PCI interrupt parent {:?} for endpoint {}", + interrupt.interrupt_parent, info.address )) - }) + })?; + let intc = rdrive::get::(parent).map_err(|err| { + OnProbeError::other(format!( + "failed to get PCI interrupt parent {:?} for endpoint {}: {err:?}", + parent, info.address + )) + })?; + let mut intc = intc.lock().map_err(|err| { + OnProbeError::other(format!( + "failed to lock PCI interrupt parent {:?} for endpoint {}: {err:?}", + parent, info.address + )) + })?; + Ok(intc.setup_irq_by_fdt(&interrupt.parent_irq).into()) +} + +#[cfg(plat_dyn)] +fn pci_interrupt_map_entry( + pci_host: fdt_edit::PciNodeView<'_>, + bus: u8, + route: PciIntxRoute, +) -> Option { + let interrupt_map = pci_host.interrupt_map().ok()?; + let mask = pci_host.interrupt_map_mask()?; + let child_addr_cells = 3; + let child_irq_cells = pci_host.interrupt_cells() as usize; + let address = encoded_pci_child_address(bus, route.root_device, route.root_function); + let irq = [u32::from(route.root_pin)]; + let child_address = masked_cells(&address, child_addr_cells, &mask, 0); + let child_irq = masked_cells(&irq, child_irq_cells, &mask, child_addr_cells); + + interrupt_map + .into_iter() + .find(|entry| entry.child_address == child_address && entry.child_irq == child_irq) +} + +#[cfg(plat_dyn)] +fn encoded_pci_child_address(bus: u8, device: u8, function: u8) -> [u32; 3] { + [ + ((u32::from(bus) & 0xff) << 16) + | ((u32::from(device) & 0x1f) << 11) + | ((u32::from(function) & 0x07) << 8), + 0, + 0, + ] } -#[cfg(pci_dyn_intx_route)] -fn decode_irq_cells(specifier: &[u32]) -> Option { - match specifier { - [irq] => Some(*irq as usize), - [kind, irq, ..] => match *kind { - 0 => Some(*irq as usize + 32), - 1 => Some(*irq as usize + 16), - _ => Some(*irq as usize), - }, - _ => None, +#[cfg(plat_dyn)] +fn masked_cells(values: &[u32], len: usize, mask: &[u32], mask_offset: usize) -> Vec { + let mut cells = Vec::with_capacity(len); + for idx in 0..len { + let value = values.get(idx).copied().unwrap_or(0); + let mask_value = mask.get(mask_offset + idx).copied().unwrap_or(0xffff_ffff); + cells.push(value & mask_value); } + cells } #[cfg(feature = "list-pci-devices")] mod pci_list_devices { use log::info; - use rdrive::probe::pci::{EndpointRc, FnOnProbe}; + use rdrive::probe::pci::{FnOnProbe, ProbePci}; use super::*; @@ -243,8 +273,9 @@ mod pci_list_devices { }], ); - fn probe(endpoint: &mut EndpointRc, _plat_dev: PlatformDevice) -> Result<(), OnProbeError> { - info!("PCIe endpoint: {} bars={:?}", &**endpoint, endpoint.bars()); + fn probe(probe: ProbePci<'_>) -> Result<(), OnProbeError> { + let endpoint = probe.endpoint(); + info!("PCIe endpoint: {} bars={:?}", endpoint, endpoint.bars()); Err(OnProbeError::NotMatch) } } diff --git a/drivers/ax-driver/src/pci/rk3588/clocks_reset_gpio.rs b/drivers/ax-driver/src/pci/fdt/rk3588/clocks_reset_gpio.rs similarity index 86% rename from drivers/ax-driver/src/pci/rk3588/clocks_reset_gpio.rs rename to drivers/ax-driver/src/pci/fdt/rk3588/clocks_reset_gpio.rs index dacfa3830d..ec3920212e 100644 --- a/drivers/ax-driver/src/pci/rk3588/clocks_reset_gpio.rs +++ b/drivers/ax-driver/src/pci/fdt/rk3588/clocks_reset_gpio.rs @@ -1,4 +1,21 @@ -fn clock_specs_for_node(node: NodeType<'_>) -> Vec { +use alloc::{format, vec::Vec}; + +use fdt_edit::{ClockRef, Node, Phandle}; +use log::warn; +use rdrive::{ + probe::{OnProbeError, fdt::NodeType}, + register::FdtInfo, +}; + +use super::{ + resources::{ClockSpec, GpioSpec, RK3588_GPIO_BASES, ResetSpec}, + windows::{live_fdt, prop_str_list, rk3588_pcie_reset_pin}, +}; +use crate::soc::{ + rk3588_enable_clock, rk3588_reset_assert, rk3588_reset_deassert, rk3588_set_clock_rate, +}; + +pub(super) fn clock_specs_for_node(node: NodeType<'_>) -> Vec { let assigned_clocks = node .as_node() .get_property("assigned-clocks") @@ -39,7 +56,7 @@ fn clock_specs_for_node(node: NodeType<'_>) -> Vec { .collect() } -fn clock_specs(clocks: Vec) -> Vec { +pub(super) fn clock_specs(clocks: Vec) -> Vec { clocks .into_iter() .filter_map(|clock| { @@ -53,7 +70,7 @@ fn clock_specs(clocks: Vec) -> Vec { .collect() } -fn enable_clocks(clocks: &[ClockSpec]) -> Result<(), OnProbeError> { +pub(super) fn enable_clocks(clocks: &[ClockSpec]) -> Result<(), OnProbeError> { for clock in clocks { let id = clock.id; if id == 0 { @@ -77,7 +94,7 @@ fn enable_clocks(clocks: &[ClockSpec]) -> Result<(), OnProbeError> { Ok(()) } -fn parse_resets(node: NodeType<'_>) -> Result, OnProbeError> { +pub(super) fn parse_resets(node: NodeType<'_>) -> Result, OnProbeError> { let Some(prop) = node.as_node().get_property("resets") else { return Ok(Vec::new()); }; @@ -99,7 +116,7 @@ fn parse_resets(node: NodeType<'_>) -> Result, OnProbeError> { .collect()) } -fn assert_resets(resets: &[ResetSpec]) -> Result<(), OnProbeError> { +pub(super) fn assert_resets(resets: &[ResetSpec]) -> Result<(), OnProbeError> { for reset in resets { rk3588_reset_assert(reset.id).map_err(|err| { OnProbeError::other(format!( @@ -111,7 +128,7 @@ fn assert_resets(resets: &[ResetSpec]) -> Result<(), OnProbeError> { Ok(()) } -fn deassert_resets(resets: &[ResetSpec]) -> Result<(), OnProbeError> { +pub(super) fn deassert_resets(resets: &[ResetSpec]) -> Result<(), OnProbeError> { for reset in resets { rk3588_reset_deassert(reset.id).map_err(|err| { OnProbeError::other(format!( @@ -123,7 +140,10 @@ fn deassert_resets(resets: &[ResetSpec]) -> Result<(), OnProbeError> { Ok(()) } -fn parse_reset_gpio(info: &FdtInfo<'_>, apb_base: u64) -> Result, OnProbeError> { +pub(super) fn parse_reset_gpio( + info: &FdtInfo<'_>, + apb_base: u64, +) -> Result, OnProbeError> { if let Some(gpio) = parse_gpio_spec(info.node, "reset-gpios")? { return Ok(Some(gpio)); } diff --git a/drivers/ax-driver/src/pci/fdt/rk3588/mod.rs b/drivers/ax-driver/src/pci/fdt/rk3588/mod.rs new file mode 100644 index 0000000000..574389c918 --- /dev/null +++ b/drivers/ax-driver/src/pci/fdt/rk3588/mod.rs @@ -0,0 +1,7 @@ +pub(super) use super::{register_fdt_legacy_irq, set_pcie_mem_range}; + +mod clocks_reset_gpio; +mod phy; +mod resources; +mod slots; +mod windows; diff --git a/drivers/ax-driver/src/pci/rk3588/phy.rs b/drivers/ax-driver/src/pci/fdt/rk3588/phy.rs similarity index 91% rename from drivers/ax-driver/src/pci/rk3588/phy.rs rename to drivers/ax-driver/src/pci/fdt/rk3588/phy.rs index d23c42059c..0e11857dc9 100644 --- a/drivers/ax-driver/src/pci/rk3588/phy.rs +++ b/drivers/ax-driver/src/pci/fdt/rk3588/phy.rs @@ -1,4 +1,25 @@ -fn parse_phys(node_type: NodeType<'_>) -> Result, OnProbeError> { +use alloc::{format, string::ToString, vec::Vec}; +use core::time::Duration; + +use fdt_edit::{Node, Phandle}; +use log::{info, warn}; +use rdrive::probe::{OnProbeError, fdt::NodeType}; + +use super::{ + clocks_reset_gpio::{ + assert_resets, clock_specs_for_node, deassert_resets, enable_clocks, parse_resets, + }, + resources::{ + BIT_WRITEABLE_SHIFT, CombphyResources, PCIE3PHY_SRAM_INIT_DONE, PHP_GRF_PCIESEL_CON, + PHY_TYPE_PCIE, Pcie3PhyResources, PhyRef, RK3588_PCIE3PHY_CMN_CON0, + RK3588_PCIE3PHY_DEFAULT_MODE, RK3588_PCIE3PHY_PHY0_STATUS1, RK3588_PCIE3PHY_PHY1_STATUS1, + RegMmio, + }, + windows::{is_compatible, live_fdt, phy_cells, prop_phandle, prop_str_list, prop_u32}, +}; +use crate::soc::{rk3588_reset_assert, rk3588_reset_deassert}; + +pub(super) fn parse_phys(node_type: NodeType<'_>) -> Result, OnProbeError> { let node = node_type.as_node(); let Some(prop) = node.get_property("phys") else { return Ok(Vec::new()); @@ -33,7 +54,7 @@ fn parse_phys(node_type: NodeType<'_>) -> Result, OnProbeError> { Ok(refs) } -fn init_phys(host_node: NodeType<'_>, phys: &[PhyRef]) -> Result<(), OnProbeError> { +pub(super) fn init_phys(host_node: NodeType<'_>, phys: &[PhyRef]) -> Result<(), OnProbeError> { if phys.is_empty() { warn!( "Rockchip RK3588 PCIe host {} has no phys property", diff --git a/drivers/ax-driver/src/pci/rk3588/resources.rs b/drivers/ax-driver/src/pci/fdt/rk3588/resources.rs similarity index 74% rename from drivers/ax-driver/src/pci/rk3588/resources.rs rename to drivers/ax-driver/src/pci/fdt/rk3588/resources.rs index d2f9411854..6b20d4b5b7 100644 --- a/drivers/ax-driver/src/pci/rk3588/resources.rs +++ b/drivers/ax-driver/src/pci/fdt/rk3588/resources.rs @@ -10,24 +10,30 @@ use core::{ time::Duration, }; -use fdt_edit::{ClockRef, Fdt, Node, PciRange, PciSpace, Phandle, RegFixed}; +use fdt_edit::{Node, PciRange, Phandle, RegFixed}; +use log::{info, warn}; use mmio_api::{MmioAddr, MmioRaw}; -use rdif_pcie::{PciMem64, PcieController}; +use rdif_pcie::PcieController; use rdrive::{ - PlatformDevice, probe::{OnProbeError, fdt::NodeType}, - register::FdtInfo, + register::{FdtInfo, ProbeFdt}, }; -use rk3588_pci::{ - Delay, HostConfig, IatuMode, MEM_ATU_FIRST_REGION, OutboundWindow, ResetControl, Rk3588PcieHost, +use rk3588_pci::{Delay, HostConfig, IatuMode, ResetControl, Rk3588PcieHost}; + +use super::{ + clocks_reset_gpio::{ + assert_resets, clock_specs, deassert_resets, enable_clocks, parse_reset_gpio, parse_resets, + }, + phy::{init_phys, parse_phys}, + register_fdt_legacy_irq, + windows::{ + align_up_4k, bus_range_info, config_window, is_config_range, live_fdt, log_direct_endpoint, + log_resource_summary, program_memory_windows, prop_phandle, set_rk3588_bar_range, + }, }; +use crate::soc::{RockchipPinCtrl, rk3588_enable_power_domain}; -use crate::soc::{ - RockchipPinCtrl, rk3588_enable_clock, rk3588_enable_power_domain, rk3588_reset_assert, - rk3588_reset_deassert, rk3588_set_clock_rate, -}; - -const RK3588_GPIO_BASES: [u64; 5] = [ +pub(super) const RK3588_GPIO_BASES: [u64; 5] = [ 0xfd8a_0000, 0xfec2_0000, 0xfec3_0000, @@ -40,15 +46,15 @@ const RK3588_GPIO_SWPORT_DR_H: usize = 0x04; const RK3588_GPIO_SWPORT_DDR_L: usize = 0x08; const RK3588_GPIO_SWPORT_DDR_H: usize = 0x0c; const RK3588_PCIE_PERST_INACTIVE_MS: u64 = 200; -const DEFAULT_CFG_SIZE: u64 = 0x10_0000; -const PHY_TYPE_PCIE: u32 = 2; -const RK3588_PCIE3PHY_DEFAULT_MODE: u32 = 4; -const RK3588_PCIE3PHY_CMN_CON0: usize = 0x000; -const RK3588_PCIE3PHY_PHY0_STATUS1: usize = 0x904; -const RK3588_PCIE3PHY_PHY1_STATUS1: usize = 0xa04; -const PHP_GRF_PCIESEL_CON: usize = 0x100; -const PCIE3PHY_SRAM_INIT_DONE: u32 = 1; -const BIT_WRITEABLE_SHIFT: u32 = 16; +pub(super) const DEFAULT_CFG_SIZE: u64 = 0x10_0000; +pub(super) const PHY_TYPE_PCIE: u32 = 2; +pub(super) const RK3588_PCIE3PHY_DEFAULT_MODE: u32 = 4; +pub(super) const RK3588_PCIE3PHY_CMN_CON0: usize = 0x000; +pub(super) const RK3588_PCIE3PHY_PHY0_STATUS1: usize = 0x904; +pub(super) const RK3588_PCIE3PHY_PHY1_STATUS1: usize = 0xa04; +pub(super) const PHP_GRF_PCIESEL_CON: usize = 0x100; +pub(super) const PCIE3PHY_SRAM_INIT_DONE: u32 = 1; +pub(super) const BIT_WRITEABLE_SHIFT: u32 = 16; const RK3588_PCIE_MAX_HOSTS: u32 = 8; static PROBED_HOST_MASK: AtomicU32 = AtomicU32::new(0); @@ -123,13 +129,13 @@ impl ResetControl for Rk3588GpioReset { } } -struct RegMmio { +pub(super) struct RegMmio { mmio: MmioRaw, size: usize, } impl RegMmio { - fn map_phandle(phandle: Phandle, context: &str) -> Result { + pub(super) fn map_phandle(phandle: Phandle, context: &str) -> Result { let fdt = live_fdt()?; let node = fdt.get_by_phandle(phandle).ok_or_else(|| { OnProbeError::other(format!("{context} phandle {phandle:?} not found")) @@ -140,96 +146,97 @@ impl RegMmio { Self::map_reg(reg) } - fn map_reg(reg: RegFixed) -> Result { + pub(super) fn map_reg(reg: RegFixed) -> Result { let size = align_up_4k((reg.size.unwrap_or(0x1000) as usize).max(1)); let mmio = map_mmio(reg.address, size)?; Ok(Self { mmio, size }) } - fn read32(&self, offset: usize) -> u32 { + pub(super) fn read32(&self, offset: usize) -> u32 { debug_assert!(offset + core::mem::size_of::() <= self.size); self.mmio.read::(offset) } - fn write32(&self, offset: usize, value: u32) { + pub(super) fn write32(&self, offset: usize, value: u32) { debug_assert!(offset + core::mem::size_of::() <= self.size); self.mmio.write::(offset, value); } - fn update32(&self, offset: usize, mask: u32, value: u32) { + pub(super) fn update32(&self, offset: usize, mask: u32, value: u32) { let current = self.read32(offset); self.write32(offset, (current & !mask) | value); } } #[derive(Clone)] -struct ClockSpec { - name: Option, - id: u32, - assigned_rate: Option, +pub(super) struct ClockSpec { + pub(super) name: Option, + pub(super) id: u32, + pub(super) assigned_rate: Option, } #[derive(Clone)] -struct ResetSpec { - name: Option, - id: u64, +pub(super) struct ResetSpec { + pub(super) name: Option, + pub(super) id: u64, } #[derive(Clone, Copy)] -struct GpioSpec { - bank: u8, - pin: u8, - active_high: bool, +pub(super) struct GpioSpec { + pub(super) bank: u8, + pub(super) pin: u8, + pub(super) active_high: bool, } -struct HostResources<'a> { - name: String, - node: NodeType<'a>, - apb: RegFixed, - dbi: RegFixed, - cfg_phys: u64, - cfg_size: u64, - ranges: Vec, - bus_base: u8, - logical_bus_end: u8, - power_domains: Vec, - clocks: Vec, - resets: Vec, - pipe_grf: Option, - reset_gpio: Option, - supply: Option, - phys: Vec, +pub(super) struct HostResources<'a> { + pub(super) name: String, + pub(super) node: NodeType<'a>, + pub(super) apb: RegFixed, + pub(super) dbi: RegFixed, + pub(super) cfg_phys: u64, + pub(super) cfg_size: u64, + pub(super) ranges: Vec, + pub(super) bus_base: u8, + pub(super) logical_bus_end: u8, + pub(super) power_domains: Vec, + pub(super) clocks: Vec, + pub(super) resets: Vec, + pub(super) pipe_grf: Option, + pub(super) reset_gpio: Option, + pub(super) supply: Option, + pub(super) phys: Vec, } #[derive(Clone)] -struct PhyRef { - phandle: Phandle, - specifier: Vec, - name: Option, +pub(super) struct PhyRef { + pub(super) phandle: Phandle, + pub(super) specifier: Vec, + pub(super) name: Option, } -struct Pcie3PhyResources { - name: String, - reg: RegFixed, - phy_grf: Phandle, - pipe_grf: Option, - pcie30_phymode: u32, - clocks: Vec, - resets: Vec, +pub(super) struct Pcie3PhyResources { + pub(super) name: String, + pub(super) reg: RegFixed, + pub(super) phy_grf: Phandle, + pub(super) pipe_grf: Option, + pub(super) pcie30_phymode: u32, + pub(super) clocks: Vec, + pub(super) resets: Vec, } -struct CombphyResources { - name: String, - reg: RegFixed, - pipe_grf: Phandle, - pipe_phy_grf: Phandle, - pcie1ln_sel_bits: Option<[u32; 4]>, - refclk_rate: u32, - clocks: Vec, - resets: Vec, +pub(super) struct CombphyResources { + pub(super) name: String, + pub(super) reg: RegFixed, + pub(super) pipe_grf: Phandle, + pub(super) pipe_phy_grf: Phandle, + pub(super) pcie1ln_sel_bits: Option<[u32; 4]>, + pub(super) refclk_rate: u32, + pub(super) clocks: Vec, + pub(super) resets: Vec, } -fn probe_rk3588(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +pub(super) fn probe_rk3588(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (info, plat_dev) = probe.into_parts(); let NodeType::Pci(node) = info.node else { return Err(OnProbeError::NotMatch); }; @@ -288,7 +295,7 @@ fn probe_rk3588(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnPro host.apb_phys() ); log_direct_endpoint(&host); - super::register_fdt_legacy_irq(&info, resources.logical_bus_end); + register_fdt_legacy_irq(&info, resources.logical_bus_end); let mut drv = PcieController::new(host); for range in &resources.ranges { diff --git a/drivers/ax-driver/src/pci/rk3588/slots.rs b/drivers/ax-driver/src/pci/fdt/rk3588/slots.rs similarity index 75% rename from drivers/ax-driver/src/pci/rk3588/slots.rs rename to drivers/ax-driver/src/pci/fdt/rk3588/slots.rs index 6d08f48839..2054ee22c8 100644 --- a/drivers/ax-driver/src/pci/rk3588/slots.rs +++ b/drivers/ax-driver/src/pci/fdt/rk3588/slots.rs @@ -1,3 +1,10 @@ +use rdrive::{ + probe::OnProbeError, + register::{ProbeFdt, ProbeKind, ProbeLevel, ProbePriority}, +}; + +use super::resources::probe_rk3588; + mod rk3588_pcie_slot0 { use super::*; @@ -13,8 +20,8 @@ mod rk3588_pcie_slot0 { ], ); - fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { - probe_rk3588(info, plat_dev) + fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + probe_rk3588(probe) } } @@ -33,8 +40,8 @@ mod rk3588_pcie_slot1 { ], ); - fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { - probe_rk3588(info, plat_dev) + fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + probe_rk3588(probe) } } @@ -53,8 +60,8 @@ mod rk3588_pcie_slot2 { ], ); - fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { - probe_rk3588(info, plat_dev) + fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + probe_rk3588(probe) } } @@ -73,8 +80,8 @@ mod rk3588_pcie_slot3 { ], ); - fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { - probe_rk3588(info, plat_dev) + fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + probe_rk3588(probe) } } @@ -93,7 +100,7 @@ mod rk3588_pcie_slot4 { ], ); - fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { - probe_rk3588(info, plat_dev) + fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + probe_rk3588(probe) } } diff --git a/drivers/ax-driver/src/pci/rk3588/windows.rs b/drivers/ax-driver/src/pci/fdt/rk3588/windows.rs similarity index 76% rename from drivers/ax-driver/src/pci/rk3588/windows.rs rename to drivers/ax-driver/src/pci/fdt/rk3588/windows.rs index 1fa87f9b7f..c25b490f31 100644 --- a/drivers/ax-driver/src/pci/rk3588/windows.rs +++ b/drivers/ax-driver/src/pci/fdt/rk3588/windows.rs @@ -1,4 +1,21 @@ -fn log_resource_summary(resources: &HostResources<'_>) { +use alloc::{ + format, + string::{String, ToString}, + vec::Vec, +}; + +use fdt_edit::{Fdt, Node, PciRange, PciSpace, Phandle, RegFixed}; +use log::{debug, info, warn}; +use rdif_pcie::{PciMem64, PcieController}; +use rdrive::probe::OnProbeError; +use rk3588_pci::{MEM_ATU_FIRST_REGION, OutboundWindow, Rk3588PcieHost}; + +use super::{ + resources::{DEFAULT_CFG_SIZE, GpioSpec, HostResources}, + set_pcie_mem_range, +}; + +pub(super) fn log_resource_summary(resources: &HostResources<'_>) { info!( "Rockchip RK3588 PCIe host {:#x}: FDT resources node={}, dbi={:#x}/{:#x}, \ cfg={:#x}/{:#x}, buses {:#x}..={:#x}, clocks={}, resets={}, power-domains={}, phys={}, \ @@ -39,11 +56,11 @@ fn reset_gpio_label(gpio: Option) -> String { } } -fn is_compatible(node: &Node, compatible: &str) -> bool { +pub(super) fn is_compatible(node: &Node, compatible: &str) -> bool { node.compatibles().any(|item| item == compatible) } -fn phy_cells(phandle: Phandle) -> Result { +pub(super) fn phy_cells(phandle: Phandle) -> Result { let fdt = live_fdt()?; let phy = fdt .get_by_phandle(phandle) @@ -60,39 +77,39 @@ fn phy_cells(phandle: Phandle) -> Result { }) } -fn prop_phandle(node: &Node, prop_name: &str) -> Option { +pub(super) fn prop_phandle(node: &Node, prop_name: &str) -> Option { node.get_property(prop_name) .and_then(|prop| prop.get_u32()) .map(Phandle::from) } -fn prop_u32(node: &Node, prop_name: &str) -> Option { +pub(super) fn prop_u32(node: &Node, prop_name: &str) -> Option { node.get_property(prop_name).and_then(|prop| prop.get_u32()) } -fn prop_str_list(node: &Node, prop_name: &str) -> Vec { +pub(super) fn prop_str_list(node: &Node, prop_name: &str) -> Vec { node.get_property(prop_name) .map(|prop| prop.as_str_iter().map(|s| s.to_string()).collect()) .unwrap_or_default() } -fn live_fdt() -> Result { +pub(super) fn live_fdt() -> Result { rdrive::with_fdt(Clone::clone).ok_or_else(|| OnProbeError::other("live FDT not found")) } -fn align_up_4k(size: usize) -> usize { +pub(super) fn align_up_4k(size: usize) -> usize { const MASK: usize = 0xfff; (size + MASK) & !MASK } #[derive(Clone, Copy)] -struct Rk3588ResetPin { - bank: u8, - pin: u8, - active_high: bool, +pub(super) struct Rk3588ResetPin { + pub(super) bank: u8, + pub(super) pin: u8, + pub(super) active_high: bool, } -fn rk3588_pcie_reset_pin(apb_base: u64) -> Option { +pub(super) fn rk3588_pcie_reset_pin(apb_base: u64) -> Option { match apb_base { 0xfe18_0000 => Some(Rk3588ResetPin { bank: 3, @@ -108,7 +125,10 @@ fn rk3588_pcie_reset_pin(apb_base: u64) -> Option { } } -fn config_window(regs: &[RegFixed], ranges: &[PciRange]) -> Result<(u64, u64), OnProbeError> { +pub(super) fn config_window( + regs: &[RegFixed], + ranges: &[PciRange], +) -> Result<(u64, u64), OnProbeError> { if let Some(reg) = regs.get(2) { return Ok((reg.address, reg.size.unwrap_or(DEFAULT_CFG_SIZE))); } @@ -124,7 +144,7 @@ fn config_window(regs: &[RegFixed], ranges: &[PciRange]) -> Result<(u64, u64), O .ok_or_else(|| OnProbeError::other("RK3588 PCIe host has no config window")) } -fn bus_range_info(bus_range: Option>) -> (u8, u8) { +pub(super) fn bus_range_info(bus_range: Option>) -> (u8, u8) { let Some(bus_range) = bus_range else { return (0, u8::MAX); }; @@ -136,7 +156,7 @@ fn bus_range_info(bus_range: Option>) -> (u8, u8) { (bus_base, logical_end) } -fn program_memory_windows( +pub(super) fn program_memory_windows( host: &Rk3588PcieHost, ranges: &[PciRange], cfg_phys: u64, @@ -176,7 +196,7 @@ fn program_memory_windows( } } -fn log_direct_endpoint(host: &Rk3588PcieHost) { +pub(super) fn log_direct_endpoint(host: &Rk3588PcieHost) { if let Some(endpoint) = host.direct_endpoint_info() { info!( "PCIe endpoint: {} {:04x}:{:04x} (rev {:02x}, class {:02x}{:02x}{:02x})", @@ -191,12 +211,12 @@ fn log_direct_endpoint(host: &Rk3588PcieHost) { } } -fn is_config_range(range: &PciRange, cfg_phys: u64, cfg_size: u64) -> bool { +pub(super) fn is_config_range(range: &PciRange, cfg_phys: u64, cfg_size: u64) -> bool { range.cpu_address == cfg_phys && range.size == cfg_size } -fn set_rk3588_bar_range(drv: &mut PcieController, range: &PciRange) { - super::set_pcie_mem_range(drv, range); +pub(super) fn set_rk3588_bar_range(drv: &mut PcieController, range: &PciRange) { + set_pcie_mem_range(drv, range); if matches!(range.space, PciSpace::Memory32) { drv.set_mem64( PciMem64 { diff --git a/drivers/ax-driver/src/pci/mod.rs b/drivers/ax-driver/src/pci/mod.rs index a4cd963b37..cf8b9a6f01 100644 --- a/drivers/ax-driver/src/pci/mod.rs +++ b/drivers/ax-driver/src/pci/mod.rs @@ -12,7 +12,7 @@ use rdrive::{ PlatformDevice, probe::{ OnProbeError, - pci::{PciAddress, PciMem32, PciMem64}, + pci::{PciAddress, PciInfo, PciMem32, PciMem64}, }, }; #[cfg(virtio_dev)] @@ -35,9 +35,9 @@ use crate::virtio::VirtIoHalImpl; mod acpi; #[cfg(plat_dyn)] mod fdt; -#[cfg(pci_dyn_acpi_intx_route)] +#[cfg(plat_dyn)] pub(crate) use acpi::acpi_irq_for_endpoint; -#[cfg(pci_dyn_intx_route)] +#[cfg(plat_dyn)] pub(crate) use fdt::fdt_irq_for_endpoint; const MAX_PCIE_LEGACY_IRQS: usize = 8; @@ -76,10 +76,11 @@ impl LegacyIrqRoute { && self.irqs[..irq_count] == irq_list[..irq_count] } - fn irq_for(&self, address: PciAddress, interrupt_pin: u8) -> Option { - if address.bus() < self.bus_start - || address.bus() > self.bus_end - || !(1..=PCI_INTX_LINES as u8).contains(&interrupt_pin) + fn irq_for(&self, info: PciInfo) -> Option { + let route = info.intx_route?; + if info.address.bus() < self.bus_start + || info.address.bus() > self.bus_end + || !(1..=PCI_INTX_LINES as u8).contains(&route.root_pin) { return None; } @@ -88,7 +89,7 @@ impl LegacyIrqRoute { let route_index = if irq_count == 1 { 0 } else { - (usize::from(address.device()) + usize::from(interrupt_pin) - 1) % irq_count + (usize::from(route.root_device) + usize::from(route.root_pin) - 1) % irq_count }; Some(self.irqs[route_index]) } @@ -99,6 +100,13 @@ static LEGACY_IRQ_ROUTES: SpinMutex bool { cfg!(any( feature = "ahci", @@ -195,33 +203,111 @@ pub fn register_ecam_controller_with_mmio_op( Ok(()) } -pub fn legacy_irq_for_endpoint(address: PciAddress, interrupt_pin: u8) -> Option { +pub fn resolve_intx_irq(info: PciInfo) -> Result, OnProbeError> { + #[cfg(plat_dyn)] + { + resolve_intx_irq_with_resolvers( + info, + dynamic_pci_irq_source(), + crate::pci::acpi_irq_for_endpoint, + crate::pci::fdt_irq_for_endpoint, + legacy_irq_for_endpoint, + interrupt_line_irq, + ) + } + + #[cfg(not(plat_dyn))] + { + if let Some(irq) = legacy_irq_for_endpoint(info) { + return Ok(Some(irq)); + } + + Ok(interrupt_line_irq(info.interrupt_line)) + } +} + +#[cfg(any(plat_dyn, test))] +fn resolve_intx_irq_with_resolvers( + info: PciInfo, + dynamic_source: Option, + acpi_irq: impl FnOnce(PciInfo) -> Result, OnProbeError>, + fdt_irq: impl FnOnce(PciInfo) -> Result, OnProbeError>, + legacy_irq: impl FnOnce(PciInfo) -> Option, + interrupt_line: impl FnOnce(u8) -> Option, +) -> Result, OnProbeError> { + match dynamic_source { + Some(DynamicPciIrqSource::Acpi) => { + if info.intx_route.is_none() { + return Ok(None); + } + return acpi_irq(info); + } + Some(DynamicPciIrqSource::Fdt) => { + if info.intx_route.is_none() { + return Ok(None); + } + return fdt_irq(info); + } + None => {} + } + + if let Some(irq) = legacy_irq(info) { + return Ok(Some(irq)); + } + + Ok(interrupt_line(info.interrupt_line)) +} + +#[cfg(plat_dyn)] +fn dynamic_pci_irq_source() -> Option { + select_dynamic_pci_irq_source( + rdrive::probe::acpi::with_acpi(|_| ()).is_some(), + rdrive::with_fdt(|_| ()).is_some(), + ) +} + +#[cfg(any(plat_dyn, test))] +fn select_dynamic_pci_irq_source(has_acpi: bool, has_fdt: bool) -> Option { + if has_acpi { + Some(DynamicPciIrqSource::Acpi) + } else if has_fdt { + Some(DynamicPciIrqSource::Fdt) + } else { + None + } +} + +pub fn legacy_irq_for_endpoint(info: PciInfo) -> Option { LEGACY_IRQ_ROUTES .lock() .iter() - .find_map(|route| route.irq_for(address, interrupt_pin)) + .find_map(|route| route.irq_for(info)) } pub fn legacy_irq_for_address(address: PciAddress) -> Option { - legacy_irq_for_endpoint(address, 1) + legacy_irq_for_endpoint(PciInfo { + address, + interrupt_pin: 1, + interrupt_line: 0, + intx_route: Some(rdrive::probe::pci::PciIntxRoute { + root_device: address.device(), + root_function: address.function(), + root_pin: 1, + }), + }) } -pub fn endpoint_legacy_irq(endpoint: &rdrive::probe::pci::EndpointRc) -> Option { - if let Some(irq) = legacy_irq_for_endpoint(endpoint.address(), endpoint.interrupt_pin()) { - return Some(irq); - } +pub(crate) const fn legacy_line_to_irq(line: u8) -> usize { + legacy_line_to_irq_for_platform(line, cfg!(target_arch = "x86_64"), cfg!(plat_dyn)) +} - let line = endpoint.interrupt_line(); +fn interrupt_line_irq(line: u8) -> Option { if line == 0 || line == u8::MAX { return None; } Some(legacy_line_to_irq(line)) } -pub(crate) const fn legacy_line_to_irq(line: u8) -> usize { - legacy_line_to_irq_for_platform(line, cfg!(target_arch = "x86_64"), cfg!(plat_dyn)) -} - const fn legacy_line_to_irq_for_platform(line: u8, is_x86_64: bool, is_plat_dyn: bool) -> usize { let base = if is_x86_64 { if is_plat_dyn { 0x30 } else { 0x20 } @@ -234,7 +320,98 @@ const fn legacy_line_to_irq_for_platform(line: u8, is_x86_64: bool, is_plat_dyn: #[cfg(test)] mod tests { - use super::legacy_line_to_irq_for_platform; + use alloc::string::ToString; + use core::cell::Cell; + + #[cfg(plat_dyn)] + use axklib::{ + AxError, AxResult, IrqCpuMask, IrqHandle, Klib, PhysAddr, RawIrqHandler, VirtAddr, + impl_trait, + }; + use rdrive::probe::{ + OnProbeError, + pci::{PciAddress, PciInfo, PciIntxRoute}, + }; + + use super::{ + DynamicPciIrqSource, LegacyIrqRoute, legacy_line_to_irq_for_platform, + resolve_intx_irq_with_resolvers, select_dynamic_pci_irq_source, + }; + + #[cfg(plat_dyn)] + struct KlibImpl; + + #[cfg(plat_dyn)] + impl_trait! { + impl Klib for KlibImpl { + fn mem_iomap(_addr: PhysAddr, _size: usize) -> AxResult { + Err(AxError::Unsupported) + } + + fn mem_virt_to_phys(addr: VirtAddr) -> PhysAddr { + PhysAddr::from_usize(addr.as_usize()) + } + + fn mem_make_dma_coherent_uncached(_addr: VirtAddr, _size: usize) -> AxResult { + Err(AxError::Unsupported) + } + + fn mem_restore_dma_cached(_addr: VirtAddr, _size: usize) -> AxResult { + Err(AxError::Unsupported) + } + + fn dma_alloc_pages( + _dma_mask: u64, + _num_pages: usize, + _align: usize, + ) -> AxResult { + Err(AxError::Unsupported) + } + + fn dma_dealloc_pages(_addr: VirtAddr, _num_pages: usize) {} + + fn time_busy_wait(_dur: core::time::Duration) {} + + fn time_monotonic_nanos() -> u64 { + 0 + } + + fn time_try_init_epoch_offset(_epoch_time_nanos: u64) -> bool { + false + } + + fn irq_set_enable(_irq: usize, _enabled: bool) {} + + fn irq_request_shared( + _irq: usize, + _handler: RawIrqHandler, + _data: core::ptr::NonNull<()>, + ) -> AxResult { + Err(AxError::Unsupported) + } + + fn irq_request_percpu( + _irq: usize, + _cpus: IrqCpuMask, + _handler: RawIrqHandler, + _data: core::ptr::NonNull<()>, + ) -> AxResult { + Err(AxError::Unsupported) + } + + fn irq_free(_handle: IrqHandle) -> AxResult { + Err(AxError::Unsupported) + } + + fn irq_enable(_handle: IrqHandle) -> AxResult { + Err(AxError::Unsupported) + } + + fn irq_disable(_handle: IrqHandle) -> AxResult { + Err(AxError::Unsupported) + } + } + } #[test] fn x86_64_legacy_line_uses_dynamic_ioapic_base_on_plat_dyn() { @@ -247,8 +424,227 @@ mod tests { assert_eq!(legacy_line_to_irq_for_platform(9, false, false), 9); assert_eq!(legacy_line_to_irq_for_platform(9, false, true), 9); } -} + #[test] + fn legacy_route_uses_swizzled_root_device_and_pin() { + let route = LegacyIrqRoute::from_irqs(0, 8, &[40, 41, 42, 43]).unwrap(); + let info = PciInfo { + address: PciAddress::new(0, 2, 7, 0), + interrupt_pin: 1, + interrupt_line: 0, + intx_route: Some(PciIntxRoute { + root_device: 2, + root_function: 0, + root_pin: 4, + }), + }; + + assert_eq!(route.irq_for(info), Some(41)); + } + + #[test] + fn legacy_route_ignores_endpoints_without_intx_route() { + let route = LegacyIrqRoute::from_irqs(0, 8, &[40, 41, 42, 43]).unwrap(); + let info = PciInfo { + address: PciAddress::new(0, 2, 7, 0), + interrupt_pin: 1, + interrupt_line: 0, + intx_route: None, + }; + + assert_eq!(route.irq_for(info), None); + } + + #[test] + fn resolve_intx_irq_source_prefers_acpi_when_both_backends_exist() { + assert_eq!( + select_dynamic_pci_irq_source(true, true), + Some(DynamicPciIrqSource::Acpi) + ); + } + + #[test] + fn resolve_intx_irq_acpi_error_does_not_fallback_to_fdt_or_legacy() { + let info = endpoint_with_intx_route(); + let fdt_called = Cell::new(false); + let legacy_called = Cell::new(false); + let line_called = Cell::new(false); + + let err = resolve_intx_irq_with_resolvers( + info, + Some(DynamicPciIrqSource::Acpi), + |_| Err(OnProbeError::other("acpi irq failed")), + |_| { + fdt_called.set(true); + Ok(Some(55)) + }, + |_| { + legacy_called.set(true); + Some(66) + }, + |_| { + line_called.set(true); + Some(77) + }, + ) + .unwrap_err(); + + assert!(err.to_string().contains("acpi irq failed")); + assert!(!fdt_called.get()); + assert!(!legacy_called.get()); + assert!(!line_called.get()); + } + + #[test] + fn resolve_intx_irq_fdt_none_does_not_fallback_to_legacy_or_interrupt_line() { + let info = endpoint_with_intx_route(); + let acpi_called = Cell::new(false); + let legacy_called = Cell::new(false); + let line_called = Cell::new(false); + + let irq = resolve_intx_irq_with_resolvers( + info, + Some(DynamicPciIrqSource::Fdt), + |_| { + acpi_called.set(true); + Ok(Some(44)) + }, + |_| Ok(None), + |_| { + legacy_called.set(true); + Some(66) + }, + |_| { + line_called.set(true); + Some(77) + }, + ) + .unwrap(); + + assert_eq!(irq, None); + assert!(!acpi_called.get()); + assert!(!legacy_called.get()); + assert!(!line_called.get()); + } + + #[test] + fn resolve_intx_irq_acpi_none_does_not_fallback_to_legacy_or_interrupt_line() { + let info = endpoint_with_intx_route(); + let fdt_called = Cell::new(false); + let legacy_called = Cell::new(false); + let line_called = Cell::new(false); + + let irq = resolve_intx_irq_with_resolvers( + info, + Some(DynamicPciIrqSource::Acpi), + |_| Ok(None), + |_| { + fdt_called.set(true); + Ok(Some(55)) + }, + |_| { + legacy_called.set(true); + Some(66) + }, + |_| { + line_called.set(true); + Some(77) + }, + ) + .unwrap(); + + assert_eq!(irq, None); + assert!(!fdt_called.get()); + assert!(!legacy_called.get()); + assert!(!line_called.get()); + } + + #[test] + fn resolve_intx_irq_dynamic_source_without_intx_route_does_not_use_interrupt_line() { + let info = PciInfo { + intx_route: None, + ..endpoint_with_intx_route() + }; + let acpi_called = Cell::new(false); + let fdt_called = Cell::new(false); + let legacy_called = Cell::new(false); + let line_called = Cell::new(false); + + let irq = resolve_intx_irq_with_resolvers( + info, + Some(DynamicPciIrqSource::Acpi), + |_| { + acpi_called.set(true); + Ok(Some(44)) + }, + |_| { + fdt_called.set(true); + Ok(Some(55)) + }, + |_| { + legacy_called.set(true); + Some(66) + }, + |_| { + line_called.set(true); + Some(77) + }, + ) + .unwrap(); + + assert_eq!(irq, None); + assert!(!acpi_called.get()); + assert!(!fdt_called.get()); + assert!(!legacy_called.get()); + assert!(!line_called.get()); + } + + #[test] + fn resolve_intx_irq_static_source_keeps_legacy_and_interrupt_line_fallback() { + let info = endpoint_with_intx_route(); + let acpi_called = Cell::new(false); + let fdt_called = Cell::new(false); + let line_called = Cell::new(false); + + let irq = resolve_intx_irq_with_resolvers( + info, + None, + |_| { + acpi_called.set(true); + Ok(Some(44)) + }, + |_| { + fdt_called.set(true); + Ok(Some(55)) + }, + |_| None, + |line| { + line_called.set(true); + assert_eq!(line, 9); + Some(77) + }, + ) + .unwrap(); + + assert_eq!(irq, Some(77)); + assert!(!acpi_called.get()); + assert!(!fdt_called.get()); + assert!(line_called.get()); + } + + fn endpoint_with_intx_route() -> PciInfo { + PciInfo { + address: PciAddress::new(0, 2, 7, 0), + interrupt_pin: 1, + interrupt_line: 9, + intx_route: Some(PciIntxRoute { + root_device: 2, + root_function: 0, + root_pin: 1, + }), + } + } +} pub fn register_legacy_irq_route(bus_start: u8, bus_end: u8, irq: usize) { register_legacy_irq_routes(bus_start, bus_end, &[irq]); } diff --git a/drivers/ax-driver/src/pci/rk3588.rs b/drivers/ax-driver/src/pci/rk3588.rs deleted file mode 100644 index b63115928a..0000000000 --- a/drivers/ax-driver/src/pci/rk3588.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub(super) use super::{register_fdt_legacy_irq, set_pcie_mem_range}; - -mod body { - use log::{debug, info, warn}; - - include!("rk3588/resources.rs"); - include!("rk3588/clocks_reset_gpio.rs"); - include!("rk3588/phy.rs"); - include!("rk3588/windows.rs"); - include!("rk3588/slots.rs"); -} diff --git a/drivers/ax-driver/src/registration.rs b/drivers/ax-driver/src/registration.rs new file mode 100644 index 0000000000..73b276cb5f --- /dev/null +++ b/drivers/ax-driver/src/registration.rs @@ -0,0 +1,38 @@ +#[cfg(any(feature = "display", feature = "input", feature = "vsock"))] +use rdrive::Device; +use rdrive::DriverGeneric; + +use crate::BindingInfo; + +pub trait BoundDevice: DriverGeneric { + fn binding_info(&self) -> &BindingInfo; + + fn irq_num(&self) -> Option { + self.binding_info().irq_num() + } +} + +pub fn register_bound_device(plat_dev: rdrive::PlatformDevice, device: T) -> Option +where + T: BoundDevice, +{ + let irq = device.irq_num(); + plat_dev.register(device); + irq +} + +#[cfg(any(feature = "display", feature = "input", feature = "vsock"))] +pub trait TakeRegistered { + type Output; + + fn take_registered(&mut self) -> Option; +} + +#[cfg(any(feature = "display", feature = "input", feature = "vsock"))] +pub fn take_registered_device(device: Device) -> Option +where + T: TakeRegistered + 'static, +{ + let mut device = device.lock().ok()?; + device.take_registered() +} diff --git a/drivers/ax-driver/src/rknpu.rs b/drivers/ax-driver/src/rknpu.rs index 4bcc299bb5..de3978043b 100644 --- a/drivers/ax-driver/src/rknpu.rs +++ b/drivers/ax-driver/src/rknpu.rs @@ -1,7 +1,7 @@ use alloc::vec::Vec; use log::info; -use rdrive::{PlatformDevice, probe::OnProbeError, register::FdtInfo}; +use rdrive::{probe::OnProbeError, register::ProbeFdt}; use rockchip_npu::{Rknpu, RknpuConfig, RknpuType}; pub use rockchip_npu::{ RknpuAction, @@ -30,7 +30,8 @@ crate::model_register!( ], ); -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (info, plat_dev) = probe.into_parts(); let regs = info.node.regs(); let config = RknpuConfig { diff --git a/drivers/ax-driver/src/serial/mod.rs b/drivers/ax-driver/src/serial/mod.rs index c6947174c0..f93d7eb9ba 100644 --- a/drivers/ax-driver/src/serial/mod.rs +++ b/drivers/ax-driver/src/serial/mod.rs @@ -2,7 +2,7 @@ use alloc::{format, string::String}; use fdt_edit::{Fdt, NodeType, RegFixed, Status}; use log::{info, warn}; -use rdrive::{PlatformDevice, probe::OnProbeError, register::FdtInfo}; +use rdrive::{probe::OnProbeError, register::ProbeFdt}; use some_serial::{ BSerial, ns16550, ns16550::rockchip_fiq::{ROCKCHIP_FIQ_RK3588_UART_CLOCK, RockchipFiqConfig, RockchipFiqSerial}, @@ -29,7 +29,8 @@ crate::model_register!( }], ); -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (info, plat_dev) = probe.into_parts(); info!("Probing serial device: {}", info.node.name()); let base_reg = info.node.regs().into_iter().next().ok_or_else(|| { @@ -66,7 +67,8 @@ fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError Ok(()) } -fn probe_rockchip_fiq(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe_rockchip_fiq(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (info, plat_dev) = probe.into_parts(); let live_fdt = rdrive::with_fdt(Clone::clone).ok_or_else(|| OnProbeError::other("live FDT not found"))?; let fdt_config = rockchip_fiq_fdt_config(&live_fdt, info.node)?; diff --git a/drivers/ax-driver/src/soc/rockchip/clk/rk3568.rs b/drivers/ax-driver/src/soc/rockchip/clk/rk3568.rs index 1abcbe51d8..2f1c115c00 100644 --- a/drivers/ax-driver/src/soc/rockchip/clk/rk3568.rs +++ b/drivers/ax-driver/src/soc/rockchip/clk/rk3568.rs @@ -1,5 +1,5 @@ use log::info; -use rdrive::{PlatformDevice, probe::OnProbeError, register::FdtInfo}; +use rdrive::{probe::OnProbeError, register::ProbeFdt}; use rockchip_soc::{Cru, SocType}; use super::ClkDrv; @@ -20,7 +20,8 @@ crate::model_register!( ], ); -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (info, plat_dev) = probe.into_parts(); let base_reg = info .node .regs() diff --git a/drivers/ax-driver/src/soc/rockchip/clk/rk3588.rs b/drivers/ax-driver/src/soc/rockchip/clk/rk3588.rs index 85d5058876..b4f448ac16 100644 --- a/drivers/ax-driver/src/soc/rockchip/clk/rk3588.rs +++ b/drivers/ax-driver/src/soc/rockchip/clk/rk3588.rs @@ -13,7 +13,7 @@ // limitations under the License. use log::info; -use rdrive::{PlatformDevice, probe::OnProbeError, register::FdtInfo}; +use rdrive::{probe::OnProbeError, register::ProbeFdt}; use rockchip_soc::{Cru, SocType}; use super::ClkDrv; @@ -34,7 +34,8 @@ crate::model_register!( ], ); -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (info, plat_dev) = probe.into_parts(); let base_reg = info .node .regs() diff --git a/drivers/ax-driver/src/soc/rockchip/pinctrl.rs b/drivers/ax-driver/src/soc/rockchip/pinctrl.rs index ae36280d70..82bb8b7057 100644 --- a/drivers/ax-driver/src/soc/rockchip/pinctrl.rs +++ b/drivers/ax-driver/src/soc/rockchip/pinctrl.rs @@ -5,7 +5,7 @@ use core::ptr::NonNull; use fdt_edit::{Fdt, Node, NodeType, Phandle, RegFixed}; use log::{info, warn}; -use rdrive::{DriverGeneric, PlatformDevice, probe::OnProbeError, register::FdtInfo}; +use rdrive::{DriverGeneric, probe::OnProbeError, register::ProbeFdt}; use rockchip_soc::{ BankId, GpioDirection, Iomux, PinConfig, PinCtrl, PinCtrlOp, PinId, Pull, SocType, }; @@ -150,7 +150,8 @@ impl DriverGeneric for RockchipPinCtrl { } } -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (info, plat_dev) = probe.into_parts(); let fdt = live_fdt()?; let grf_phandle = info diff --git a/drivers/ax-driver/src/soc/rockchip/pm.rs b/drivers/ax-driver/src/soc/rockchip/pm.rs index 0b35cd2de6..644254d715 100644 --- a/drivers/ax-driver/src/soc/rockchip/pm.rs +++ b/drivers/ax-driver/src/soc/rockchip/pm.rs @@ -13,7 +13,7 @@ // limitations under the License. use log::info; -use rdrive::{PlatformDevice, probe::OnProbeError, register::FdtInfo}; +use rdrive::{probe::OnProbeError, register::ProbeFdt}; use rockchip_pm::{PowerDomain, RkBoard, RockchipPM}; use crate::mmio::iomap; @@ -30,7 +30,8 @@ crate::model_register!( ], ); -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (info, plat_dev) = probe.into_parts(); let base_reg = info .node .regs() diff --git a/drivers/ax-driver/src/soc/scmi.rs b/drivers/ax-driver/src/soc/scmi.rs index a46d4fde44..8d68e6e4b3 100644 --- a/drivers/ax-driver/src/soc/scmi.rs +++ b/drivers/ax-driver/src/soc/scmi.rs @@ -6,7 +6,7 @@ use ax_kspin::SpinNoIrq as Mutex; use fdt_edit::Phandle; use log::{info, warn}; -use crate::{DriverGeneric, PlatformDevice, mmio::iomap, probe::OnProbeError, register::FdtInfo}; +use crate::{DriverGeneric, mmio::iomap, probe::OnProbeError, register::ProbeFdt}; const SCMI_SHMEM_SIZE: usize = 0x100; const RK3588_SCMI_SHMEM_BASE: usize = 0x10f000; @@ -26,7 +26,8 @@ crate::model_register!( ], ); -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (info, plat_dev) = probe.into_parts(); let smc_id = info .node .as_node() diff --git a/drivers/ax-driver/src/soc/sg2002.rs b/drivers/ax-driver/src/soc/sg2002.rs index 30da06c141..0c21db461c 100644 --- a/drivers/ax-driver/src/soc/sg2002.rs +++ b/drivers/ax-driver/src/soc/sg2002.rs @@ -1,5 +1,5 @@ use log::info; -use rdrive::{PlatformDevice, probe::OnProbeError, register::FdtInfo}; +use rdrive::{probe::OnProbeError, register::ProbeFdt}; const PLACEHOLDER_COMPATIBLES: &[&str] = &[ "cvitek,tpu", @@ -35,7 +35,8 @@ crate::model_register!( }], ); -fn probe(info: FdtInfo<'_>, _plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let info = probe.info(); let mut mapped = 0usize; for node in info.find_compatible(PLACEHOLDER_COMPATIBLES) { for reg in node.regs() { diff --git a/drivers/ax-driver/src/time.rs b/drivers/ax-driver/src/time.rs index 44f2afd524..50604d1192 100644 --- a/drivers/ax-driver/src/time.rs +++ b/drivers/ax-driver/src/time.rs @@ -1,7 +1,10 @@ #[cfg(target_arch = "aarch64")] use ax_arm_pl031::Rtc as Pl031Rtc; use log::{debug, info}; -use rdrive::{PlatformDevice, probe::OnProbeError, register::FdtInfo}; +use rdrive::{ + probe::OnProbeError, + register::{FdtInfo, ProbeFdt}, +}; #[cfg(target_arch = "riscv64")] use riscv_goldfish::Rtc as GoldfishRtc; @@ -30,15 +33,17 @@ crate::model_register!( ); #[cfg(target_arch = "aarch64")] -fn probe_pl031(info: FdtInfo<'_>, _plat_dev: PlatformDevice) -> Result<(), OnProbeError> { - let mmio_base = map_first_reg(&info)?; +fn probe_pl031(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let info = probe.info(); + let mmio_base = map_first_reg(info)?; let rtc = unsafe { Pl031Rtc::new(mmio_base.as_ptr().cast()) }; init_epoch_offset(info.node.name(), u64::from(rtc.get_unix_timestamp())) } #[cfg(target_arch = "riscv64")] -fn probe_goldfish(info: FdtInfo<'_>, _plat_dev: PlatformDevice) -> Result<(), OnProbeError> { - let mmio_base = map_first_reg(&info)?; +fn probe_goldfish(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let info = probe.info(); + let mmio_base = map_first_reg(info)?; let rtc = GoldfishRtc::new(mmio_base.as_ptr() as usize); init_epoch_offset(info.node.name(), rtc.get_unix_timestamp()) } diff --git a/drivers/ax-driver/src/usb/dwc.rs b/drivers/ax-driver/src/usb/dwc.rs index 1e5b5344af..a47ebbff7b 100644 --- a/drivers/ax-driver/src/usb/dwc.rs +++ b/drivers/ax-driver/src/usb/dwc.rs @@ -13,10 +13,13 @@ use crab_usb::{ }; use fdt_edit::{ClockRef, Fdt, Node, NodeType, Phandle, RegFixed}; use log::{debug, info, warn}; -use rdrive::{PlatformDevice, probe::OnProbeError, register::FdtInfo}; +use rdrive::{ + probe::OnProbeError, + register::{FdtInfo, ProbeFdt}, +}; use rockchip_pm::{PowerDomain, RockchipPM}; -use super::{PlatformDeviceUsbHost, decode_fdt_irq, usb_kernel}; +use super::{ProbeFdtUsbHost, usb_kernel}; use crate::{ mmio::iomap, soc::{RockchipPinCtrl, rk3588_enable_clock, rk3588_reset_assert, rk3588_reset_deassert}, @@ -87,7 +90,6 @@ struct UsbdpPhyResources { struct DwcResources { ctrl: RegFixed, - irq_num: Option, power_domains: Vec, clocks: Vec, ctrl_resets: Vec, @@ -96,7 +98,8 @@ struct DwcResources { params: DwcParams, } -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let info = probe.info(); match prop_str(info.node.as_node(), "dr_mode") { Some("host") => {} Some(mode) => { @@ -113,7 +116,7 @@ fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError } let fdt = live_fdt()?; - let resources = collect_resources(&info, &fdt)?; + let resources = collect_resources(info, &fdt)?; enable_power_domains(&resources.power_domains)?; enable_clocks(&resources.clocks); @@ -168,11 +171,11 @@ fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError )) })?; - plat_dev.register_usb_host(DRIVER_NAME, host, resources.irq_num); + let node_name = probe.info().node.name().to_string(); + let irq = probe.register_usb_host(DRIVER_NAME, host)?; info!( "DWC xHCI driver initialized successfully for {} with irq {:?}", - info.node.name(), - resources.irq_num + node_name, irq ); Ok(()) } @@ -184,7 +187,6 @@ fn collect_resources(info: &FdtInfo<'_>, fdt: &Fdt) -> Result, fdt: &Fdt) -> Result &'static dyn crab_usb::KernelOp { pub struct PlatformUsbHost { name: &'static str, - irq_num: Option, + info: BindingInfo, host: USBHost, + irq_handler_taken: bool, } impl PlatformUsbHost { - fn new(name: &'static str, host: USBHost, irq_num: Option) -> Self { + fn new(name: &'static str, host: USBHost, info: BindingInfo) -> Self { Self { name, - irq_num, + info, host, + irq_handler_taken: false, } } @@ -150,7 +159,30 @@ impl PlatformUsbHost { } pub fn irq_num(&self) -> Option { - self.irq_num + self.info.irq_num() + } + + pub fn binding_info(&self) -> &BindingInfo { + &self.info + } + + pub fn enable_irq(&mut self) -> crab_usb::err::Result { + self.host.enable_irq() + } + + pub fn disable_irq(&mut self) -> crab_usb::err::Result { + self.host.disable_irq() + } + + pub fn take_irq_handler(&mut self) -> Option<(usize, UsbHostIrqHandler)> { + let irq = self.info.irq_num()?; + if self.irq_handler_taken { + return None; + } + + self.irq_handler_taken = true; + let handler = UsbHostIrqHandler::new(self.host.create_event_handler()); + Some((irq, handler)) } } @@ -160,66 +192,141 @@ impl DriverGeneric for PlatformUsbHost { } } +impl BoundDevice for PlatformUsbHost { + fn binding_info(&self) -> &BindingInfo { + &self.info + } +} + +pub struct UsbHostIrqHandler { + handler: EventHandler, +} + +impl UsbHostIrqHandler { + fn new(handler: EventHandler) -> Self { + Self { handler } + } + + pub fn handle(&self) -> crab_usb::Event { + self.handler.handle_event() + } +} + pub trait PlatformDeviceUsbHost { - fn register_usb_host(self, name: &'static str, host: USBHost, irq_num: Option); + fn register_usb_host(self, name: &'static str, host: USBHost) -> Option; + + fn register_usb_host_with_info( + self, + name: &'static str, + host: USBHost, + info: BindingInfo, + ) -> Option; } impl PlatformDeviceUsbHost for rdrive::PlatformDevice { - fn register_usb_host(self, name: &'static str, host: USBHost, irq_num: Option) { - self.register(PlatformUsbHost::new(name, host, irq_num)); + fn register_usb_host(self, name: &'static str, host: USBHost) -> Option { + self.register_usb_host_with_info(name, host, BindingInfo::empty()) + } + + fn register_usb_host_with_info( + self, + name: &'static str, + host: USBHost, + info: BindingInfo, + ) -> Option { + register_usb_host_with_info(self, name, host, info) } } -#[cfg(feature = "xhci-pci")] -pub(crate) fn align_up_4k(size: usize) -> usize { - const MASK: usize = 0xfff; - (size + MASK) & !MASK +pub trait ProbeFdtUsbHost { + fn register_usb_host( + self, + name: &'static str, + host: USBHost, + ) -> Result, OnProbeError>; } -pub fn decode_fdt_irq(interrupts: &[rdrive::probe::fdt::InterruptRef]) -> Option { - let interrupt = interrupts.first()?; - decode_irq_cells(&interrupt.specifier) +impl ProbeFdtUsbHost for rdrive::probe::fdt::ProbeFdt<'_> { + fn register_usb_host( + self, + name: &'static str, + host: USBHost, + ) -> Result, OnProbeError> { + let info = binding_info_from_fdt(self.info())?; + Ok(register_usb_host_with_info( + self.into_platform_device(), + name, + host, + info, + )) + } } -fn decode_irq_cells(specifier: &[u32]) -> Option { - match specifier { - [irq] => Some(*irq as usize), - [kind, irq, ..] => match *kind { - 0 => Some(*irq as usize + 32), - 1 => Some(*irq as usize + 16), - _ => Some(*irq as usize), - }, - _ => None, +pub trait ProbeAcpiUsbHost { + fn register_usb_host( + self, + name: &'static str, + host: USBHost, + ) -> Result, OnProbeError>; +} + +impl ProbeAcpiUsbHost for rdrive::probe::acpi::ProbeAcpi<'_> { + fn register_usb_host( + self, + name: &'static str, + host: USBHost, + ) -> Result, OnProbeError> { + let info = binding_info_from_acpi(self.info())?; + Ok(register_usb_host_with_info( + self.into_platform_device(), + name, + host, + info, + )) } } -#[cfg(feature = "xhci-pci")] -fn pci_static_irq(endpoint: &rdrive::probe::pci::EndpointRc) -> Option { - let interrupt_pin = endpoint.interrupt_pin(); - if let Some(irq) = crate::pci::legacy_irq_for_endpoint(endpoint.address(), interrupt_pin) { - return Some(irq); +#[cfg(feature = "pci")] +pub trait ProbePciUsbHost { + fn register_usb_host( + self, + name: &'static str, + host: USBHost, + requirement: PciIrqRequirement, + ) -> Result, OnProbeError>; +} + +#[cfg(feature = "pci")] +impl ProbePciUsbHost for rdrive::probe::pci::ProbePci<'_> { + fn register_usb_host( + self, + name: &'static str, + host: USBHost, + requirement: PciIrqRequirement, + ) -> Result, OnProbeError> { + let info = binding_info_from_pci(self.info(), requirement)?; + Ok(register_usb_host_with_info( + self.into_platform_device(), + name, + host, + info, + )) } - let line = endpoint.interrupt_line(); - (line != 0 && line != u8::MAX).then_some(crate::pci::legacy_line_to_irq(line)) +} + +fn register_usb_host_with_info( + plat_dev: rdrive::PlatformDevice, + name: &'static str, + host: USBHost, + info: BindingInfo, +) -> Option { + register_bound_device(plat_dev, PlatformUsbHost::new(name, host, info)) } #[cfg(feature = "xhci-pci")] -pub(crate) fn pci_irq_or_error( - endpoint: &rdrive::probe::pci::EndpointRc, -) -> Result { - #[cfg(pci_dyn_intx_route)] - if let Some(irq) = - crate::pci::fdt_irq_for_endpoint(endpoint.address(), endpoint.interrupt_pin())? - { - return Ok(irq); - } - - pci_static_irq(endpoint).ok_or_else(|| { - rdrive::probe::OnProbeError::other(alloc::format!( - "failed to resolve IRQ for USB endpoint {}", - endpoint.address() - )) - }) +pub(crate) fn align_up_4k(size: usize) -> usize { + const MASK: usize = 0xfff; + (size + MASK) & !MASK } pub fn usb_host_device() -> Option { diff --git a/drivers/ax-driver/src/usb/xhci_mmio.rs b/drivers/ax-driver/src/usb/xhci_mmio.rs index 53e78f54c9..eb73145b56 100644 --- a/drivers/ax-driver/src/usb/xhci_mmio.rs +++ b/drivers/ax-driver/src/usb/xhci_mmio.rs @@ -3,9 +3,9 @@ extern crate alloc; use alloc::format; use log::info; -use rdrive::{PlatformDevice, probe::OnProbeError, register::FdtInfo}; +use rdrive::{probe::OnProbeError, register::ProbeFdt}; -use super::{PlatformDeviceUsbHost, decode_fdt_irq, usb_kernel}; +use super::{ProbeFdtUsbHost, usb_kernel}; const DRIVER_NAME: &str = "usb-xhci-mmio"; @@ -19,28 +19,30 @@ crate::model_register!( }], ); -fn probe(info: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { - let base_reg = - info.node.regs().into_iter().next().ok_or_else(|| { - OnProbeError::other(alloc::format!("[{}] has no reg", info.node.name())) - })?; +fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let node_name = probe.info().node.name(); + let base_reg = probe + .info() + .node + .regs() + .into_iter() + .next() + .ok_or_else(|| OnProbeError::other(alloc::format!("[{}] has no reg", node_name)))?; let mmio_size = base_reg.size.unwrap_or(0x1000) as usize; let mmio = crate::mmio::iomap(base_reg.address as usize, mmio_size)?; - let irq_num = decode_fdt_irq(&info.interrupts()); let host = crab_usb::USBHost::new_xhci(mmio, usb_kernel()).map_err(|err| { OnProbeError::other(format!( "failed to create xHCI host for [{}]: {err}", - info.node.name() + node_name )) })?; - plat_dev.register_usb_host(DRIVER_NAME, host, irq_num); + let irq = probe.register_usb_host(DRIVER_NAME, host)?; info!( "xHCI MMIO host registered successfully for {} with irq {:?}", - info.node.name(), - irq_num + node_name, irq ); Ok(()) } diff --git a/drivers/ax-driver/src/usb/xhci_pci.rs b/drivers/ax-driver/src/usb/xhci_pci.rs index 418679dc45..33a283feec 100644 --- a/drivers/ax-driver/src/usb/xhci_pci.rs +++ b/drivers/ax-driver/src/usb/xhci_pci.rs @@ -4,15 +4,13 @@ use alloc::format; use log::info; use pcie::CommandRegister; -use rdrive::{ - PlatformDevice, - probe::{ - OnProbeError, - pci::{EndpointRc, FnOnProbe}, - }, +use rdrive::probe::{ + OnProbeError, + pci::{FnOnProbe, ProbePci}, }; -use super::{PlatformDeviceUsbHost, align_up_4k, pci_irq_or_error, usb_kernel}; +use super::{ProbePciUsbHost, align_up_4k, usb_kernel}; +use crate::PciIrqRequirement; const DRIVER_NAME: &str = "usb-xhci-pci"; @@ -25,7 +23,8 @@ crate::model_register!( }], ); -fn probe(endpoint: &mut EndpointRc, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe(mut probe: ProbePci<'_>) -> Result<(), OnProbeError> { + let endpoint = probe.endpoint_mut(); let class = endpoint.revision_and_class(); if (class.base_class, class.sub_class, class.interface) != (0x0c, 0x03, 0x30) { return Err(OnProbeError::NotMatch); @@ -41,19 +40,17 @@ fn probe(endpoint: &mut EndpointRc, plat_dev: PlatformDevice) -> Result<(), OnPr }); let mmio = crate::mmio::iomap(bar.start, align_up_4k(bar.count().max(1)))?; - let irq_num = Some(pci_irq_or_error(endpoint)?); + let address = endpoint.address(); let host = crab_usb::USBHost::new_xhci(mmio, usb_kernel()).map_err(|err| { OnProbeError::other(format!( - "failed to create xHCI host for PCI endpoint {}: {err}", - endpoint.address() + "failed to create xHCI host for PCI endpoint {address}: {err}", )) })?; - plat_dev.register_usb_host(DRIVER_NAME, host, irq_num); + let irq = probe.register_usb_host(DRIVER_NAME, host, PciIrqRequirement::Required)?; info!( "xHCI PCI host registered successfully at {} with irq {:?}", - endpoint.address(), - irq_num + address, irq ); Ok(()) } diff --git a/drivers/ax-driver/src/virtio/block.rs b/drivers/ax-driver/src/virtio/block.rs index 2dd81012f4..9418258cae 100644 --- a/drivers/ax-driver/src/virtio/block.rs +++ b/drivers/ax-driver/src/virtio/block.rs @@ -3,7 +3,7 @@ extern crate alloc; use alloc::format; use rdrive::{DriverGeneric, PlatformDevice, probe::OnProbeError}; -#[cfg(any(plat_dyn, plat_static))] +#[cfg(all(feature = "pci", any(plat_dyn, plat_static)))] use virtio_drivers::transport::DeviceType; use virtio_drivers::{ Error as VirtIoError, @@ -18,8 +18,8 @@ use crate::{ const VIRTIO_BLK_DMA_BUFFER_SIZE: usize = 32 * SECTOR_SIZE; -#[cfg(any(plat_static, plat_dyn))] -crate::model_register!( +#[cfg(all(feature = "pci", any(plat_static, plat_dyn)))] +model_register!( name: "VirtIO Block", level: ProbeLevel::PostKernel, priority: ProbePriority::DEFAULT, @@ -28,17 +28,14 @@ crate::model_register!( }], ); -#[cfg(any(plat_static, plat_dyn))] -fn probe_pci( - endpoint: &mut rdrive::probe::pci::EndpointRc, - plat_dev: PlatformDevice, -) -> Result<(), OnProbeError> { - let transport = crate::pci::take_virtio_transport(endpoint, DeviceType::Block)?; - register_transport(plat_dev, transport) +#[cfg(all(feature = "pci", any(plat_static, plat_dyn)))] +fn probe_pci(mut probe: rdrive::probe::pci::ProbePci<'_>) -> Result<(), OnProbeError> { + let transport = crate::pci::take_virtio_transport(probe.endpoint_mut(), DeviceType::Block)?; + register_transport(probe.into_platform_device(), transport) } #[cfg(plat_dyn)] -crate::model_register!( +model_register!( name: "VirtIO MMIO Block", level: ProbeLevel::PostKernel, priority: ProbePriority::DEFAULT, @@ -49,10 +46,8 @@ crate::model_register!( ); #[cfg(plat_dyn)] -fn probe_fdt( - info: rdrive::register::FdtInfo<'_>, - plat_dev: PlatformDevice, -) -> Result<(), OnProbeError> { +fn probe_fdt(probe: rdrive::register::ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (info, plat_dev) = probe.into_parts(); let (ty, transport) = crate::virtio::probe_fdt_mmio_device(&info)?; if ty != DeviceType::Block { return Err(OnProbeError::NotMatch); diff --git a/drivers/ax-driver/src/virtio/display.rs b/drivers/ax-driver/src/virtio/display.rs index cf09df0f0e..d07f2dfe06 100644 --- a/drivers/ax-driver/src/virtio/display.rs +++ b/drivers/ax-driver/src/virtio/display.rs @@ -4,13 +4,15 @@ use alloc::format; use rdif_display::{DisplayError, DisplayInfo, FrameBuffer, PixelFormat}; use rdrive::{DriverGeneric, PlatformDevice, probe::OnProbeError}; -#[cfg(any(plat_static, plat_dyn))] +#[cfg(all(feature = "pci", any(plat_static, plat_dyn)))] use virtio_drivers::transport::DeviceType; use virtio_drivers::{Error as VirtIoError, device::gpu::VirtIOGpu, transport::Transport}; -use crate::{display::PlatformDeviceDisplay, virtio::VirtIoHalImpl}; +use crate::{BindingInfo, display::PlatformDeviceDisplay, virtio::VirtIoHalImpl}; +#[cfg(all(feature = "pci", any(plat_static, plat_dyn)))] +use crate::{PciIrqRequirement, binding_info_from_pci}; -#[cfg(any(plat_static, plat_dyn))] +#[cfg(all(feature = "pci", any(plat_static, plat_dyn)))] crate::model_register!( name: "VirtIO GPU", level: ProbeLevel::PostKernel, @@ -20,23 +22,29 @@ crate::model_register!( }], ); -#[cfg(any(plat_static, plat_dyn))] -fn probe_pci( - endpoint: &mut rdrive::probe::pci::EndpointRc, +#[cfg(all(feature = "pci", any(plat_static, plat_dyn)))] +fn probe_pci(mut probe: rdrive::probe::pci::ProbePci<'_>) -> Result<(), OnProbeError> { + let transport = crate::pci::take_virtio_transport(probe.endpoint_mut(), DeviceType::GPU)?; + let info = binding_info_from_pci(probe.info(), PciIrqRequirement::Optional)?; + register_transport_with_info(probe.into_platform_device(), transport, info) +} + +pub fn register_transport( plat_dev: PlatformDevice, + transport: T, ) -> Result<(), OnProbeError> { - let transport = crate::pci::take_virtio_transport(endpoint, DeviceType::GPU)?; - register_transport(plat_dev, transport) + register_transport_with_info(plat_dev, transport, BindingInfo::empty()) } -pub fn register_transport( +pub fn register_transport_with_info( plat_dev: PlatformDevice, transport: T, + info: BindingInfo, ) -> Result<(), OnProbeError> { let dev = VirtIoDisplay::new(transport) .map_err(|err| OnProbeError::other(format!("failed to initialize virtio-gpu: {err:?}")))?; - plat_dev.register_display(dev); - log::info!("registered virtio GPU device"); + let irq = plat_dev.register_display_with_info(dev, info); + log::info!("registered virtio GPU device irq={irq:?}"); Ok(()) } diff --git a/drivers/ax-driver/src/virtio/input.rs b/drivers/ax-driver/src/virtio/input.rs index db93d0795e..594e50cc51 100644 --- a/drivers/ax-driver/src/virtio/input.rs +++ b/drivers/ax-driver/src/virtio/input.rs @@ -4,7 +4,7 @@ use alloc::{borrow::ToOwned, format, string::String}; use rdif_input::{AbsInfo, EventType, InputDeviceId, InputError, InputEvent}; use rdrive::{DriverGeneric, PlatformDevice, probe::OnProbeError}; -#[cfg(any(plat_static, plat_dyn))] +#[cfg(all(feature = "pci", any(plat_static, plat_dyn)))] use virtio_drivers::transport::DeviceType; use virtio_drivers::{ Error as VirtIoError, @@ -12,9 +12,11 @@ use virtio_drivers::{ transport::Transport, }; -use crate::{input::PlatformDeviceInput, virtio::VirtIoHalImpl}; +use crate::{BindingInfo, input::PlatformDeviceInput, virtio::VirtIoHalImpl}; +#[cfg(all(feature = "pci", any(plat_static, plat_dyn)))] +use crate::{PciIrqRequirement, binding_info_from_pci}; -#[cfg(any(plat_static, plat_dyn))] +#[cfg(all(feature = "pci", any(plat_static, plat_dyn)))] crate::model_register!( name: "VirtIO Input", level: ProbeLevel::PostKernel, @@ -24,24 +26,30 @@ crate::model_register!( }], ); -#[cfg(any(plat_static, plat_dyn))] -fn probe_pci( - endpoint: &mut rdrive::probe::pci::EndpointRc, +#[cfg(all(feature = "pci", any(plat_static, plat_dyn)))] +fn probe_pci(mut probe: rdrive::probe::pci::ProbePci<'_>) -> Result<(), OnProbeError> { + let transport = crate::pci::take_virtio_transport(probe.endpoint_mut(), DeviceType::Input)?; + let info = binding_info_from_pci(probe.info(), PciIrqRequirement::Optional)?; + register_transport_with_info(probe.into_platform_device(), transport, info) +} + +pub fn register_transport( plat_dev: PlatformDevice, + transport: T, ) -> Result<(), OnProbeError> { - let transport = crate::pci::take_virtio_transport(endpoint, DeviceType::Input)?; - register_transport(plat_dev, transport) + register_transport_with_info(plat_dev, transport, BindingInfo::empty()) } -pub fn register_transport( +pub fn register_transport_with_info( plat_dev: PlatformDevice, transport: T, + info: BindingInfo, ) -> Result<(), OnProbeError> { let dev = VirtIoInputDevice::new(transport).map_err(|err| { OnProbeError::other(format!("failed to initialize virtio-input: {err:?}")) })?; - plat_dev.register_input(dev); - log::info!("registered virtio input device"); + let irq = plat_dev.register_input_with_info(dev, info); + log::info!("registered virtio input device irq={irq:?}"); Ok(()) } diff --git a/drivers/ax-driver/src/virtio/net.rs b/drivers/ax-driver/src/virtio/net.rs index 4e07c37579..3c99f05935 100644 --- a/drivers/ax-driver/src/virtio/net.rs +++ b/drivers/ax-driver/src/virtio/net.rs @@ -7,12 +7,14 @@ use core::{ }; use ax_kernel_guard::NoPreemptIrqSave; -use rd_net::{DmaBuffer, Event, IRxQueue, ITxQueue, Interface, NetError, QueueConfig}; +use rd_net::{DmaBuffer, Event, IRxQueue, ITxQueue, NetError, QueueConfig}; use rdrive::{DriverGeneric, PlatformDevice, probe::OnProbeError}; -#[cfg(any(plat_static, plat_dyn))] +#[cfg(all(feature = "pci", any(plat_static, plat_dyn)))] use virtio_drivers::transport::DeviceType; use virtio_drivers::{Error as VirtIoError, device::net::VirtIONetRaw, transport::Transport}; +#[cfg(all(feature = "pci", any(plat_static, plat_dyn)))] +use crate::{PciIrqRequirement, binding_info_from_pci}; use crate::{ net::PlatformDeviceNet, virtio::{self, VirtIoHalImpl, VirtIoTransport}, @@ -21,7 +23,7 @@ use crate::{ const QUEUE_SIZE: usize = 64; const BUFFER_SIZE: usize = 2048; -#[cfg(any(plat_static, plat_dyn))] +#[cfg(all(feature = "pci", any(plat_static, plat_dyn)))] crate::model_register!( name: "VirtIO Net", level: ProbeLevel::PostKernel, @@ -305,39 +307,42 @@ struct RxInflight { len: usize, } -#[cfg(any(plat_static, plat_dyn))] -fn probe_pci( - endpoint: &mut rdrive::probe::pci::EndpointRc, - plat_dev: PlatformDevice, -) -> Result<(), OnProbeError> { - let irq = crate::net::pci_legacy_irq(endpoint); - let transport = crate::pci::take_virtio_transport(endpoint, DeviceType::Network)?; - register_transport_with_irq(plat_dev, transport, irq) +#[cfg(all(feature = "pci", any(plat_static, plat_dyn)))] +fn probe_pci(mut probe: rdrive::probe::pci::ProbePci<'_>) -> Result<(), OnProbeError> { + let transport = crate::pci::take_virtio_transport(probe.endpoint_mut(), DeviceType::Network)?; + register_pci_transport(probe, transport) } pub fn register_transport( plat_dev: PlatformDevice, transport: T, ) -> Result<(), OnProbeError> { - register_transport_with_irq(plat_dev, transport, None) + let net = make_net(transport)?; + let irq = plat_dev.register_net("virtio-net", net); + log::info!("registered virtio network device irq={irq:?}"); + Ok(()) } -pub fn register_transport_with_irq( - plat_dev: PlatformDevice, +#[cfg(all(feature = "pci", any(plat_static, plat_dyn)))] +fn register_pci_transport( + probe: rdrive::probe::pci::ProbePci<'_>, transport: T, - irq_num: Option, ) -> Result<(), OnProbeError> { - let mut net = VirtIoNetDevice::new(transport).map_err(|err| { + let info = binding_info_from_pci(probe.info(), PciIrqRequirement::Optional)?; + let net = make_net(transport)?; + let irq = probe + .into_platform_device() + .register_net_with_info("virtio-net", net, info); + log::info!("registered virtio network device irq={irq:?}"); + Ok(()) +} + +fn make_net(transport: T) -> Result, OnProbeError> { + VirtIoNetDevice::new(transport).map_err(|err| { OnProbeError::other(format!( "failed to initialize static VirtIO net device: {err:?}" )) - })?; - if irq_num.is_some() { - net.enable_irq(); - } - plat_dev.register_net("virtio-net", net, irq_num); - log::info!("registered virtio network device irq={irq_num:?}"); - Ok(()) + }) } fn map_net_error(err: VirtIoError) -> NetError { diff --git a/drivers/ax-driver/src/virtio/vsock.rs b/drivers/ax-driver/src/virtio/vsock.rs index 7571ac1e8e..c3bc77c14b 100644 --- a/drivers/ax-driver/src/virtio/vsock.rs +++ b/drivers/ax-driver/src/virtio/vsock.rs @@ -4,7 +4,7 @@ use alloc::format; use rdif_vsock::{VsockAddr as RdifVsockAddr, VsockConnId, VsockError, VsockEvent}; use rdrive::{DriverGeneric, PlatformDevice, probe::OnProbeError}; -#[cfg(any(plat_static, plat_dyn))] +#[cfg(all(feature = "pci", any(plat_static, plat_dyn)))] use virtio_drivers::transport::DeviceType; use virtio_drivers::{ Error as VirtIoError, @@ -15,11 +15,13 @@ use virtio_drivers::{ transport::Transport, }; -use crate::{virtio::VirtIoHalImpl, vsock::PlatformDeviceVsock}; +use crate::{BindingInfo, virtio::VirtIoHalImpl, vsock::PlatformDeviceVsock}; +#[cfg(all(feature = "pci", any(plat_static, plat_dyn)))] +use crate::{PciIrqRequirement, binding_info_from_pci}; const DEFAULT_RX_BUFFER_CAPACITY: u32 = 32 * 1024; -#[cfg(any(plat_static, plat_dyn))] +#[cfg(all(feature = "pci", any(plat_static, plat_dyn)))] crate::model_register!( name: "VirtIO Socket", level: ProbeLevel::PostKernel, @@ -29,24 +31,30 @@ crate::model_register!( }], ); -#[cfg(any(plat_static, plat_dyn))] -fn probe_pci( - endpoint: &mut rdrive::probe::pci::EndpointRc, +#[cfg(all(feature = "pci", any(plat_static, plat_dyn)))] +fn probe_pci(mut probe: rdrive::probe::pci::ProbePci<'_>) -> Result<(), OnProbeError> { + let transport = crate::pci::take_virtio_transport(probe.endpoint_mut(), DeviceType::Socket)?; + let info = binding_info_from_pci(probe.info(), PciIrqRequirement::Optional)?; + register_transport_with_info(probe.into_platform_device(), transport, info) +} + +pub fn register_transport( plat_dev: PlatformDevice, + transport: T, ) -> Result<(), OnProbeError> { - let transport = crate::pci::take_virtio_transport(endpoint, DeviceType::Socket)?; - register_transport(plat_dev, transport) + register_transport_with_info(plat_dev, transport, BindingInfo::empty()) } -pub fn register_transport( +pub fn register_transport_with_info( plat_dev: PlatformDevice, transport: T, + info: BindingInfo, ) -> Result<(), OnProbeError> { let dev = VirtIoVsock::new(transport).map_err(|err| { OnProbeError::other(format!("failed to initialize virtio-socket: {err:?}")) })?; - plat_dev.register_vsock(dev); - log::info!("registered virtio socket device"); + let irq = plat_dev.register_vsock_with_info(dev, info); + log::info!("registered virtio socket device irq={irq:?}"); Ok(()) } diff --git a/drivers/ax-driver/src/vsock/binding.rs b/drivers/ax-driver/src/vsock/binding.rs index 1dcea29073..26aaa91fd2 100644 --- a/drivers/ax-driver/src/vsock/binding.rs +++ b/drivers/ax-driver/src/vsock/binding.rs @@ -2,20 +2,37 @@ use alloc::{boxed::Box, string::String, vec::Vec}; use ax_errno::AxError; use rdif_vsock::Interface; -use rdrive::{Device, DriverGeneric}; +use rdrive::{DriverGeneric, probe::OnProbeError}; + +use crate::{ + BindingInfo, binding_info_from_acpi, binding_info_from_fdt, + registration::{BoundDevice, TakeRegistered, register_bound_device, take_registered_device}, +}; +#[cfg(feature = "pci")] +use crate::{PciIrqRequirement, binding_info_from_pci}; pub struct PlatformVsockDevice { name: String, + info: BindingInfo, vsock: Option>, } impl PlatformVsockDevice { - fn new(name: String, vsock: Box) -> Self { + fn new(name: String, vsock: Box, info: BindingInfo) -> Self { Self { name, + info, vsock: Some(vsock), } } + + pub fn binding_info(&self) -> &BindingInfo { + &self.info + } + + pub fn irq_num(&self) -> Option { + self.info.irq_num() + } } impl DriverGeneric for PlatformVsockDevice { @@ -24,22 +41,131 @@ impl DriverGeneric for PlatformVsockDevice { } } +impl BoundDevice for PlatformVsockDevice { + fn binding_info(&self) -> &BindingInfo { + &self.info + } +} + +impl TakeRegistered for PlatformVsockDevice { + type Output = Box; + + fn take_registered(&mut self) -> Option { + self.vsock.take() + } +} + pub trait PlatformDeviceVsock { - fn register_vsock(self, dev: T) + fn register_vsock(self, dev: T) -> Option + where + T: Interface + 'static; + + fn register_vsock_with_info(self, dev: T, info: BindingInfo) -> Option where T: Interface + 'static; } impl PlatformDeviceVsock for rdrive::PlatformDevice { - fn register_vsock(self, dev: T) + fn register_vsock(self, dev: T) -> Option where T: Interface + 'static, { - let name = dev.name().into(); - self.register(PlatformVsockDevice::new(name, Box::new(dev))); + self.register_vsock_with_info(dev, BindingInfo::empty()) + } + + fn register_vsock_with_info(self, dev: T, info: BindingInfo) -> Option + where + T: Interface + 'static, + { + register_vsock_with_info(self, dev, info) } } +pub trait ProbeFdtVsock { + fn register_vsock(self, dev: T) -> Result, OnProbeError> + where + T: Interface + 'static; +} + +impl ProbeFdtVsock for rdrive::probe::fdt::ProbeFdt<'_> { + fn register_vsock(self, dev: T) -> Result, OnProbeError> + where + T: Interface + 'static, + { + let info = binding_info_from_fdt(self.info())?; + Ok(register_vsock_with_info( + self.into_platform_device(), + dev, + info, + )) + } +} + +pub trait ProbeAcpiVsock { + fn register_vsock(self, dev: T) -> Result, OnProbeError> + where + T: Interface + 'static; +} + +impl ProbeAcpiVsock for rdrive::probe::acpi::ProbeAcpi<'_> { + fn register_vsock(self, dev: T) -> Result, OnProbeError> + where + T: Interface + 'static, + { + let info = binding_info_from_acpi(self.info())?; + Ok(register_vsock_with_info( + self.into_platform_device(), + dev, + info, + )) + } +} + +#[cfg(feature = "pci")] +pub trait ProbePciVsock { + fn register_vsock( + self, + dev: T, + requirement: PciIrqRequirement, + ) -> Result, OnProbeError> + where + T: Interface + 'static; +} + +#[cfg(feature = "pci")] +impl ProbePciVsock for rdrive::probe::pci::ProbePci<'_> { + fn register_vsock( + self, + dev: T, + requirement: PciIrqRequirement, + ) -> Result, OnProbeError> + where + T: Interface + 'static, + { + let info = binding_info_from_pci(self.info(), requirement)?; + Ok(register_vsock_with_info( + self.into_platform_device(), + dev, + info, + )) + } +} + +fn register_vsock_with_info( + plat_dev: rdrive::PlatformDevice, + dev: T, + info: BindingInfo, +) -> Option +where + T: Interface + 'static, +{ + let name = dev.name().into(); + register_bound_device( + plat_dev, + PlatformVsockDevice::new(name, Box::new(dev), info), + ) +} + pub fn take_vsock_devices() -> Result>, AxError> { let mut devices = Vec::new(); for dev in rdrive::get_list::() { @@ -48,7 +174,91 @@ pub fn take_vsock_devices() -> Result>, AxError> { Ok(devices) } -fn take_vsock_device(device: Device) -> Result, AxError> { - let mut device = device.lock().map_err(|_| AxError::BadState)?; - device.vsock.take().ok_or(AxError::BadState) +fn take_vsock_device( + device: rdrive::Device, +) -> Result, AxError> { + take_registered_device(device).ok_or(AxError::BadState) +} + +#[cfg(test)] +mod tests { + extern crate std; + + use rdif_vsock::{VsockConnId, VsockError, VsockEvent}; + + use super::*; + use crate::BindingInfo; + + struct TestVsock; + + impl DriverGeneric for TestVsock { + fn name(&self) -> &str { + "test-vsock" + } + } + + impl Interface for TestVsock { + fn guest_cid(&self) -> u64 { + 3 + } + + fn listen(&mut self, _port: u32) -> Result<(), VsockError> { + Ok(()) + } + + fn connect(&mut self, _id: VsockConnId) -> Result<(), VsockError> { + Ok(()) + } + + fn send(&mut self, _id: VsockConnId, buf: &[u8]) -> Result { + Ok(buf.len()) + } + + fn recv(&mut self, _id: VsockConnId, _buf: &mut [u8]) -> Result { + Ok(0) + } + + fn recv_avail(&mut self, _id: VsockConnId) -> Result { + Ok(0) + } + + fn disconnect(&mut self, _id: VsockConnId) -> Result<(), VsockError> { + Ok(()) + } + + fn abort(&mut self, _id: VsockConnId) -> Result<(), VsockError> { + Ok(()) + } + + fn poll_event(&mut self) -> Result, VsockError> { + Ok(None) + } + } + + #[test] + fn platform_vsock_device_exposes_binding_info_irq_num() { + let irq = 44; + let device = PlatformVsockDevice::new( + "test-vsock".into(), + Box::new(TestVsock), + BindingInfo::with_irq(Some(irq)), + ); + + assert_eq!(device.binding_info().irq_num(), Some(irq)); + assert_eq!(device.irq_num(), Some(irq)); + assert_eq!(BoundDevice::irq_num(&device), Some(irq)); + } + + #[test] + fn platform_vsock_device_empty_binding_has_no_irq_num() { + let device = PlatformVsockDevice::new( + "test-vsock".into(), + Box::new(TestVsock), + BindingInfo::empty(), + ); + + assert_eq!(device.binding_info().irq_num(), None); + assert_eq!(device.irq_num(), None); + assert_eq!(BoundDevice::irq_num(&device), None); + } } diff --git a/drivers/ax-driver/tests/binding_info.rs b/drivers/ax-driver/tests/binding_info.rs new file mode 100644 index 0000000000..3542d2bf8e --- /dev/null +++ b/drivers/ax-driver/tests/binding_info.rs @@ -0,0 +1,356 @@ +use ax_driver::BindingInfo; +#[cfg(feature = "pci")] +use ax_driver::PciIrqRequirement; +#[cfg(feature = "plat-dyn")] +use ax_driver::binding_info_from_acpi_route; +#[cfg(feature = "plat-dyn")] +use ax_driver::binding_info_from_fdt; +#[cfg(feature = "pci")] +use ax_driver::binding_info_from_pci; +#[cfg(feature = "pci")] +use rdrive::probe::pci::{PciAddress, PciInfo}; +#[cfg(feature = "plat-dyn")] +use { + axklib::{ + AxError, AxResult, IrqCpuMask, IrqHandle, Klib, PhysAddr, RawIrqHandler, VirtAddr, + impl_trait, + }, + core::time::Duration, + fdt_edit::{Fdt, Node, Property}, + rdrive::{ + DriverGeneric, Platform, PlatformDevice, + probe::{ + OnProbeError, + acpi::{AcpiGsiController, AcpiGsiRoute, AcpiIrqPolarity, AcpiIrqTrigger}, + }, + register::{DriverRegister, ProbeFdt, ProbeKind, ProbeLevel, ProbePriority}, + }, + std::ptr::NonNull, + std::sync::Mutex, +}; + +#[cfg(feature = "plat-dyn")] +static CAPTURED_IRQ: Mutex>> = Mutex::new(None); +#[cfg(feature = "plat-dyn")] +static SETUP_SPECIFIER: Mutex>> = Mutex::new(None); +#[cfg(feature = "plat-dyn")] +static SETUP_ACPI_ROUTE: Mutex> = Mutex::new(None); + +#[cfg(feature = "plat-dyn")] +static TEST_INTC_PROBE_KINDS: &[ProbeKind] = &[ProbeKind::Fdt { + compatibles: &["test,intc"], + on_probe: register_test_intc, +}]; + +#[cfg(feature = "plat-dyn")] +static TEST_DEVICE_PROBE_KINDS: &[ProbeKind] = &[ProbeKind::Fdt { + compatibles: &["test,binding-info"], + on_probe: capture_binding_info, +}]; + +#[cfg(feature = "plat-dyn")] +static STATIC_INTC_PROBE_KINDS: &[ProbeKind] = &[ProbeKind::Static { + on_probe: register_static_test_intc, +}]; + +#[cfg(feature = "plat-dyn")] +struct KlibImpl; + +#[cfg(feature = "plat-dyn")] +impl_trait! { + impl Klib for KlibImpl { + fn mem_iomap(_addr: PhysAddr, _size: usize) -> AxResult { + Err(AxError::Unsupported) + } + + fn mem_virt_to_phys(addr: VirtAddr) -> PhysAddr { + PhysAddr::from_usize(addr.as_usize()) + } + + fn mem_make_dma_coherent_uncached(_addr: VirtAddr, _size: usize) -> AxResult { + Err(AxError::Unsupported) + } + + fn mem_restore_dma_cached(_addr: VirtAddr, _size: usize) -> AxResult { + Err(AxError::Unsupported) + } + + fn dma_alloc_pages(_dma_mask: u64, _num_pages: usize, _align: usize) -> AxResult { + Err(AxError::Unsupported) + } + + fn dma_dealloc_pages(_addr: VirtAddr, _num_pages: usize) {} + + fn time_busy_wait(_dur: Duration) {} + + fn time_monotonic_nanos() -> u64 { + 0 + } + + fn time_try_init_epoch_offset(_epoch_time_nanos: u64) -> bool { + false + } + + fn irq_set_enable(_irq: usize, _enabled: bool) {} + + fn irq_request_shared( + _irq: usize, + _handler: RawIrqHandler, + _data: core::ptr::NonNull<()>, + ) -> AxResult { + Err(AxError::Unsupported) + } + + fn irq_request_percpu( + _irq: usize, + _cpus: IrqCpuMask, + _handler: RawIrqHandler, + _data: core::ptr::NonNull<()>, + ) -> AxResult { + Err(AxError::Unsupported) + } + + fn irq_free(_handle: IrqHandle) -> AxResult { + Err(AxError::Unsupported) + } + + fn irq_enable(_handle: IrqHandle) -> AxResult { + Err(AxError::Unsupported) + } + + fn irq_disable(_handle: IrqHandle) -> AxResult { + Err(AxError::Unsupported) + } + } +} + +#[test] +fn empty_binding_info_has_no_irq() { + let info = BindingInfo::empty(); + + assert_eq!(info.irq_num(), None); +} + +#[test] +fn explicit_binding_info_reports_numbered_irq() { + let info = BindingInfo::with_irq(Some(33)); + + assert_eq!(info.irq_num(), Some(33)); +} + +#[test] +#[cfg(feature = "pci")] +fn optional_pci_binding_info_can_be_empty() { + let info = binding_info_from_pci( + PciInfo { + address: PciAddress::new(0, 0, 0, 0), + interrupt_pin: 0, + interrupt_line: 0, + intx_route: None, + }, + PciIrqRequirement::Optional, + ) + .unwrap(); + + assert_eq!(info.irq_num(), None); +} + +#[test] +#[cfg(feature = "pci")] +fn required_pci_binding_info_reports_unresolved_irq() { + let err = binding_info_from_pci( + PciInfo { + address: PciAddress::new(0, 0, 0, 0), + interrupt_pin: 0, + interrupt_line: 0, + intx_route: None, + }, + PciIrqRequirement::Required, + ) + .unwrap_err(); + + assert!(err.to_string().contains("failed to resolve IRQ")); +} + +#[cfg(feature = "plat-dyn")] +#[test] +fn fdt_binding_info_resolves_first_irq_during_probe() { + *CAPTURED_IRQ.lock().unwrap() = None; + *SETUP_SPECIFIER.lock().unwrap() = None; + + let fdt_data = Box::leak(Box::new(minimal_irq_fdt().encode())); + let fdt_addr = NonNull::new(fdt_data.as_ref().as_ptr() as *mut u8).unwrap(); + + rdrive::init(Platform::Fdt { addr: fdt_addr }).unwrap(); + rdrive::register_add(DriverRegister { + name: "binding-info-fdt-test-intc", + level: ProbeLevel::PostKernel, + priority: ProbePriority::INTC, + probe_kinds: TEST_INTC_PROBE_KINDS, + }); + rdrive::register_add(DriverRegister { + name: "binding-info-fdt-test-device", + level: ProbeLevel::PostKernel, + priority: ProbePriority::DEFAULT, + probe_kinds: TEST_DEVICE_PROBE_KINDS, + }); + rdrive::probe_all(true).unwrap(); + + assert_eq!(*CAPTURED_IRQ.lock().unwrap(), Some(Some(77))); + assert_eq!(*SETUP_SPECIFIER.lock().unwrap(), Some(vec![0, 42, 4])); +} + +#[cfg(feature = "plat-dyn")] +#[test] +fn acpi_binding_info_sets_up_intc_during_registration() { + *SETUP_ACPI_ROUTE.lock().unwrap() = None; + ensure_rdrive_static_intc(); + + let info = binding_info_from_acpi_route("\\_SB.TEST", Some(acpi_route())).unwrap(); + + assert_eq!(info.irq_num(), Some(88)); + assert_eq!(*SETUP_ACPI_ROUTE.lock().unwrap(), Some(acpi_route())); +} + +#[cfg(feature = "plat-dyn")] +struct TestIntc; + +#[cfg(feature = "plat-dyn")] +impl DriverGeneric for TestIntc { + fn name(&self) -> &str { + "test-intc" + } +} + +#[cfg(feature = "plat-dyn")] +impl rdif_intc::Interface for TestIntc { + fn supports_acpi_gsi(&self, route: &AcpiGsiRoute) -> bool { + *route == acpi_route() + } + + fn setup_irq_by_fdt(&mut self, irq_prop: &[u32]) -> rdif_intc::IrqId { + *SETUP_SPECIFIER.lock().unwrap() = Some(irq_prop.to_vec()); + 77.into() + } + + fn setup_irq_by_acpi(&mut self, route: &AcpiGsiRoute) -> rdif_intc::IrqId { + *SETUP_ACPI_ROUTE.lock().unwrap() = Some(*route); + 88.into() + } +} + +#[cfg(feature = "plat-dyn")] +fn register_test_intc(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + probe + .into_platform_device() + .register(rdif_intc::Intc::new(TestIntc)); + Ok(()) +} + +#[cfg(feature = "plat-dyn")] +fn register_static_test_intc(plat_dev: PlatformDevice) -> Result<(), OnProbeError> { + plat_dev.register(rdif_intc::Intc::new(TestIntc)); + Ok(()) +} + +#[cfg(feature = "plat-dyn")] +fn ensure_rdrive_static_intc() { + if !rdrive::is_initialized() { + rdrive::init(Platform::Static).unwrap(); + } + let has_acpi_intc = rdrive::get_list::() + .iter() + .any(|intc| intc.descriptor().name.starts_with("ACPI IOAPIC")); + if !has_acpi_intc { + rdrive::register_add(DriverRegister { + name: "ACPI IOAPIC binding-info-test", + level: ProbeLevel::PostKernel, + priority: ProbePriority::INTC, + probe_kinds: STATIC_INTC_PROBE_KINDS, + }); + rdrive::probe_all(true).unwrap(); + } +} + +#[cfg(feature = "plat-dyn")] +fn capture_binding_info(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + *CAPTURED_IRQ.lock().unwrap() = Some(binding_info_from_fdt(probe.info())?.irq_num()); + Ok(()) +} + +#[cfg(feature = "plat-dyn")] +fn minimal_irq_fdt() -> Fdt { + let mut fdt = Fdt::new(); + let root = fdt.root_id(); + fdt.node_mut(root) + .unwrap() + .set_property(prop_u32s("#address-cells", &[1])); + fdt.node_mut(root) + .unwrap() + .set_property(prop_u32s("#size-cells", &[1])); + + let intc = fdt.add_node(root, Node::new("interrupt-controller@0")); + fdt.node_mut(intc).unwrap().set_property(prop_strs( + "compatible", + &["test,intc", "test,intc-fallback"], + )); + fdt.node_mut(intc) + .unwrap() + .set_property(prop_u32s("phandle", &[1])); + fdt.node_mut(intc) + .unwrap() + .set_property(Property::new("interrupt-controller", Vec::new())); + fdt.node_mut(intc) + .unwrap() + .set_property(prop_u32s("#interrupt-cells", &[3])); + + let dev = fdt.add_node(root, Node::new("device@0")); + fdt.node_mut(dev).unwrap().set_property(prop_strs( + "compatible", + &["test,binding-info", "test,binding-info-fallback"], + )); + fdt.node_mut(dev) + .unwrap() + .set_property(prop_u32s("interrupt-parent", &[1])); + fdt.node_mut(dev) + .unwrap() + .set_property(prop_u32s("interrupts", &[0, 42, 4, 0, 43, 4])); + fdt.node_mut(dev) + .unwrap() + .set_property(prop_strs("interrupt-names", &["main", "backup"])); + + fdt +} + +#[cfg(feature = "plat-dyn")] +fn acpi_route() -> AcpiGsiRoute { + AcpiGsiRoute { + gsi: 32, + vector: 0x50, + controller: AcpiGsiController::IoApic, + controller_id: 0, + controller_address: 0xfec0_0000, + controller_input: 32, + trigger: AcpiIrqTrigger::Level, + polarity: AcpiIrqPolarity::ActiveLow, + } +} + +#[cfg(feature = "plat-dyn")] +fn prop_u32s(name: &str, values: &[u32]) -> Property { + let mut data = Vec::new(); + for value in values { + data.extend_from_slice(&value.to_be_bytes()); + } + Property::new(name, data) +} + +#[cfg(feature = "plat-dyn")] +fn prop_strs(name: &str, values: &[&str]) -> Property { + let mut data = Vec::new(); + for value in values { + data.extend_from_slice(value.as_bytes()); + data.push(0); + } + Property::new(name, data) +} diff --git a/drivers/ax-driver/tests/model_register.rs b/drivers/ax-driver/tests/model_register.rs index 0c251d2e6c..315c48bc8d 100644 --- a/drivers/ax-driver/tests/model_register.rs +++ b/drivers/ax-driver/tests/model_register.rs @@ -1,6 +1,85 @@ #![feature(used_with_arg)] use ax_driver::{PlatformDevice, probe::OnProbeError}; +#[cfg(feature = "plat-dyn")] +use axklib::{ + AxError, AxResult, IrqCpuMask, IrqHandle, Klib, PhysAddr, RawIrqHandler, VirtAddr, impl_trait, +}; + +#[cfg(feature = "plat-dyn")] +struct KlibImpl; + +#[cfg(feature = "plat-dyn")] +impl_trait! { + impl Klib for KlibImpl { + fn mem_iomap(_addr: PhysAddr, _size: usize) -> AxResult { + Err(AxError::Unsupported) + } + + fn mem_virt_to_phys(addr: VirtAddr) -> PhysAddr { + PhysAddr::from_usize(addr.as_usize()) + } + + fn mem_make_dma_coherent_uncached(_addr: VirtAddr, _size: usize) -> AxResult { + Err(AxError::Unsupported) + } + + fn mem_restore_dma_cached(_addr: VirtAddr, _size: usize) -> AxResult { + Err(AxError::Unsupported) + } + + fn dma_alloc_pages( + _dma_mask: u64, + _num_pages: usize, + _align: usize, + ) -> AxResult { + Err(AxError::Unsupported) + } + + fn dma_dealloc_pages(_addr: VirtAddr, _num_pages: usize) {} + + fn time_busy_wait(_dur: core::time::Duration) {} + + fn time_monotonic_nanos() -> u64 { + 0 + } + + fn time_try_init_epoch_offset(_epoch_time_nanos: u64) -> bool { + false + } + + fn irq_set_enable(_irq: usize, _enabled: bool) {} + + fn irq_request_shared( + _irq: usize, + _handler: RawIrqHandler, + _data: core::ptr::NonNull<()>, + ) -> AxResult { + Err(AxError::Unsupported) + } + + fn irq_request_percpu( + _irq: usize, + _cpus: IrqCpuMask, + _handler: RawIrqHandler, + _data: core::ptr::NonNull<()>, + ) -> AxResult { + Err(AxError::Unsupported) + } + + fn irq_free(_handle: IrqHandle) -> AxResult { + Err(AxError::Unsupported) + } + + fn irq_enable(_handle: IrqHandle) -> AxResult { + Err(AxError::Unsupported) + } + + fn irq_disable(_handle: IrqHandle) -> AxResult { + Err(AxError::Unsupported) + } + } +} ax_driver::model_register!( name: "ax-driver model register test", diff --git a/drivers/examples/enumerate/src/blk.rs b/drivers/examples/enumerate/src/blk.rs index 831fd1b217..2fc6c73680 100644 --- a/drivers/examples/enumerate/src/blk.rs +++ b/drivers/examples/enumerate/src/blk.rs @@ -3,9 +3,8 @@ extern crate alloc; use log::debug; use rdif_intc::Intc; use rdrive::{ - PlatformDevice, probe::OnProbeError, - register::{DriverRegister, FdtInfo, ProbeKind, ProbeLevel, ProbePriority}, + register::{DriverRegister, ProbeFdt, ProbeKind, ProbeLevel, ProbePriority}, }; pub fn register() -> DriverRegister { @@ -20,14 +19,15 @@ pub fn register() -> DriverRegister { } } -fn probe(info: FdtInfo<'_>, _dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (info, dev) = probe.into_parts(); let mut reg = info.node.regs().into_iter(); let base_reg = reg.next().ok_or(OnProbeError::other(format!( "[{}] has no reg", info.node.name() )))?; - if let Some(irq) = _dev.descriptor.irq_parent { + if let Some(irq) = dev.descriptor.irq_parent { let intc = rdrive::get::(irq).unwrap(); for interrupt in info.interrupts() { diff --git a/drivers/examples/enumerate/src/clk.rs b/drivers/examples/enumerate/src/clk.rs index bbfccbd200..ebfe51167a 100644 --- a/drivers/examples/enumerate/src/clk.rs +++ b/drivers/examples/enumerate/src/clk.rs @@ -2,7 +2,7 @@ use log::debug; use rdrive::{ driver::clk::*, probe::OnProbeError, - register::{DriverRegister, FdtInfo, ProbeKind, ProbeLevel, ProbePriority}, + register::{DriverRegister, ProbeFdt, ProbeKind, ProbeLevel, ProbePriority}, *, }; @@ -22,7 +22,8 @@ pub fn register() -> DriverRegister { } } -fn probe(_node: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let plat_dev = probe.into_platform_device(); plat_dev.register(Clk::new(Clock { rate: 0 })); Ok(()) @@ -63,6 +64,6 @@ module_driver!( }], ); -fn probe_clk(_fdt: FdtInfo<'_>, _desc: PlatformDevice) -> Result<(), OnProbeError> { +fn probe_clk(_probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { todo!() } diff --git a/drivers/examples/enumerate/src/intc.rs b/drivers/examples/enumerate/src/intc.rs index bda939db13..cdc616067c 100644 --- a/drivers/examples/enumerate/src/intc.rs +++ b/drivers/examples/enumerate/src/intc.rs @@ -1,9 +1,8 @@ use log::debug; use rdif_intc::*; use rdrive::{ - PlatformDevice, probe::OnProbeError, - register::{DriverRegister, FdtInfo, ProbeKind, ProbeLevel, ProbePriority}, + register::{DriverRegister, ProbeFdt, ProbeKind, ProbeLevel, ProbePriority}, }; pub struct IrqTest {} @@ -33,7 +32,8 @@ impl Interface for IrqTest { } } -fn probe_intc(fdt: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe_intc(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (fdt, plat_dev) = probe.into_parts(); debug!( "on_probe: {}, parent intc {:?}", fdt.node.name(), diff --git a/drivers/examples/enumerate/src/timer.rs b/drivers/examples/enumerate/src/timer.rs index 97ed01b2c7..c4d31c7360 100644 --- a/drivers/examples/enumerate/src/timer.rs +++ b/drivers/examples/enumerate/src/timer.rs @@ -1,10 +1,9 @@ use log::debug; use rdrive::{ - PlatformDevice, driver::{Intc, systick::*}, get, probe::OnProbeError, - register::{DriverRegister, FdtInfo, ProbeKind, ProbeLevel, ProbePriority}, + register::{DriverRegister, ProbeFdt, ProbeKind, ProbeLevel, ProbePriority}, }; struct Timer; @@ -21,7 +20,8 @@ pub fn register() -> DriverRegister { } } -fn probe(_node: FdtInfo<'_>, dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let dev = probe.into_platform_device(); if let Some(parent) = dev.descriptor.irq_parent && let Ok(intc) = get::(parent) { diff --git a/drivers/interface/rdif-def/src/irq.rs b/drivers/interface/rdif-def/src/irq.rs index e28059eadf..118f9fb325 100644 --- a/drivers/interface/rdif-def/src/irq.rs +++ b/drivers/interface/rdif-def/src/irq.rs @@ -33,12 +33,19 @@ pub enum AcpiIrqPolarity { ActiveLow, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum AcpiGsiController { + IoApic, + PchPic, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct AcpiGsiRoute { pub gsi: u32, pub vector: usize, - pub controller_id: u8, - pub controller_address: u32, + pub controller: AcpiGsiController, + pub controller_id: u16, + pub controller_address: u64, pub controller_input: u8, pub trigger: AcpiIrqTrigger, pub polarity: AcpiIrqPolarity, diff --git a/drivers/interface/rdif-intc/src/lib.rs b/drivers/interface/rdif-intc/src/lib.rs index dccf443c14..4a9479c5cf 100644 --- a/drivers/interface/rdif-intc/src/lib.rs +++ b/drivers/interface/rdif-intc/src/lib.rs @@ -10,6 +10,10 @@ pub trait Interface: DriverGeneric { unimplemented!(); } + fn supports_acpi_gsi(&self, _route: &AcpiGsiRoute) -> bool { + false + } + fn setup_irq_by_acpi(&mut self, _route: &AcpiGsiRoute) -> IrqId { unimplemented!(); } diff --git a/drivers/net/rd-net/src/lib.rs b/drivers/net/rd-net/src/lib.rs index 748569b7ce..3e58373d55 100644 --- a/drivers/net/rd-net/src/lib.rs +++ b/drivers/net/rd-net/src/lib.rs @@ -106,6 +106,18 @@ impl Net { self.interface().mac_address() } + pub fn enable_irq(&mut self) { + self.interface().enable_irq(); + } + + pub fn disable_irq(&mut self) { + self.interface().disable_irq(); + } + + pub fn is_irq_enabled(&self) -> bool { + self.interface().is_irq_enabled() + } + pub fn create_tx_queue(&mut self) -> Result { let irq_guard = self.irq_guard(); let queue = self @@ -173,6 +185,16 @@ pub struct IrqHandler { unsafe impl Sync for IrqHandler {} impl IrqHandler { + pub fn enable(&self) { + let iface = unsafe { &mut **self.inner.interface.get() }; + iface.enable_irq(); + } + + pub fn disable(&self) { + let iface = unsafe { &mut **self.inner.interface.get() }; + iface.disable_irq(); + } + pub fn handle(&self) { let iface = unsafe { &mut **self.inner.interface.get() }; let event = iface.handle_irq(); diff --git a/drivers/pci/pcie/src/lib.rs b/drivers/pci/pcie/src/lib.rs index 1c38610e5f..9571bb5a20 100644 --- a/drivers/pci/pcie/src/lib.rs +++ b/drivers/pci/pcie/src/lib.rs @@ -14,5 +14,7 @@ pub use bar_alloc::*; pub use chip::PcieGeneric; pub use mmio_api::{MapError, Mmio, MmioAddr, MmioOp}; pub use rdif_pcie::{Interface as Controller, PciMem32, PciMem64, PcieController}; -pub use root::enumerate_by_controller; +pub use root::{ + EnumeratedEndpoint, PciIntxRoute, enumerate_by_controller, enumerate_by_controller_with_info, +}; pub use types::*; diff --git a/drivers/pci/pcie/src/root.rs b/drivers/pci/pcie/src/root.rs index b3a993379c..882a9b018e 100644 --- a/drivers/pci/pcie/src/root.rs +++ b/drivers/pci/pcie/src/root.rs @@ -12,6 +12,13 @@ pub fn enumerate_by_controller<'a>( controller: &'a mut PcieController, range: Option>, ) -> impl Iterator + 'a { + enumerate_by_controller_with_info(controller, range).map(|item| item.endpoint) +} + +pub fn enumerate_by_controller_with_info<'a>( + controller: &'a mut PcieController, + range: Option>, +) -> impl Iterator + 'a { let range = range.unwrap_or(0..0x100); PciIterator { @@ -25,6 +32,19 @@ pub fn enumerate_by_controller<'a>( } } +#[derive(Debug)] +pub struct EnumeratedEndpoint { + pub endpoint: Endpoint, + pub intx_route: Option, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct PciIntxRoute { + pub root_device: u8, + pub root_function: u8, + pub root_pin: u8, +} + pub(crate) struct PciIterator<'a> { root: &'a mut PcieController, segment: u16, @@ -36,7 +56,7 @@ pub(crate) struct PciIterator<'a> { } impl<'a> Iterator for PciIterator<'a> { - type Item = Endpoint; + type Item = EnumeratedEndpoint; fn next(&mut self) -> Option { while !self.is_finish { @@ -46,7 +66,11 @@ impl<'a> Iterator for PciIterator<'a> { self.next(Some(pci_pci_bridge)); } PciConfigSpace::Endpoint(ep) => { - let item = ep; + let intx_route = self.intx_route_for_endpoint(&ep); + let item = EnumeratedEndpoint { + endpoint: ep, + intx_route, + }; self.next(None); return Some(item); } @@ -113,6 +137,17 @@ impl<'a> PciIterator<'a> { PciAddress::new(self.segment, bus, device, self.function) } + fn intx_route_for_endpoint(&self, endpoint: &Endpoint) -> Option { + root_intx_route( + endpoint.address(), + endpoint.interrupt_pin(), + self.stack.iter().skip(1).map(|bridge| BridgeAddress { + device: bridge.parent_device, + function: bridge.parent_function, + }), + ) + } + /// 若进位返回true fn is_next_function_max(&mut self) -> bool { if self.is_mulitple_function { @@ -163,7 +198,13 @@ impl<'a> PciIterator<'a> { }); } - self.stack.push(Bridge { bridge, device: 0 }); + let address = bridge.address(); + self.stack.push(Bridge { + bridge, + device: 0, + parent_device: address.device(), + parent_function: address.function(), + }); self.function = 0; return; @@ -180,6 +221,8 @@ impl<'a> PciIterator<'a> { struct Bridge { bridge: PciPciBridge, device: u8, + parent_device: u8, + parent_function: u8, } impl Bridge { @@ -187,6 +230,131 @@ impl Bridge { Self { bridge: PciPciBridge::root(), device: bus_start, + parent_device: 0, + parent_function: 0, } } } + +const fn swizzle_interrupt_pin(interrupt_pin: u8, device: u8) -> Option { + if interrupt_pin == 0 || interrupt_pin > 4 { + return None; + } + Some(((interrupt_pin - 1 + (device & 0x3)) % 4) + 1) +} + +#[derive(Clone, Copy)] +struct BridgeAddress { + device: u8, + function: u8, +} + +fn root_intx_route( + endpoint: PciAddress, + interrupt_pin: u8, + bridges_from_root: impl DoubleEndedIterator, +) -> Option { + let mut pin = interrupt_pin; + if !(1..=4).contains(&pin) { + return None; + } + + let mut device = endpoint.device(); + let mut function = endpoint.function(); + for bridge in bridges_from_root.rev() { + pin = swizzle_interrupt_pin(pin, device)?; + device = bridge.device; + function = bridge.function; + } + + Some(PciIntxRoute { + root_device: device, + root_function: function, + root_pin: pin, + }) +} + +#[cfg(test)] +mod tests { + use super::{BridgeAddress, PciIntxRoute, root_intx_route, swizzle_interrupt_pin}; + use crate::PciAddress; + + #[test] + fn intx_swizzle_uses_linux_slot_rotation() { + assert_eq!(swizzle_interrupt_pin(1, 0), Some(1)); + assert_eq!(swizzle_interrupt_pin(1, 1), Some(2)); + assert_eq!(swizzle_interrupt_pin(1, 2), Some(3)); + assert_eq!(swizzle_interrupt_pin(1, 3), Some(4)); + assert_eq!(swizzle_interrupt_pin(4, 1), Some(1)); + } + + #[test] + fn intx_swizzle_rejects_absent_or_invalid_pins() { + assert_eq!(swizzle_interrupt_pin(0, 1), None); + assert_eq!(swizzle_interrupt_pin(5, 1), None); + } + + #[test] + fn root_endpoint_uses_its_own_device_function_and_pin() { + let route = root_intx_route(PciAddress::new(0, 0, 5, 2), 3, [].into_iter()); + + assert_eq!( + route, + Some(PciIntxRoute { + root_device: 5, + root_function: 2, + root_pin: 3, + }) + ); + } + + #[test] + fn bridge_endpoint_uses_parent_bridge_slot_and_swizzled_pin() { + let route = root_intx_route( + PciAddress::new(0, 1, 3, 0), + 1, + [BridgeAddress { + device: 2, + function: 0, + }] + .into_iter(), + ); + + assert_eq!( + route, + Some(PciIntxRoute { + root_device: 2, + root_function: 0, + root_pin: 4, + }) + ); + } + + #[test] + fn nested_bridge_endpoint_swizzles_at_each_level() { + let route = root_intx_route( + PciAddress::new(0, 2, 1, 0), + 2, + [ + BridgeAddress { + device: 4, + function: 1, + }, + BridgeAddress { + device: 3, + function: 0, + }, + ] + .into_iter(), + ); + + assert_eq!( + route, + Some(PciIntxRoute { + root_device: 4, + root_function: 1, + root_pin: 2, + }) + ); + } +} diff --git a/drivers/pci/pcie/src/types/config/endpoint.rs b/drivers/pci/pcie/src/types/config/endpoint.rs index 63a2ba5030..430b16fc8b 100644 --- a/drivers/pci/pcie/src/types/config/endpoint.rs +++ b/drivers/pci/pcie/src/types/config/endpoint.rs @@ -1,6 +1,6 @@ -use alloc::vec::Vec; +use alloc::{format, vec::Vec}; use core::{ - fmt::{self, Debug, Display, Write}, + fmt::{self, Debug, Display}, ops::{Deref, DerefMut, Range}, }; @@ -180,45 +180,80 @@ impl Display for Endpoint { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let address = self.base.address(); let class_info = self.base.revision_and_class(); - let device_type = self.device_type(); + EndpointIdentity { + segment: address.segment(), + bus: address.bus(), + device: address.device(), + function: address.function(), + device_type: self.device_type(), + vendor_id: self.base.vendor_id(), + device_id: self.base.device_id(), + revision_id: class_info.revision_id, + interface: class_info.interface, + } + .fmt(f) + } +} +struct EndpointIdentity { + segment: u16, + bus: u8, + device: u8, + function: u8, + device_type: DeviceType, + vendor_id: u16, + device_id: u16, + revision_id: u8, + interface: u8, +} + +impl Display for EndpointIdentity { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let device_type = format!("{:?}", self.device_type); write!( f, - "{:04x}:{:02x}:{:02x}.{} {} {:04x}:{:04x} (rev {:02x}, prog-if {:02x})", - address.segment(), - address.bus(), - address.device(), - address.function(), - PaddedDebug(&device_type, 24), - self.base.vendor_id(), - self.base.device_id(), - class_info.revision_id, - class_info.interface, + "{:04x}:{:02x}:{:02x}.{} {:24} {:04x}:{:04x} (rev {:02x}, prog-if {:02x})", + self.segment, + self.bus, + self.device, + self.function, + device_type, + self.vendor_id, + self.device_id, + self.revision_id, + self.interface, ) } } -struct PaddedDebug<'a, T>(&'a T, usize); +#[cfg(test)] +mod tests { + use alloc::format; -impl Display for PaddedDebug<'_, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - struct Counter<'a, 'b> { - inner: &'a mut fmt::Formatter<'b>, - len: usize, - } + use pci_types::device_type::DeviceType; + + use super::EndpointIdentity; - impl fmt::Write for Counter<'_, '_> { - fn write_str(&mut self, s: &str) -> fmt::Result { - self.len += s.len(); - self.inner.write_str(s) + #[test] + fn endpoint_identity_pads_device_type_for_aligned_output() { + let rendered = format!( + "{}", + EndpointIdentity { + segment: 0, + bus: 0, + device: 1, + function: 0, + device_type: DeviceType::UsbController, + vendor_id: 0x1234, + device_id: 0x5678, + revision_id: 1, + interface: 0x30, } - } + ); - let mut counter = Counter { inner: f, len: 0 }; - write!(counter, "{:?}", self.0)?; - for _ in counter.len..self.1 { - counter.inner.write_str(" ")?; - } - Ok(()) + assert_eq!( + rendered, + "0000:00:01.0 UsbController 1234:5678 (rev 01, prog-if 30)" + ); } } diff --git a/drivers/rdrive/README.md b/drivers/rdrive/README.md index a97c090dea..be5695eb1d 100644 --- a/drivers/rdrive/README.md +++ b/drivers/rdrive/README.md @@ -61,7 +61,17 @@ init(Platform::Fdt { addr: fdt_addr }).expect("rdrive init failed"); ### 3. 注册驱动 ```rust -use rdrive::{register_add, ProbeKind, ProbeLevel, ProbePriority}; +use rdrive::{ + register_add, + probe::OnProbeError, + register::{ProbeFdt, ProbeKind, ProbeLevel, ProbePriority}, +}; + +fn my_probe_function(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (info, plat_dev) = probe.into_parts(); + // 初始化设备并通过 plat_dev.register(...) 注册 + Ok(()) +} // 定义 FDT 驱动注册信息 static MY_DRIVER_REGISTER: DriverRegister = DriverRegister { diff --git a/drivers/rdrive/src/lib.rs b/drivers/rdrive/src/lib.rs index 33ddea1a6c..9e6bf95add 100644 --- a/drivers/rdrive/src/lib.rs +++ b/drivers/rdrive/src/lib.rs @@ -218,19 +218,36 @@ pub fn with_fdt(f: impl FnOnce(&Fdt) -> T) -> Option { /// /// # Example /// -/// ```rust +/// ```rust,no_run /// #![feature(used_with_arg)] /// -/// use rdrive::{module_driver, register::*}; +/// use rdrive::{driver::*, module_driver, probe::OnProbeError, register::ProbeFdt}; +/// +/// struct DemoDriver {} +/// +/// impl DriverGeneric for DemoDriver { +/// fn name(&self) -> &str { +/// "DemoDriver" +/// } +/// } +/// +/// // Define probe function +/// fn probe_clk(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { +/// // Implement specific device probing logic +/// let dev = probe.into_platform_device(); +/// dev.register(DemoDriver {}); +/// Ok(()) +/// } /// /// // Use macro to generate driver registration module /// module_driver! { -/// name: "Example Driver", +/// name: "CLK Driver", /// level: ProbeLevel::PostKernel, -/// priority: ProbePriority::DEFAULT, -/// probe_kinds: &[ProbeKind::Static { -/// on_probe: |dev| { -/// dev.register(rdrive::driver::Empty); +/// priority: ProbePriority::CLK, +/// probe_kinds: &[ProbeKind::Fdt { +/// compatibles: &["fixed-clock"], +/// // Use `probe_clk` above; this usage is because doctests cannot find the parent module. +/// on_probe: |_probe|{ /// Ok(()) /// }, /// }], diff --git a/drivers/rdrive/src/probe/acpi.rs b/drivers/rdrive/src/probe/acpi.rs index 7d34c8e7bf..5bd97a277e 100644 --- a/drivers/rdrive/src/probe/acpi.rs +++ b/drivers/rdrive/src/probe/acpi.rs @@ -1,5 +1,5 @@ use alloc::{ - collections::BTreeSet, + collections::{BTreeMap, BTreeSet}, format, rc::Rc, string::{String, ToString}, @@ -12,7 +12,8 @@ use acpi::{ aml::{ AmlError, Interpreter, namespace::{AmlName, NamespaceLevelKind}, - object::Object, + object::{FieldUnit, FieldUnitKind, FieldUpdateRule, Object, ObjectType}, + op_region::{OpRegion, RegionSpace}, pci_routing::{IrqDescriptor, PciRoutingTable, Pin}, resource::{InterruptPolarity, InterruptTrigger}, }, @@ -22,17 +23,21 @@ use acpi::{ pci::PciConfigRegions, }, }; -pub use rdif_base::irq::{AcpiGsiRoute, AcpiIrqPolarity, AcpiIrqTrigger}; +pub use rdif_base::irq::{AcpiGsiController, AcpiGsiRoute, AcpiIrqPolarity, AcpiIrqTrigger}; use spin::{Mutex, Once}; use crate::{ DeviceId, PlatformDevice, error::DriverError, - probe::{OnProbeError, ProbeError, pci::PciAddress}, + probe::{ + OnProbeError, ProbeError, + pci::{PciAddress, PciInfo, PciIntxRoute}, + }, register::{DriverRegister, ProbeKind}, }; pub const PCI_INTX_VECTOR_BASE: usize = 0x30; +const LOONGARCH_PCH_PIC_GSI_COUNT: u16 = 256; const PCI_ROOT_FALLBACK_PATHS: &[&str] = &["\\_SB.PCI0", "\\_SB.PCI1", "\\_SB.PC00", "\\_SB.PC01"]; static SYSTEM: Once = Once::new(); @@ -92,22 +97,80 @@ pub struct AcpiIoApic { } impl AcpiIoApic { + fn gsi_source(self) -> AcpiGsiSource { + AcpiGsiSource { + controller: AcpiGsiController::IoApic, + controller_id: u16::from(self.id), + controller_address: u64::from(self.address), + gsi_base: self.gsi_base, + gsi_count: u16::from(self.redirection_entries), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct AcpiPchPic { + pub id: u16, + pub address: u64, + pub mmio_size: u16, + pub gsi_count: u16, + pub gsi_base: u32, +} + +impl AcpiPchPic { + fn gsi_source(self) -> AcpiGsiSource { + AcpiGsiSource { + controller: AcpiGsiController::PchPic, + controller_id: self.id, + controller_address: self.address, + gsi_base: self.gsi_base, + gsi_count: self.gsi_count, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct AcpiGsiSource { + controller: AcpiGsiController, + controller_id: u16, + controller_address: u64, + gsi_base: u32, + gsi_count: u16, +} + +impl AcpiGsiSource { fn contains_gsi(self, gsi: u32) -> bool { let start = self.gsi_base; - let end = start.saturating_add(u32::from(self.redirection_entries)); + let end = start.saturating_add(u32::from(self.gsi_count)); (start..end).contains(&gsi) } + + fn route(self, gsi: u32) -> Option { + let controller_input = u8::try_from(gsi.checked_sub(self.gsi_base)?).ok()?; + Some(AcpiGsiRoute { + gsi, + vector: PCI_INTX_VECTOR_BASE + gsi as usize, + controller: self.controller, + controller_id: self.controller_id, + controller_address: self.controller_address, + controller_input, + trigger: AcpiIrqTrigger::Level, + polarity: AcpiIrqPolarity::ActiveLow, + }) + } } #[derive(Debug, Clone)] pub struct AcpiRouting { io_apics: Vec, + pch_pics: Vec, } impl AcpiRouting { pub const fn new() -> Self { Self { io_apics: Vec::new(), + pch_pics: Vec::new(), } } @@ -119,22 +182,26 @@ impl AcpiRouting { &self.io_apics } + pub fn add_pch_pic(&mut self, pch_pic: AcpiPchPic) { + self.pch_pics.push(pch_pic); + } + + pub fn pch_pics(&self) -> &[AcpiPchPic] { + &self.pch_pics + } + pub fn resolve_gsi(&self, gsi: u32) -> Option { - let io_apic = self - .io_apics + self.gsi_sources() + .find(|source| source.contains_gsi(gsi))? + .route(gsi) + } + + fn gsi_sources(&self) -> impl Iterator + '_ { + self.io_apics .iter() .copied() - .find(|io_apic| io_apic.contains_gsi(gsi))?; - let input = u8::try_from(gsi.saturating_sub(io_apic.gsi_base)).ok()?; - Some(AcpiGsiRoute { - gsi, - vector: PCI_INTX_VECTOR_BASE + gsi as usize, - controller_id: io_apic.id, - controller_address: io_apic.address, - controller_input: input, - trigger: AcpiIrqTrigger::Level, - polarity: AcpiIrqPolarity::ActiveLow, - }) + .map(AcpiIoApic::gsi_source) + .chain(self.pch_pics.iter().copied().map(AcpiPchPic::gsi_source)) } pub fn resolve_vector(&self, vector: usize) -> Option { @@ -151,7 +218,17 @@ impl Default for AcpiRouting { #[cfg(test)] mod tests { - use super::{AcpiIoApic, AcpiIrqPolarity, AcpiIrqTrigger, AcpiRouting}; + use acpi::aml::{ + pci_routing::IrqDescriptor, + resource::{InterruptPolarity, InterruptTrigger}, + }; + + use super::{ + AcpiGsiController, AcpiIoApic, AcpiIrqPolarity, AcpiIrqTrigger, AcpiPchPic, AcpiRouting, + LinkIrqResource, LinkIrqResourceKind, PciLinkAllocator, irq_descriptor_gsi, + is_buffer_field_to_field_unit_store_gap, pci_link_irq_field_candidates, + route_with_irq_descriptor_flags, select_pci_link_irq, + }; #[test] fn ioapic_routes_map_gsi_to_stable_vector() { @@ -167,6 +244,7 @@ mod tests { .resolve_gsi(16) .expect("gsi 16 should be handled by the IOAPIC"); assert_eq!(irq.gsi, 16); + assert_eq!(irq.controller, AcpiGsiController::IoApic); assert_eq!(irq.controller_id, 0); assert_eq!(irq.controller_address, 0xfec0_0000); assert_eq!(irq.controller_input, 16); @@ -175,21 +253,221 @@ mod tests { assert_eq!(irq.polarity, AcpiIrqPolarity::ActiveLow); assert!(routing.resolve_gsi(24).is_none()); } + + #[test] + fn pci_link_irq_can_route_to_legacy_ioapic_gsi() { + let mut routing = AcpiRouting::new(); + routing.add_io_apic(AcpiIoApic { + id: 0, + address: 0xfec0_0000, + gsi_base: 0, + redirection_entries: 24, + }); + + let descriptor = IrqDescriptor { + is_consumer: false, + trigger: InterruptTrigger::Level, + polarity: InterruptPolarity::ActiveLow, + is_shared: true, + is_wake_capable: false, + irq: 1 << 10, + }; + let gsi = irq_descriptor_gsi(&descriptor).unwrap(); + + assert_eq!(gsi, 10); + let route = routing + .resolve_gsi(gsi) + .expect("IOAPIC routes the legacy GSI from the ACPI PCI link"); + assert_eq!(route.gsi, 10); + assert_eq!(route.controller_input, 10); + } + + #[test] + fn pch_pic_routes_map_acpi_gsi_to_controller_input() { + let mut routing = AcpiRouting::new(); + routing.add_pch_pic(AcpiPchPic { + id: 1, + address: 0x1000_0000, + mmio_size: 0x1000, + gsi_count: 64, + gsi_base: 64, + }); + + let route = routing + .resolve_gsi(82) + .expect("GSI 82 should be covered by the PCH-PIC"); + assert_eq!(route.controller, AcpiGsiController::PchPic); + assert_eq!(route.controller_id, 1); + assert_eq!(route.controller_address, 0x1000_0000); + assert_eq!(route.controller_input, 18); + assert_eq!(route.vector, 0x30 + 82); + assert!(routing.resolve_gsi(128).is_none()); + } + + #[test] + fn pci_link_irq_selects_prs_when_crs_is_unassigned() { + let current = LinkIrqResource { + kind: LinkIrqResourceKind::ExtendedIrq, + descriptor: IrqDescriptor { + is_consumer: true, + trigger: InterruptTrigger::Level, + polarity: InterruptPolarity::ActiveHigh, + is_shared: true, + is_wake_capable: false, + irq: 0, + }, + irqs: alloc::vec![0], + }; + let possible = LinkIrqResource { + kind: LinkIrqResourceKind::ExtendedIrq, + descriptor: IrqDescriptor { + is_consumer: true, + trigger: InterruptTrigger::Level, + polarity: InterruptPolarity::ActiveHigh, + is_shared: true, + is_wake_capable: false, + irq: 5, + }, + irqs: alloc::vec![5, 10, 11], + }; + + let link = "\\_SB.LNKA".parse().unwrap(); + let allocator = PciLinkAllocator::default(); + let selection = select_pci_link_irq(&link, Some(¤t), Some(&possible), &allocator) + .expect("possible IRQs should allocate a PCI link"); + + assert_eq!(selection.irq, 10); + assert!(selection.needs_programming); + assert_eq!(selection.resource.irqs, alloc::vec![5, 10, 11]); + } + + #[test] + fn pci_link_irq_allocator_spreads_unassigned_links() { + let possible = LinkIrqResource { + kind: LinkIrqResourceKind::ExtendedIrq, + descriptor: IrqDescriptor { + is_consumer: true, + trigger: InterruptTrigger::Level, + polarity: InterruptPolarity::ActiveHigh, + is_shared: true, + is_wake_capable: false, + irq: 5, + }, + irqs: alloc::vec![5, 10, 11], + }; + let first = "\\_SB.LNKA".parse().unwrap(); + let second = "\\_SB.LNKB".parse().unwrap(); + let mut allocator = PciLinkAllocator::default(); + + let first_selection = select_pci_link_irq(&first, None, Some(&possible), &allocator) + .expect("first link should allocate"); + assert_eq!(first_selection.irq, 10); + allocator.commit(&first, first_selection.irq); + + let second_selection = select_pci_link_irq(&second, None, Some(&possible), &allocator) + .expect("second link should allocate"); + assert_eq!(second_selection.irq, 11); + } + + #[test] + fn pci_link_srs_gap_detection_is_narrow() { + let gap = acpi::aml::AmlError::ObjectNotOfExpectedType { + expected: acpi::aml::object::ObjectType::Integer, + got: acpi::aml::object::ObjectType::BufferField, + }; + let other = acpi::aml::AmlError::ObjectNotOfExpectedType { + expected: acpi::aml::object::ObjectType::Buffer, + got: acpi::aml::object::ObjectType::BufferField, + }; + + assert!(is_buffer_field_to_field_unit_store_gap(&gap)); + assert!(!is_buffer_field_to_field_unit_store_gap(&other)); + } + + #[test] + fn qemu_pci_link_names_map_to_pirq_fields() { + assert_eq!( + pci_link_irq_field_candidates(&"\\_SB.LNKB".parse().unwrap()), + ["\\_SB.PRQB", "\\_SB.PRQ1"] + ); + assert_eq!( + pci_link_irq_field_candidates(&"\\_SB.LNKG".parse().unwrap()), + ["\\_SB.PRQG"] + ); + assert!(pci_link_irq_field_candidates(&"\\_SB.LNKS".parse().unwrap()).is_empty()); + } + + #[test] + fn pci_irq_route_preserves_descriptor_trigger_and_polarity() { + let mut routing = AcpiRouting::new(); + routing.add_io_apic(AcpiIoApic { + id: 0, + address: 0xfec0_0000, + gsi_base: 0, + redirection_entries: 24, + }); + let route = routing.resolve_gsi(16).unwrap(); + let descriptor = IrqDescriptor { + is_consumer: true, + trigger: InterruptTrigger::Edge, + polarity: InterruptPolarity::ActiveHigh, + is_shared: false, + is_wake_capable: false, + irq: 16, + }; + + let route = route_with_irq_descriptor_flags(route, &descriptor); + + assert_eq!(route.trigger, AcpiIrqTrigger::Edge); + assert_eq!(route.polarity, AcpiIrqPolarity::ActiveHigh); + } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct AcpiPciIrqRoute { pub address: PciAddress, pub interrupt_pin: u8, + pub intx_route: PciIntxRoute, pub gsi: AcpiGsiRoute, } pub struct AcpiInfo<'a> { pub root: &'a System, pub path: &'a str, + pub irq_route: Option, +} + +impl AcpiInfo<'_> { + pub const fn irq_route(&self) -> Option { + self.irq_route + } +} + +pub struct ProbeAcpi<'a> { + info: AcpiInfo<'a>, + platform: PlatformDevice, +} + +impl<'a> ProbeAcpi<'a> { + #[allow(dead_code)] + pub(crate) fn new(info: AcpiInfo<'a>, platform: PlatformDevice) -> Self { + Self { info, platform } + } + + pub const fn info(&self) -> &AcpiInfo<'a> { + &self.info + } + + pub fn into_platform_device(self) -> PlatformDevice { + self.platform + } + + pub fn into_parts(self) -> (AcpiInfo<'a>, PlatformDevice) { + (self.info, self.platform) + } } -pub type FnOnProbe = fn(AcpiInfo<'_>, PlatformDevice) -> Result<(), OnProbeError>; +pub type FnOnProbe = for<'a> fn(ProbeAcpi<'a>) -> Result<(), OnProbeError>; pub fn check_root(root: AcpiRoot) -> Result<(), DriverError> { if root.rsdp == 0 { @@ -201,9 +479,10 @@ pub fn check_root(root: AcpiRoot) -> Result<(), DriverError> { pub fn init(root: AcpiRoot) -> Result<(), DriverError> { let system = System::new(root)?; info!( - "ACPI initialized: {} PCI ECAM region(s), {} IOAPIC(s)", + "ACPI initialized: {} PCI ECAM region(s), {} IOAPIC(s), {} PCH-PIC(s)", system.pci_ecam_regions().len(), - system.routing().io_apics().len() + system.routing().io_apics().len(), + system.routing().pch_pics().len() ); SYSTEM.call_once(|| system); Ok(()) @@ -447,6 +726,8 @@ unsafe impl Sync for System {} struct AcpiPciNamespace { interpreter: Interpreter, + handler: AcpiHandler, + link_allocator: Mutex, roots: Vec, } @@ -455,6 +736,7 @@ struct AcpiPciRoot { bus: u8, path: String, prt: Option, + link_prt: Option, } impl System { @@ -488,44 +770,46 @@ impl System { pub fn pci_irq_for_endpoint( &self, - address: PciAddress, - interrupt_pin: u8, + info: PciInfo, ) -> Result, OnProbeError> { - let Some(mut irq) = self.resolve_endpoint_gsi(address, interrupt_pin)? else { + let Some(intx_route) = info.intx_route else { + return Ok(None); + }; + let Some(irq) = self.resolve_endpoint_gsi(info.address, intx_route)? else { return Ok(None); }; let Some(gsi) = irq_descriptor_gsi(&irq) else { return Err(OnProbeError::other(format!( "ACPI PCI endpoint {} pin {} returned an invalid IRQ descriptor: {:?}", - address, interrupt_pin, irq + info.address, intx_route.root_pin, irq ))); }; - irq.irq = gsi; + if gsi == 0 { + return Ok(None); + } let Some(route) = self.routing.resolve_gsi(gsi) else { return Err(OnProbeError::other(format!( - "ACPI GSI {} for PCI endpoint {} is not covered by an IOAPIC", - gsi, address + "ACPI GSI {} for PCI endpoint {} is not covered by a registered GSI controller", + gsi, info.address ))); }; + let route = route_with_irq_descriptor_flags(route, &irq); Ok(Some(AcpiPciIrqRoute { - address, - interrupt_pin, - gsi: AcpiGsiRoute { - trigger: irq_trigger(irq.trigger), - polarity: irq_polarity(irq.polarity), - ..route - }, + address: info.address, + interrupt_pin: intx_route.root_pin, + intx_route, + gsi: route, })) } fn resolve_endpoint_gsi( &self, address: PciAddress, - interrupt_pin: u8, + route: PciIntxRoute, ) -> Result, OnProbeError> { - let pin = acpi_pin(interrupt_pin)?; + let pin = acpi_pin(route.root_pin)?; let Some(pci) = &self.pci else { return Ok(None); }; @@ -539,9 +823,24 @@ impl System { continue; }; + if let Some(link_prt) = &root.link_prt + && let Some(route) = link_prt + .route( + u16::from(route.root_device), + u16::from(route.root_function), + pin, + &pci.interpreter, + &pci.handler, + &mut pci.link_allocator.lock(), + ) + .map_err(on_probe_error)? + { + return Ok(Some(route)); + } + match prt.route( - u16::from(address.device()), - u16::from(address.function()), + u16::from(route.root_device), + u16::from(route.root_function), pin, &pci.interpreter, ) { @@ -588,12 +887,9 @@ impl System { ) -> Result>, ProbeError> { let mut out = Vec::new(); for probe in register.probe_kinds { - let ProbeKind::Acpi { ids, on_probe } = probe else { + let ProbeKind::Acpi { on_probe, .. } = probe else { continue; }; - if ids.is_empty() { - continue; - } if self.probed_names.lock().contains(register.name) { continue; } @@ -606,8 +902,9 @@ impl System { let info = AcpiInfo { root: self, path: "\\", + irq_route: None, }; - let res = on_probe(info, PlatformDevice::new(desc)); + let res = on_probe(ProbeAcpi::new(info, PlatformDevice::new(desc))); if res.is_ok() { self.probed_names.lock().insert(register.name); } @@ -646,15 +943,87 @@ fn read_interrupt_routing(tables: &AcpiTables) -> Result() + 8; + +fn read_loongarch_pch_pic_routing(tables: &AcpiTables, routing: &mut AcpiRouting) { + let Some(madt) = tables.find_table::() else { + return; + }; + + let base = madt.virtual_start.as_ptr() as *const u8; + let length = madt.region_length; + if length < RAW_MADT_HEADER_LEN { + return; + } + + let mut offset = RAW_MADT_HEADER_LEN; + while offset + core::mem::size_of::() <= length { + let header = unsafe { (base.add(offset) as *const RawMadtEntryHeader).read_unaligned() }; + let entry_len = usize::from(header.length); + if entry_len < core::mem::size_of::() || offset + entry_len > length { + break; + } + + if header.entry_type == ACPI_MADT_TYPE_BIO_PIC { + read_loongarch_bio_pic_entry(base, offset, entry_len, routing); + } + + offset += entry_len; + } +} + +fn read_loongarch_bio_pic_entry( + base: *const u8, + offset: usize, + entry_len: usize, + routing: &mut AcpiRouting, +) { + if entry_len < core::mem::size_of::() { + warn!("ignore short LoongArch BIO_PIC MADT entry: {entry_len} bytes"); + return; + } + + let entry = unsafe { (base.add(offset) as *const RawMadtBioPic).read_unaligned() }; + + routing.add_pch_pic(AcpiPchPic { + id: entry.id, + address: entry.address, + mmio_size: entry.size, + gsi_count: LOONGARCH_PCH_PIC_GSI_COUNT, + gsi_base: u32::from(entry.gsi_base), + }); +} + fn read_pci_namespace( root: AcpiRoot, ecam_regions: Vec, ) -> Result { let handler = root.handler_with_pci_ecam(ecam_regions); let tables = unsafe { AcpiTables::from_rsdp(handler.clone(), root.rsdp) }?; + let namespace_handler = handler.clone(); let platform = AcpiPlatform::new(tables, handler)?; let interpreter = Interpreter::new_from_platform(&platform)?; interpreter.initialize_namespace(); @@ -673,6 +1042,7 @@ fn read_pci_namespace( bus, path: path.as_string(), prt: None, + link_prt: None, }); } Ok(true) @@ -682,6 +1052,7 @@ fn read_pci_namespace( for root in &mut roots { root.prt = read_pci_routing_table(&interpreter, &root.path)?; + root.link_prt = read_pci_link_routing_table(&interpreter, &root.path)?; } for path in PCI_ROOT_FALLBACK_PATHS { if roots.iter().any(|root| root.path == *path) { @@ -690,15 +1061,22 @@ fn read_pci_namespace( let Some(prt) = read_pci_routing_table(&interpreter, path)? else { continue; }; + let link_prt = read_pci_link_routing_table(&interpreter, path)?; roots.push(AcpiPciRoot { segment: 0, bus: 0, path: path.to_string(), prt: Some(prt), + link_prt, }); } - Ok(AcpiPciNamespace { interpreter, roots }) + Ok(AcpiPciNamespace { + interpreter, + handler: namespace_handler, + link_allocator: Mutex::new(PciLinkAllocator::default()), + roots, + }) } fn read_pci_routing_table( @@ -713,6 +1091,18 @@ fn read_pci_routing_table( } } +fn read_pci_link_routing_table( + interpreter: &Interpreter, + root_path: &str, +) -> Result, AcpiError> { + let prt_path = AmlName::from_str(&format!("{root_path}._PRT")).map_err(AcpiError::Aml)?; + match PciLinkRoutingTable::from_prt_path(prt_path, interpreter) { + Ok(prt) => Ok(Some(prt)), + Err(AmlError::ObjectDoesNotExist(_)) | Err(AmlError::LevelDoesNotExist(_)) => Ok(None), + Err(err) => Err(AcpiError::Aml(err)), + } +} + fn is_pci_root(interpreter: &Interpreter, path: &AmlName) -> bool { has_pci_root_id(interpreter, path, "_HID") || has_pci_root_id(interpreter, path, "_CID") } @@ -779,6 +1169,743 @@ fn decode_eisa_id(raw: u32) -> Option { )) } +#[derive(Debug)] +struct PciLinkRoutingTable { + entries: Vec, +} + +#[derive(Debug)] +struct PciLinkRoute { + device: u16, + function: u16, + pin: Pin, + link: AmlName, + source_index: usize, +} + +impl PciLinkRoutingTable { + fn from_prt_path( + prt_path: AmlName, + interpreter: &Interpreter, + ) -> Result { + let prt = interpreter.evaluate(prt_path.clone(), Vec::new())?; + let Object::Package(ref entries) = *prt else { + return Err(AmlError::InvalidOperationOnObject { + op: acpi::aml::Operation::DecodePrt, + typ: prt.typ(), + }); + }; + + let mut routes = Vec::new(); + for entry in entries { + let Object::Package(ref package) = **entry else { + return Err(AmlError::InvalidOperationOnObject { + op: acpi::aml::Operation::DecodePrt, + typ: entry.typ(), + }); + }; + if package.len() != 4 { + return Err(AmlError::UnexpectedResourceType); + } + + let Object::Integer(address) = *package[0] else { + return Err(AmlError::PrtInvalidAddress); + }; + let entry_device = + u16::try_from((address >> 16) & 0xffff).map_err(|_| AmlError::PrtInvalidAddress)?; + let entry_function = + u16::try_from(address & 0xffff).map_err(|_| AmlError::PrtInvalidAddress)?; + let entry_pin = match *package[1] { + Object::Integer(0) => Pin::IntA, + Object::Integer(1) => Pin::IntB, + Object::Integer(2) => Pin::IntC, + Object::Integer(3) => Pin::IntD, + _ => return Err(AmlError::PrtInvalidPin), + }; + let Object::String(ref source) = *package[2] else { + continue; + }; + let Object::Integer(source_index) = *package[3] else { + return Err(AmlError::PrtInvalidSource); + }; + let source_index = + usize::try_from(source_index).map_err(|_| AmlError::PrtInvalidSource)?; + let link = interpreter + .namespace + .lock() + .search_for_level(&AmlName::from_str(source)?, &prt_path)?; + routes.push(PciLinkRoute { + device: entry_device, + function: entry_function, + pin: entry_pin, + link, + source_index, + }); + } + + Ok(Self { entries: routes }) + } + + fn route( + &self, + device: u16, + function: u16, + pin: Pin, + interpreter: &Interpreter, + handler: &AcpiHandler, + allocator: &mut PciLinkAllocator, + ) -> Result, AmlError> { + let Some(route) = self.entries.iter().find(|entry| { + entry.device == device + && (entry.function == 0xffff || entry.function == function) + && entry.pin == pin + }) else { + return Ok(None); + }; + + resolve_pci_link_irq( + interpreter, + handler, + allocator, + &route.link, + route.source_index, + ) + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum LinkIrqResourceKind { + SmallIrq, + ExtendedIrq, +} + +#[derive(Clone, Debug)] +struct LinkIrqResource { + kind: LinkIrqResourceKind, + descriptor: IrqDescriptor, + irqs: Vec, +} + +struct LinkIrqSelection<'a> { + resource: &'a LinkIrqResource, + irq: u32, + needs_programming: bool, +} + +#[derive(Default)] +struct PciLinkAllocator { + assigned: BTreeMap, + irq_use_count: BTreeMap, +} + +impl PciLinkAllocator { + fn assigned_irq(&self, link: &AmlName) -> Option { + self.assigned.get(&link.as_string()).copied() + } + + fn commit(&mut self, link: &AmlName, irq: u32) { + let key = link.as_string(); + match self.assigned.insert(key, irq) { + Some(old_irq) if old_irq == irq => return, + Some(old_irq) => { + if let Some(count) = self.irq_use_count.get_mut(&old_irq) { + *count = count.saturating_sub(1); + } + } + None => {} + } + *self.irq_use_count.entry(irq).or_default() += 1; + } + + fn penalty(&self, irq: u32) -> usize { + let isa_penalty = match irq { + 0..=2 => usize::MAX / 2, + 3..=8 => 16 * 16 * 16 * 16, + 9..=11 => 0, + 12..=15 => 16 * 16 * 16 * 16 * 16, + _ => 0, + }; + isa_penalty + self.irq_use_count.get(&irq).copied().unwrap_or(0) * 16 * 16 * 16 + } +} + +fn resolve_pci_link_irq( + interpreter: &Interpreter, + handler: &AcpiHandler, + allocator: &mut PciLinkAllocator, + link: &AmlName, + source_index: usize, +) -> Result, AmlError> { + let current = evaluate_link_irq_resource(interpreter, link, "_CRS", source_index)?; + let possible = evaluate_link_irq_resource(interpreter, link, "_PRS", source_index)?; + let Some(selection) = select_pci_link_irq(link, current.as_ref(), possible.as_ref(), allocator) + else { + return Ok(None); + }; + + if selection.needs_programming { + let srs = build_link_srs_buffer(selection.resource, selection.irq)?; + let srs_path = AmlName::from_str("_SRS")?.resolve(link)?; + if let Err(err) = interpreter.evaluate(srs_path.clone(), vec![Object::Buffer(srs).wrap()]) { + if is_buffer_field_to_field_unit_store_gap(&err) { + let _ = interpreter.namespace.lock().remove_level(srs_path); + // Current acpi's AML Store path cannot coerce a BufferField into a FieldUnit. + // SeaBIOS PIRQ link _SRS methods use that exact pattern; program the already + // decoded link field directly so PCI link allocation still follows _PRS/_SRS. + program_known_pci_link_field(interpreter, handler, link, selection.irq)?; + } else { + let _ = interpreter.namespace.lock().remove_level(srs_path); + return Err(err); + } + } + } + + allocator.commit(link, selection.irq); + Ok(Some(selection.resource.descriptor_for_irq(selection.irq))) +} + +fn evaluate_link_irq_resource( + interpreter: &Interpreter, + link: &AmlName, + method: &str, + source_index: usize, +) -> Result, AmlError> { + let path = AmlName::from_str(method)?.resolve(link)?; + let value = match interpreter.evaluate_if_present(path.clone(), Vec::new()) { + Ok(Some(value)) => value, + Ok(None) => return Ok(None), + Err(err) if method == "_CRS" && is_buffer_field_to_field_unit_store_gap(&err) => { + let _ = interpreter.namespace.lock().remove_level(path); + return Ok(None); + } + Err(err) => { + let _ = interpreter.namespace.lock().remove_level(path); + return Err(err); + } + }; + let resources = parse_link_irq_resources(&value)?; + Ok(resources.into_iter().nth(source_index)) +} + +fn is_buffer_field_to_field_unit_store_gap(err: &AmlError) -> bool { + matches!( + err, + AmlError::ObjectNotOfExpectedType { + expected: ObjectType::Integer, + got: ObjectType::BufferField, + } + ) +} + +fn program_known_pci_link_field( + interpreter: &Interpreter, + handler: &AcpiHandler, + link: &AmlName, + irq: u32, +) -> Result<(), AmlError> { + let Some(field_path) = known_pci_link_irq_field(interpreter, link) else { + return Err(AmlError::ObjectNotOfExpectedType { + expected: ObjectType::Integer, + got: ObjectType::BufferField, + }); + }; + let field = interpreter.namespace.lock().get(field_path)?.clone(); + let Object::FieldUnit(ref field) = *field else { + return Err(AmlError::ObjectNotOfExpectedType { + expected: ObjectType::FieldUnit, + got: field.typ(), + }); + }; + write_field_unit(interpreter, handler, field, irq as u64) +} + +fn known_pci_link_irq_field( + interpreter: &Interpreter, + link: &AmlName, +) -> Option { + let mut namespace = interpreter.namespace.lock(); + for field in pci_link_irq_field_candidates(link) { + let path = AmlName::from_str(field).ok()?; + if namespace.get(path.clone()).is_ok() { + return Some(path); + } + } + None +} + +fn pci_link_irq_field_candidates(link: &AmlName) -> &'static [&'static str] { + match link.as_string().rsplit('.').next().unwrap_or_default() { + "LNKA" => &["\\_SB.PRQA", "\\_SB.PRQ0"], + "LNKB" => &["\\_SB.PRQB", "\\_SB.PRQ1"], + "LNKC" => &["\\_SB.PRQC", "\\_SB.PRQ2"], + "LNKD" => &["\\_SB.PRQD", "\\_SB.PRQ3"], + "LNKE" => &["\\_SB.PRQE"], + "LNKF" => &["\\_SB.PRQF"], + "LNKG" => &["\\_SB.PRQG"], + "LNKH" => &["\\_SB.PRQH"], + _ => &[], + } +} + +fn write_field_unit( + interpreter: &Interpreter, + handler: &AcpiHandler, + field: &FieldUnit, + value: u64, +) -> Result<(), AmlError> { + let access_width_bits = field.flags.access_type_bytes()? * 8; + let FieldUnitKind::Normal { ref region } = field.kind else { + return Err(AmlError::LibUnimplemented); + }; + let Object::OpRegion(ref region) = **region else { + return Err(AmlError::ObjectNotOfExpectedType { + expected: ObjectType::OpRegion, + got: region.typ(), + }); + }; + + let value_bytes = value.to_le_bytes(); + let native_accesses = + (field.bit_length + (field.bit_index % access_width_bits)).div_ceil(access_width_bits); + let mut written_so_far = 0; + + for i in 0..native_accesses { + let aligned_bit = align_down(field.bit_index + i * access_width_bits, access_width_bits); + let dst_index = if i == 0 { + field.bit_index % access_width_bits + } else { + 0 + }; + let remaining = field.bit_length - written_so_far; + let length = if i == 0 { + usize::min( + remaining, + access_width_bits - (field.bit_index % access_width_bits), + ) + } else { + usize::min(remaining, access_width_bits) + }; + + let mut bytes = if dst_index > 0 || remaining < access_width_bits { + match field.flags.update_rule() { + FieldUpdateRule::Preserve => read_native_region( + interpreter, + handler, + region, + aligned_bit / 8, + access_width_bits / 8, + )? + .to_le_bytes(), + FieldUpdateRule::WriteAsOnes => [0xff; 8], + FieldUpdateRule::WriteAsZeros => [0; 8], + } + } else { + [0; 8] + }; + + copy_bits(&value_bytes, written_so_far, &mut bytes, dst_index, length); + write_native_region( + interpreter, + handler, + region, + aligned_bit / 8, + access_width_bits / 8, + u64::from_le_bytes(bytes), + )?; + written_so_far += length; + } + + Ok(()) +} + +fn read_native_region( + interpreter: &Interpreter, + handler: &AcpiHandler, + region: &OpRegion, + offset: usize, + length: usize, +) -> Result { + match region.space { + RegionSpace::SystemMemory => { + let address = region.base as usize + offset; + match length { + 1 => Ok(u64::from(handler.read_u8(address))), + 2 => Ok(u64::from(handler.read_u16(address))), + 4 => Ok(u64::from(handler.read_u32(address))), + 8 => Ok(handler.read_u64(address)), + _ => Err(AmlError::InvalidFieldFlags), + } + } + RegionSpace::SystemIO => { + let address = region.base as u16 + offset as u16; + match length { + 1 => Ok(u64::from(handler.read_io_u8(address))), + 2 => Ok(u64::from(handler.read_io_u16(address))), + 4 => Ok(u64::from(handler.read_io_u32(address))), + _ => Err(AmlError::InvalidFieldFlags), + } + } + RegionSpace::PciConfig => { + let address = pci_address_for_region(interpreter, region)?; + let offset = region.base as u16 + offset as u16; + match length { + 1 => Ok(u64::from(handler.read_pci_u8(address, offset))), + 2 => Ok(u64::from(handler.read_pci_u16(address, offset))), + 4 => Ok(u64::from(handler.read_pci_u32(address, offset))), + _ => Err(AmlError::InvalidFieldFlags), + } + } + _ => Err(AmlError::NoHandlerForRegionAccess(region.space)), + } +} + +fn write_native_region( + interpreter: &Interpreter, + handler: &AcpiHandler, + region: &OpRegion, + offset: usize, + length: usize, + value: u64, +) -> Result<(), AmlError> { + match region.space { + RegionSpace::SystemMemory => { + let address = region.base as usize + offset; + match length { + 1 => handler.write_u8(address, value as u8), + 2 => handler.write_u16(address, value as u16), + 4 => handler.write_u32(address, value as u32), + 8 => handler.write_u64(address, value), + _ => return Err(AmlError::InvalidFieldFlags), + } + Ok(()) + } + RegionSpace::SystemIO => { + let address = region.base as u16 + offset as u16; + match length { + 1 => handler.write_io_u8(address, value as u8), + 2 => handler.write_io_u16(address, value as u16), + 4 => handler.write_io_u32(address, value as u32), + _ => return Err(AmlError::InvalidFieldFlags), + } + Ok(()) + } + RegionSpace::PciConfig => { + let address = pci_address_for_region(interpreter, region)?; + let offset = region.base as u16 + offset as u16; + match length { + 1 => handler.write_pci_u8(address, offset, value as u8), + 2 => handler.write_pci_u16(address, offset, value as u16), + 4 => handler.write_pci_u32(address, offset, value as u32), + _ => return Err(AmlError::InvalidFieldFlags), + } + Ok(()) + } + _ => Err(AmlError::NoHandlerForRegionAccess(region.space)), + } +} + +fn pci_address_for_region( + interpreter: &Interpreter, + region: &OpRegion, +) -> Result { + let path = ®ion.parent_device_path; + let segment = eval_integer_child(interpreter, path, "_SEG")?.unwrap_or(0) as u16; + let bus = eval_integer_child(interpreter, path, "_BBN")?.unwrap_or(0) as u8; + let address = eval_integer_child(interpreter, path, "_ADR")?.unwrap_or(0); + Ok(acpi::PciAddress::new( + segment, + bus, + ((address >> 16) & 0xff) as u8, + (address & 0xff) as u8, + )) +} + +fn copy_bits(src: &[u8], src_offset: usize, dst: &mut [u8], dst_offset: usize, length: usize) { + for bit in 0..length { + let src_bit = src_offset + bit; + let dst_bit = dst_offset + bit; + let is_set = src[src_bit / 8] & (1 << (src_bit % 8)) != 0; + if is_set { + dst[dst_bit / 8] |= 1 << (dst_bit % 8); + } else { + dst[dst_bit / 8] &= !(1 << (dst_bit % 8)); + } + } +} + +fn align_down(value: usize, align: usize) -> usize { + assert!(align.is_power_of_two()); + value & !(align - 1) +} + +fn parse_link_irq_resources( + value: &acpi::aml::object::WrappedObject, +) -> Result, AmlError> { + let Object::Buffer(ref bytes) = **value else { + return Err(AmlError::InvalidOperationOnObject { + op: acpi::aml::Operation::ParseResource, + typ: value.typ(), + }); + }; + + let mut resources = Vec::new(); + let mut rest = bytes.as_slice(); + while !rest.is_empty() { + if rest[0] & 0x80 != 0 { + if rest.len() < 3 { + return Err(AmlError::InvalidResourceDescriptor); + } + let length = usize::from(u16::from_le_bytes([rest[1], rest[2]])); + if rest.len() < length + 3 { + return Err(AmlError::InvalidResourceDescriptor); + } + let descriptor = &rest[..length + 3]; + if rest[0] & 0x7f == 0x09 { + resources.push(parse_extended_irq_resource(descriptor)?); + } + rest = &rest[length + 3..]; + } else { + let length = usize::from(rest[0] & 0b111); + if rest.len() < length + 1 { + return Err(AmlError::InvalidResourceDescriptor); + } + let descriptor = &rest[..length + 1]; + match (rest[0] >> 3) & 0x0f { + 0x04 => resources.push(parse_small_irq_resource(descriptor)?), + 0x0f => break, + _ => {} + } + rest = &rest[length + 1..]; + } + } + Ok(resources) +} + +fn parse_extended_irq_resource(bytes: &[u8]) -> Result { + if bytes.len() < 5 { + return Err(AmlError::InvalidResourceDescriptor); + } + let count = usize::from(bytes[4]); + if bytes.len() < 5 + count * 4 { + return Err(AmlError::InvalidResourceDescriptor); + } + + let mut irqs = Vec::new(); + for idx in 0..count { + let start = 5 + idx * 4; + irqs.push(u32::from_le_bytes([ + bytes[start], + bytes[start + 1], + bytes[start + 2], + bytes[start + 3], + ])); + } + + Ok(LinkIrqResource { + kind: LinkIrqResourceKind::ExtendedIrq, + descriptor: IrqDescriptor { + is_consumer: bytes[3] & 0b0000_0001 != 0, + trigger: if bytes[3] & 0b0000_0010 != 0 { + InterruptTrigger::Edge + } else { + InterruptTrigger::Level + }, + polarity: if bytes[3] & 0b0000_0100 != 0 { + InterruptPolarity::ActiveLow + } else { + InterruptPolarity::ActiveHigh + }, + is_shared: bytes[3] & 0b0000_1000 != 0, + is_wake_capable: bytes[3] & 0b0001_0000 != 0, + irq: irqs.first().copied().unwrap_or(0), + }, + irqs, + }) +} + +fn parse_small_irq_resource(bytes: &[u8]) -> Result { + if bytes.len() != 3 && bytes.len() != 4 { + return Err(AmlError::InvalidResourceDescriptor); + } + + let mask = u16::from_le_bytes([bytes[1], bytes[2]]); + let mut irqs = Vec::new(); + for irq in 0..16 { + if mask & (1 << irq) != 0 { + irqs.push(irq); + } + } + + let (trigger, polarity, is_shared, is_wake_capable) = if bytes.len() == 4 { + ( + if bytes[3] & 0b0000_0001 != 0 { + InterruptTrigger::Edge + } else { + InterruptTrigger::Level + }, + if bytes[3] & 0b0000_1000 != 0 { + InterruptPolarity::ActiveLow + } else { + InterruptPolarity::ActiveHigh + }, + bytes[3] & 0b0001_0000 != 0, + bytes[3] & 0b0010_0000 != 0, + ) + } else { + ( + InterruptTrigger::Edge, + InterruptPolarity::ActiveHigh, + false, + false, + ) + }; + + Ok(LinkIrqResource { + kind: LinkIrqResourceKind::SmallIrq, + descriptor: IrqDescriptor { + is_consumer: false, + trigger, + polarity, + is_shared, + is_wake_capable, + irq: u32::from(mask), + }, + irqs, + }) +} + +fn select_pci_link_irq<'a>( + link: &AmlName, + current: Option<&'a LinkIrqResource>, + possible: Option<&'a LinkIrqResource>, + allocator: &PciLinkAllocator, +) -> Option> { + if let Some(irq) = allocator.assigned_irq(link) { + let resource = possible + .filter(|possible| possible.irqs.contains(&irq)) + .or(current.filter(|current| current.irqs.contains(&irq)))?; + return Some(LinkIrqSelection { + resource, + irq, + needs_programming: current.is_none_or(|current| current.irqs.first() != Some(&irq)), + }); + } + + if let Some(current) = current + && let Some(irq) = current.irqs.first().copied().filter(|irq| *irq != 0) + && possible.is_none_or(|possible| possible.irqs.contains(&irq)) + { + return Some(LinkIrqSelection { + resource: possible.unwrap_or(current), + irq, + needs_programming: false, + }); + } + + let possible = possible?; + let irq = preferred_pci_link_irq(&possible.irqs, allocator)?; + Some(LinkIrqSelection { + resource: possible, + irq, + needs_programming: true, + }) +} + +fn preferred_pci_link_irq(irqs: &[u32], allocator: &PciLinkAllocator) -> Option { + irqs.iter() + .copied() + .filter(|irq| *irq != 0) + .min_by_key(|irq| (allocator.penalty(*irq), link_irq_tiebreaker(*irq))) +} + +fn link_irq_tiebreaker(irq: u32) -> usize { + match irq { + 10 => 0, + 11 => 1, + 9 => 2, + 16.. => 3 + irq as usize, + _ => 1024 + irq as usize, + } +} + +fn build_link_srs_buffer(resource: &LinkIrqResource, irq: u32) -> Result, AmlError> { + match resource.kind { + LinkIrqResourceKind::ExtendedIrq => { + let mut buffer = Vec::new(); + buffer.extend_from_slice(&[ + 0x89, + 0x06, + 0x00, + extended_irq_flags(&resource.descriptor), + 1, + ]); + buffer.extend_from_slice(&irq.to_le_bytes()); + buffer.extend_from_slice(&[0x79, 0x00]); + Ok(buffer) + } + LinkIrqResourceKind::SmallIrq => { + if irq >= 16 { + return Err(AmlError::InvalidResourceDescriptor); + } + let mask = 1u16 << irq; + let mut buffer = Vec::new(); + buffer.push((0x04 << 3) | 3); + buffer.extend_from_slice(&mask.to_le_bytes()); + buffer.push(small_irq_flags(&resource.descriptor)); + buffer.extend_from_slice(&[0x79, 0x00]); + Ok(buffer) + } + } +} + +impl LinkIrqResource { + fn descriptor_for_irq(&self, irq: u32) -> IrqDescriptor { + let mut descriptor = self.descriptor.clone(); + descriptor.irq = match self.kind { + LinkIrqResourceKind::SmallIrq if irq < 16 => 1u32 << irq, + _ => irq, + }; + descriptor + } +} + +fn extended_irq_flags(descriptor: &IrqDescriptor) -> u8 { + let mut flags = 0; + if descriptor.is_consumer { + flags |= 1 << 0; + } + if descriptor.trigger == InterruptTrigger::Edge { + flags |= 1 << 1; + } + if descriptor.polarity == InterruptPolarity::ActiveLow { + flags |= 1 << 2; + } + if descriptor.is_shared { + flags |= 1 << 3; + } + if descriptor.is_wake_capable { + flags |= 1 << 4; + } + flags +} + +fn small_irq_flags(descriptor: &IrqDescriptor) -> u8 { + let mut flags = 0; + if descriptor.trigger == InterruptTrigger::Edge { + flags |= 1 << 0; + } + if descriptor.polarity == InterruptPolarity::ActiveLow { + flags |= 1 << 3; + } + if descriptor.is_shared { + flags |= 1 << 4; + } + if descriptor.is_wake_capable { + flags |= 1 << 5; + } + flags +} + fn acpi_pin(interrupt_pin: u8) -> Result { match interrupt_pin { 1 => Ok(Pin::IntA), @@ -800,17 +1927,28 @@ fn irq_descriptor_gsi(descriptor: &IrqDescriptor) -> Option { } } -fn irq_trigger(trigger: InterruptTrigger) -> AcpiIrqTrigger { +fn route_with_irq_descriptor_flags( + route: AcpiGsiRoute, + descriptor: &IrqDescriptor, +) -> AcpiGsiRoute { + AcpiGsiRoute { + trigger: irq_trigger(descriptor.trigger), + polarity: irq_polarity(descriptor.polarity), + ..route + } +} + +fn irq_trigger(trigger: acpi::aml::resource::InterruptTrigger) -> AcpiIrqTrigger { match trigger { - InterruptTrigger::Edge => AcpiIrqTrigger::Edge, - InterruptTrigger::Level => AcpiIrqTrigger::Level, + acpi::aml::resource::InterruptTrigger::Edge => AcpiIrqTrigger::Edge, + acpi::aml::resource::InterruptTrigger::Level => AcpiIrqTrigger::Level, } } -fn irq_polarity(polarity: InterruptPolarity) -> AcpiIrqPolarity { +fn irq_polarity(polarity: acpi::aml::resource::InterruptPolarity) -> AcpiIrqPolarity { match polarity { - InterruptPolarity::ActiveHigh => AcpiIrqPolarity::ActiveHigh, - InterruptPolarity::ActiveLow => AcpiIrqPolarity::ActiveLow, + acpi::aml::resource::InterruptPolarity::ActiveHigh => AcpiIrqPolarity::ActiveHigh, + acpi::aml::resource::InterruptPolarity::ActiveLow => AcpiIrqPolarity::ActiveLow, } } diff --git a/drivers/rdrive/src/probe/fdt/mod.rs b/drivers/rdrive/src/probe/fdt/mod.rs index 8531d0f1d5..0049790b6b 100644 --- a/drivers/rdrive/src/probe/fdt/mod.rs +++ b/drivers/rdrive/src/probe/fdt/mod.rs @@ -80,7 +80,30 @@ impl<'a> FdtInfo<'a> { } } -pub type FnOnProbe = fn(fdt: FdtInfo<'_>, plat_dev: PlatformDevice) -> Result<(), OnProbeError>; +pub struct ProbeFdt<'a> { + info: FdtInfo<'a>, + platform: PlatformDevice, +} + +impl<'a> ProbeFdt<'a> { + pub(crate) fn new(info: FdtInfo<'a>, platform: PlatformDevice) -> Self { + Self { info, platform } + } + + pub const fn info(&self) -> &FdtInfo<'a> { + &self.info + } + + pub fn into_platform_device(self) -> PlatformDevice { + self.platform + } + + pub fn into_parts(self) -> (FdtInfo<'a>, PlatformDevice) { + (self.info, self.platform) + } +} + +pub type FnOnProbe = for<'a> fn(ProbeFdt<'a>) -> Result<(), OnProbeError>; pub struct System { fdt: Fdt, @@ -192,13 +215,13 @@ impl System { irq_parent, }; - let res = (node_info.on_probe)( + let res = (node_info.on_probe)(ProbeFdt::new( FdtInfo { node, phandle_2_device_id: phandle_map, }, PlatformDevice::new(descriptor), - ); + )); if res.is_ok() { self.probed_names.lock().insert(node_info.name); diff --git a/drivers/rdrive/src/probe/pci/mod.rs b/drivers/rdrive/src/probe/pci/mod.rs index 0369478478..395e47e4bd 100644 --- a/drivers/rdrive/src/probe/pci/mod.rs +++ b/drivers/rdrive/src/probe/pci/mod.rs @@ -2,7 +2,7 @@ use alloc::{collections::btree_set::BTreeSet, vec::Vec}; use core::ops::{Deref, DerefMut}; use ::pcie::*; -pub use ::pcie::{Endpoint, PciCapability, PcieGeneric}; +pub use ::pcie::{Endpoint, PciCapability, PciIntxRoute, PcieGeneric}; use mmio_api::{MapError, MmioOp}; pub use rdif_pcie::{DriverGeneric, PciAddress, PciMem32, PciMem64, PcieController}; use spin::{Mutex, Once}; @@ -15,7 +15,7 @@ use crate::{ static PCIE: Once>> = Once::new(); -pub type FnOnProbe = fn(ep: &mut EndpointRc, plat_dev: PlatformDevice) -> Result<(), OnProbeError>; +pub type FnOnProbe = fn(ProbePci<'_>) -> Result<(), OnProbeError>; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] struct Id { @@ -69,6 +69,69 @@ impl EndpointRc { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct PciInfo { + pub address: PciAddress, + pub interrupt_pin: u8, + pub interrupt_line: u8, + pub intx_route: Option, +} + +impl PciInfo { + fn from_endpoint(endpoint: &EndpointRc, intx_route: Option) -> Self { + Self { + address: endpoint.address(), + interrupt_pin: endpoint.interrupt_pin(), + interrupt_line: endpoint.interrupt_line(), + intx_route, + } + } +} + +pub struct ProbePci<'a> { + info: PciInfo, + endpoint: &'a mut EndpointRc, + platform: PlatformDevice, +} + +impl<'a> ProbePci<'a> { + pub(crate) fn new( + info: PciInfo, + endpoint: &'a mut EndpointRc, + platform: PlatformDevice, + ) -> Self { + Self { + info, + endpoint, + platform, + } + } + + pub const fn info(&self) -> PciInfo { + self.info + } + + pub fn endpoint(&self) -> &Endpoint { + self.endpoint + } + + pub fn endpoint_mut(&mut self) -> &mut EndpointRc { + self.endpoint + } + + pub fn take_endpoint(&mut self) -> Endpoint { + self.endpoint.take() + } + + pub fn into_platform_device(self) -> PlatformDevice { + self.platform + } + + pub fn into_parts(self) -> (PciInfo, &'a mut EndpointRc, PlatformDevice) { + (self.info, self.endpoint, self.platform) + } +} + impl Deref for EndpointRc { type Target = Endpoint; @@ -96,8 +159,8 @@ impl PcieEnumterator { ) -> Result<(), ProbeError> { let mut g = self.ctrl.lock().unwrap(); - for ep in enumerate_by_controller(&mut g, None) { - debug!("PCIe endpiont: {}", ep); + for ep in enumerate_by_controller_with_info(&mut g, None) { + debug!("PCIe endpiont: {}", ep.endpoint); match self.probe_one(ep, registers, stop_if_fail) { Ok(_) => {} // Successfully probed, move to the next Err(e) => { @@ -115,10 +178,12 @@ impl PcieEnumterator { fn probe_one( &mut self, - endpoint: Endpoint, + endpoint: EnumeratedEndpoint, registers: &[DriverRegister], stop_if_fail: bool, ) -> Result<(), ProbeError> { + let intx_route = endpoint.intx_route; + let endpoint = endpoint.endpoint; let id = Id { vendor: endpoint.vendor_id(), device: endpoint.device_id(), @@ -143,8 +208,9 @@ impl PcieEnumterator { desc.name = register.name; desc.irq_parent = self.ctrl.descriptor().irq_parent; + let info = PciInfo::from_endpoint(&endpoint, intx_route); let plat_dev = PlatformDevice::new(desc); - match (pci_probe)(&mut endpoint, plat_dev) { + match (pci_probe)(ProbePci::new(info, &mut endpoint, plat_dev)) { Ok(_) => { self.probed.insert(id); return Ok(()); diff --git a/drivers/rdrive/src/register/mod.rs b/drivers/rdrive/src/register/mod.rs index dac96e5f8f..af2b6c9009 100644 --- a/drivers/rdrive/src/register/mod.rs +++ b/drivers/rdrive/src/register/mod.rs @@ -3,8 +3,12 @@ use core::ops::Deref; pub use fdt::NodeType as Node; -pub use crate::probe::fdt::FdtInfo; use crate::probe::{acpi, fdt, pci, static_}; +pub use crate::probe::{ + acpi::{AcpiInfo, ProbeAcpi}, + fdt::{FdtInfo, ProbeFdt}, + pci::{PciInfo, ProbePci}, +}; #[repr(transparent)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] diff --git a/drivers/usb/test_crates/test_hub/tests/test_dwc.rs b/drivers/usb/test_crates/test_hub/tests/test_dwc.rs index 31c77b3372..ee043f7c44 100644 --- a/drivers/usb/test_crates/test_hub/tests/test_dwc.rs +++ b/drivers/usb/test_crates/test_hub/tests/test_dwc.rs @@ -3,7 +3,7 @@ #![no_main] #![feature(used_with_arg)] -use rdrive::{Phandle, PlatformDevice, probe::OnProbeError, register::FdtInfo}; +use rdrive::{Phandle, probe::OnProbeError, register::ProbeFdt}; extern crate alloc; @@ -655,7 +655,8 @@ rdrive::module_driver! { }], } -fn on_probe_cru(node: FdtInfo<'_>, dev: PlatformDevice) -> Result<(), OnProbeError> { +fn on_probe_cru(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (node, dev) = probe.into_parts(); // Initialization code for CRU can be added here if needed. // 获取 CRU 寄存器基址 let Some(reg) = node.node.reg().and_then(|mut r| r.next()) else { diff --git a/drivers/usb/usb-host/src/backend/kmod/dwc/mod.rs b/drivers/usb/usb-host/src/backend/kmod/dwc/mod.rs index 0030701bc1..f56e552b56 100644 --- a/drivers/usb/usb-host/src/backend/kmod/dwc/mod.rs +++ b/drivers/usb/usb-host/src/backend/kmod/dwc/mod.rs @@ -698,6 +698,16 @@ impl CoreOp for Dwc { }) } + fn enable_irq(&mut self) -> Result<()> { + self.xhci.enable_irq(); + Ok(()) + } + + fn disable_irq(&mut self) -> Result<()> { + self.xhci.disable_irq(); + Ok(()) + } + fn new_addressed_device<'a>( &'a mut self, addr: DeviceAddressInfo, diff --git a/drivers/usb/usb-host/src/backend/kmod/kcore.rs b/drivers/usb/usb-host/src/backend/kmod/kcore.rs index 51ae48c366..7d5822fd99 100644 --- a/drivers/usb/usb-host/src/backend/kmod/kcore.rs +++ b/drivers/usb/usb-host/src/backend/kmod/kcore.rs @@ -33,6 +33,14 @@ pub trait CoreOp: Send + 'static { fn create_event_handler(&mut self) -> Box; + fn enable_irq(&mut self) -> Result<(), USBError> { + Err(USBError::NotSupported) + } + + fn disable_irq(&mut self) -> Result<(), USBError> { + Err(USBError::NotSupported) + } + fn kernel(&self) -> &Kernel; } @@ -191,6 +199,14 @@ impl BackendOp for Core { fn create_event_handler(&mut self) -> Box { self.backend.create_event_handler() } + + fn enable_irq(&mut self) -> Result<(), USBError> { + self.backend.enable_irq() + } + + fn disable_irq(&mut self) -> Result<(), USBError> { + self.backend.disable_irq() + } } #[derive(Debug, Clone)] diff --git a/drivers/usb/usb-host/src/backend/kmod/xhci/host.rs b/drivers/usb/usb-host/src/backend/kmod/xhci/host.rs index 3b4fbe680a..afd2ef2971 100644 --- a/drivers/usb/usb-host/src/backend/kmod/xhci/host.rs +++ b/drivers/usb/usb-host/src/backend/kmod/xhci/host.rs @@ -76,6 +76,16 @@ impl CoreOp for Xhci { ) } + fn enable_irq(&mut self) -> Result<()> { + Self::enable_irq(self); + Ok(()) + } + + fn disable_irq(&mut self) -> Result<()> { + Self::disable_irq(self); + Ok(()) + } + fn kernel(&self) -> &Kernel { &self.kernel } diff --git a/drivers/usb/usb-host/src/backend/mod.rs b/drivers/usb/usb-host/src/backend/mod.rs index 56a46d338a..8c532ec4ed 100644 --- a/drivers/usb/usb-host/src/backend/mod.rs +++ b/drivers/usb/usb-host/src/backend/mod.rs @@ -49,4 +49,12 @@ pub(crate) trait BackendOp: Send + Any + 'static { #[cfg(kmod)] fn create_event_handler(&mut self) -> Box; + + fn enable_irq(&mut self) -> Result<(), USBError> { + Err(USBError::NotSupported) + } + + fn disable_irq(&mut self) -> Result<(), USBError> { + Err(USBError::NotSupported) + } } diff --git a/drivers/usb/usb-host/src/host.rs b/drivers/usb/usb-host/src/host.rs index acb719b8d7..bc83ce4737 100644 --- a/drivers/usb/usb-host/src/host.rs +++ b/drivers/usb/usb-host/src/host.rs @@ -44,6 +44,14 @@ impl USBHost { EventHandler { handler } } + pub fn enable_irq(&mut self) -> Result { + self.backend.enable_irq() + } + + pub fn disable_irq(&mut self) -> Result { + self.backend.disable_irq() + } + pub async fn open_device(&mut self, dev: &DeviceInfo) -> Result { let device = self.backend.open_device(dev.inner.as_ref()).await?; let mut device: Device = device.into(); @@ -62,3 +70,64 @@ impl EventHandler { self.handler.handle_event() } } + +#[cfg(test)] +mod tests { + use alloc::sync::Arc; + use core::sync::atomic::{AtomicUsize, Ordering}; + + use futures::{FutureExt, future::LocalBoxFuture}; + use usb_if::err::USBError; + + use super::*; + use crate::backend::{BackendOp, ty::DeviceOp}; + + #[derive(Default)] + struct IrqCalls { + enable: AtomicUsize, + disable: AtomicUsize, + } + + struct TestBackend { + calls: Arc, + } + + impl BackendOp for TestBackend { + fn init<'a>(&'a mut self) -> futures::future::BoxFuture<'a, crate::err::Result> { + async { Ok(()) }.boxed() + } + + fn open_device<'a>( + &'a mut self, + _dev: &'a dyn crate::backend::ty::DeviceInfoOp, + ) -> LocalBoxFuture<'a, crate::err::Result>> { + async { Err(USBError::NotSupported) }.boxed_local() + } + + fn enable_irq(&mut self) -> crate::err::Result { + self.calls.enable.fetch_add(1, Ordering::Relaxed); + Ok(()) + } + + fn disable_irq(&mut self) -> crate::err::Result { + self.calls.disable.fetch_add(1, Ordering::Relaxed); + Ok(()) + } + } + + #[test] + fn host_irq_control_forwards_to_backend() { + let calls = Arc::new(IrqCalls::default()); + let mut host = USBHost { + backend: Box::new(TestBackend { + calls: calls.clone(), + }), + }; + + host.enable_irq().unwrap(); + host.disable_irq().unwrap(); + + assert_eq!(calls.enable.load(Ordering::Relaxed), 1); + assert_eq!(calls.disable.load(Ordering::Relaxed), 1); + } +} diff --git a/net/ax-net/src/device/driver.rs b/net/ax-net/src/device/driver.rs index fceb39234c..e9d715e03f 100644 --- a/net/ax-net/src/device/driver.rs +++ b/net/ax-net/src/device/driver.rs @@ -70,6 +70,8 @@ pub trait NetTxBuffer: Send { pub trait EthernetDriver: Send + Sync { fn device_name(&self) -> &str; fn irq_num(&self) -> Option; + fn enable_irq(&mut self); + fn disable_irq(&mut self); fn mac_address(&self) -> [u8; 6]; fn alloc_tx_buffer(&mut self, size: usize) -> NetDeviceResult>; fn recycle_tx_buffers(&mut self) -> NetDeviceResult; @@ -126,26 +128,22 @@ struct RdNetState { pub struct RdNetDriver { name: String, mac: [u8; 6], - irq_num: Option, + irq: Option, irq_handler: Option, state: SpinNoIrq, } impl RdNetDriver { - pub fn new( - name: impl Into, - mut net: Net, - irq_num: Option, - ) -> NetDeviceResult { + pub fn new(name: impl Into, mut net: Net, irq: Option) -> NetDeviceResult { let mac = net.mac_address(); let tx_queue = net.create_tx_queue().map_err(map_net_error)?; let rx_queue = net.create_rx_queue().map_err(map_net_error)?; - let irq_handler = irq_num.map(|_| net.irq_handler()); + let irq_handler = irq.as_ref().map(|_| net.irq_handler()); Ok(Self { name: name.into(), mac, - irq_num, + irq, irq_handler, state: SpinNoIrq::new(RdNetState { tx_queue, @@ -174,7 +172,19 @@ impl EthernetDriver for RdNetDriver { } fn irq_num(&self) -> Option { - self.irq_num + self.irq + } + + fn enable_irq(&mut self) { + if let Some(handler) = &self.irq_handler { + handler.enable(); + } + } + + fn disable_irq(&mut self) { + if let Some(handler) = &self.irq_handler { + handler.disable(); + } } fn mac_address(&self) -> [u8; 6] { diff --git a/net/ax-net/src/device/ethernet.rs b/net/ax-net/src/device/ethernet.rs index ae7d004543..5c669f897a 100644 --- a/net/ax-net/src/device/ethernet.rs +++ b/net/ax-net/src/device/ethernet.rs @@ -20,6 +20,66 @@ use crate::{ const EMPTY_MAC: EthernetAddress = EthernetAddress([0; 6]); +pub trait EthernetIrqRegistration: Send + Sync {} + +#[derive(Clone, Copy)] +pub struct EthernetIrqAction { + data: NonNull<()>, + handler: unsafe fn(NonNull<()>) -> EthernetIrqOutcome, +} + +impl EthernetIrqAction { + pub const fn new( + data: NonNull<()>, + handler: unsafe fn(NonNull<()>) -> EthernetIrqOutcome, + ) -> Self { + Self { data, handler } + } + + /// Runs the IRQ action. + /// + /// # Safety + /// + /// The caller must ensure `data` still points to the Ethernet IRQ state + /// expected by `handler`, and that the associated registration is still + /// alive while the handler runs. + pub unsafe fn run(self) -> EthernetIrqOutcome { + unsafe { (self.handler)(self.data) } + } +} + +unsafe impl Send for EthernetIrqAction {} +unsafe impl Sync for EthernetIrqAction {} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum EthernetIrqOutcome { + Handled, + Wake, +} + +pub trait EthernetIrqRegistrar: Send + Sync { + fn register_shared( + &self, + name: &str, + irq: usize, + action: EthernetIrqAction, + ) -> Result, EthernetIrqRegistrationError>; +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum EthernetIrqRegistrationError { + InvalidIrq, + Busy, + Unsupported, + Other, +} + +static ETHERNET_IRQ_REGISTRAR: spin::Once<&'static dyn EthernetIrqRegistrar> = spin::Once::new(); + +pub fn set_ethernet_irq_registrar(registrar: &'static dyn EthernetIrqRegistrar) { + ETHERNET_IRQ_REGISTRAR.call_once(|| registrar); +} + struct Neighbor { hardware_address: EthernetAddress, expires_at: Instant, @@ -30,10 +90,10 @@ struct PendingNeighbor { } struct EthernetIrqState { - irq_num: Option, + irq: Option, + irq_registration: spin::Once>, driver: SpinNoIrq>, poll_ready: PollSet, - irq_handle: spin::Once, } impl EthernetIrqState { @@ -52,17 +112,14 @@ pub struct EthernetDevice { pending_packets: PacketBuffer<'static, IpAddress>, } -unsafe fn handle_ethernet_irq( - _ctx: ax_hal::irq::IrqContext, - data: NonNull<()>, -) -> ax_hal::irq::IrqReturn { +unsafe fn handle_ethernet_irq(data: NonNull<()>) -> EthernetIrqOutcome { let state = unsafe { data.cast::().as_ref() }; let events = state.handle_irq(); if events.intersects(NetIrqEvents::RX_READY | NetIrqEvents::RX_ERROR | NetIrqEvents::TX_DONE) { state.poll_ready.wake(); - return ax_hal::irq::IrqReturn::Wake; + return EthernetIrqOutcome::Wake; } - ax_hal::irq::IrqReturn::Handled + EthernetIrqOutcome::Handled } impl EthernetDevice { @@ -76,12 +133,12 @@ impl EthernetDevice { const ARP_REQUEST_RETRY: Duration = Duration::from_secs(1); pub fn new(name: String, inner: Box, ip: Option) -> Self { - let irq_num = inner.irq_num(); + let irq = inner.irq_num(); let mut inner = Arc::new(EthernetIrqState { - irq_num, + irq, + irq_registration: spin::Once::new(), driver: SpinNoIrq::new(inner), poll_ready: PollSet::new(), - irq_handle: spin::Once::new(), }); let pending_packets = PacketBuffer::new( vec![PacketMetadata::EMPTY; ETHERNET_MAX_PENDING_PACKETS], @@ -91,15 +148,27 @@ impl EthernetDevice { * ETHERNET_MAX_PENDING_PACKETS ], ); - if let Some(irq) = inner.irq_num { + if let Some(irq) = inner.irq { let data = NonNull::from(Arc::get_mut(&mut inner).expect("new Arc is unique")).cast(); - match ax_hal::irq::request_shared_irq(irq, handle_ethernet_irq, data) { - Ok(handle) => { - inner.irq_handle.call_once(|| handle); - } - Err(err) => { - warn!("failed to register ethernet irq handler for irq {irq}: {err:?}"); + if let Some(registrar) = ETHERNET_IRQ_REGISTRAR.get().copied() { + let action = EthernetIrqAction::new(data, handle_ethernet_irq); + match registrar.register_shared(&name, irq, action) { + Ok(registration) => { + inner.irq_registration.call_once(|| registration); + inner.driver.lock().enable_irq(); + } + Err(err) => { + warn!( + "failed to register ethernet irq handler for {name} irq {}: {err:?}", + irq + ); + } } + } else { + warn!( + "ethernet irq registrar is not installed for {name} irq {}; use polling", + irq + ); } } @@ -374,16 +443,6 @@ impl EthernetDevice { } } -impl Drop for EthernetIrqState { - fn drop(&mut self) { - if let Some(handle) = self.irq_handle.get().copied() - && let Err(err) = ax_hal::irq::free_irq(handle) - { - warn!("failed to free ethernet irq handler: {err:?}"); - } - } -} - impl Device for EthernetDevice { fn name(&self) -> &str { &self.name @@ -503,7 +562,7 @@ impl Device for EthernetDevice { } fn register_waker(&self, waker: &Waker) { - if self.inner.irq_num.is_some() { + if self.inner.irq_registration.get().is_some() { self.inner.poll_ready.register(waker); } } diff --git a/net/ax-net/src/lib.rs b/net/ax-net/src/lib.rs index a4444022cb..028b229805 100644 --- a/net/ax-net/src/lib.rs +++ b/net/ax-net/src/lib.rs @@ -65,8 +65,10 @@ pub use self::device::{VsockDevice, VsockDeviceList}; pub use self::{ config::{Ipv4InterfaceConfig, NetworkConfig, StaticIpConfig}, device::{ - ArpEntry, EthernetDeviceList, EthernetDriver, NetDeviceError, NetDeviceResult, - NetIrqEvents, NetRxBuffer, NetTxBuffer, RdNetDriver, + ArpEntry, EthernetDeviceList, EthernetDriver, EthernetIrqAction, EthernetIrqOutcome, + EthernetIrqRegistrar, EthernetIrqRegistration, EthernetIrqRegistrationError, + NetDeviceError, NetDeviceResult, NetIrqEvents, NetRxBuffer, NetTxBuffer, RdNetDriver, + set_ethernet_irq_registrar, }, socket::{ CMsgData, RecvFlags, RecvOptions, SendFlags, SendOptions, Shutdown, Socket, SocketAddrEx, diff --git a/os/StarryOS/kernel/Cargo.toml b/os/StarryOS/kernel/Cargo.toml index 8a0f88b4a2..866dad47ef 100644 --- a/os/StarryOS/kernel/Cargo.toml +++ b/os/StarryOS/kernel/Cargo.toml @@ -26,6 +26,8 @@ k230-kpu = ["ax-feat/xuantie-c9xx", "dep:axklib", "dep:k230-kpu"] plat-dyn = [ "dep:axplat-dyn", "ax-feat/plat-dyn", + "ax-feat/usb", + "ax-driver/usb", ] vsock = ["ax-feat/vsock"] dynamic_debug = ["ax-feat/ipi"] diff --git a/os/StarryOS/kernel/src/pseudofs/usbfs/irq.rs b/os/StarryOS/kernel/src/pseudofs/usbfs/irq.rs index 60dac14e2b..277671f25a 100644 --- a/os/StarryOS/kernel/src/pseudofs/usbfs/irq.rs +++ b/os/StarryOS/kernel/src/pseudofs/usbfs/irq.rs @@ -7,7 +7,6 @@ use core::{ use ax_kspin::SpinNoIrq; use ax_lazyinit::LazyInit; -use crab_usb::{Event, EventHandler}; use rdrive::DeviceId as RDriveDeviceId; use super::manager::UsbFsManager; @@ -19,13 +18,13 @@ pub(super) struct PendingUsbIrqSlot { pub(super) irq_num: usize, pub(super) device_id: RDriveDeviceId, pub(super) bus_num: u8, - pub(super) handler: EventHandler, + pub(super) handler: ax_driver::usb::UsbHostIrqHandler, } pub(super) struct UsbIrqSlot { device_id: RDriveDeviceId, bus_num: u8, - handler: EventHandler, + handler: ax_driver::usb::UsbHostIrqHandler, dirty: AtomicBool, handle: SpinNoIrq>, } @@ -156,29 +155,29 @@ fn usbfs_irq_handler(irq_num: usize) { let mut stopped_events = 0usize; loop { handler_calls += 1; - match slot.handler.handle_event() { - Event::PortChange { port } => { + match slot.handler.handle() { + crab_usb::Event::PortChange { port } => { port_events += 1; trace!( "usbfs: IRQ {} bus {} host {:?}: port change on port {}", irq_num, slot.bus_num, slot.device_id, port ); } - Event::TransferActivity { count } => { + crab_usb::Event::TransferActivity { count } => { transfer_events += count; trace!( "usbfs: IRQ {} bus {} host {:?}: {} transfer event(s)", irq_num, slot.bus_num, slot.device_id, count ); } - Event::Stopped => { + crab_usb::Event::Stopped => { stopped_events += 1; trace!( "usbfs: IRQ {} bus {} host {:?}: event handler stopped", irq_num, slot.bus_num, slot.device_id ); } - Event::Nothing => break, + crab_usb::Event::Nothing => break, } } diff --git a/os/StarryOS/kernel/src/pseudofs/usbfs/manager.rs b/os/StarryOS/kernel/src/pseudofs/usbfs/manager.rs index 29deb52e42..5c82bf6553 100644 --- a/os/StarryOS/kernel/src/pseudofs/usbfs/manager.rs +++ b/os/StarryOS/kernel/src/pseudofs/usbfs/manager.rs @@ -9,7 +9,7 @@ use ax_errno::{AxError, AxResult, LinuxError}; use ax_kspin::SpinNoIrq as Mutex; use ax_sync::Mutex as BlockingMutex; use crab_usb::{ - Device, DeviceInfo, Endpoint, EventHandler, ProbedDevice, + Device, DeviceInfo, Endpoint, ProbedDevice, usb_if::{ endpoint::{RequestId, TransferCompletion, TransferRequest}, err::{TransferError, USBError}, @@ -1307,16 +1307,16 @@ pub(super) fn discover_hosts() -> (Vec, Vec) { }; let irq_num = guard.irq_num(); - info!("usbfs: creating event handler for bus {}", bus_num); - let event_handler: EventHandler = guard.host_mut().create_event_handler(); + let irq_handler = guard.take_irq_handler(); drop(guard); - if let Some(irq_num) = irq_num { + if let Some((irq, handler)) = irq_handler { + debug_assert_eq!(irq_num, Some(irq)); irq_slots.push(PendingUsbIrqSlot { - irq_num, + irq_num: irq, device_id, bus_num, - handler: event_handler, + handler, }); } diff --git a/os/arceos/api/axfeat/Cargo.toml b/os/arceos/api/axfeat/Cargo.toml index 1cc2d7f4fd..8cbad8d231 100644 --- a/os/arceos/api/axfeat/Cargo.toml +++ b/os/arceos/api/axfeat/Cargo.toml @@ -96,6 +96,9 @@ input = [ "ax-runtime/input", ] +# USB +usb = ["irq", "ax-driver?/usb"] + # Real Time Clock (RTC) Driver. rtc = ["ax-hal/rtc", "ax-runtime/rtc"] diff --git a/os/arceos/modules/axruntime/src/block/mod.rs b/os/arceos/modules/axruntime/src/block/mod.rs index 9a80deac2c..a5caeaaa01 100644 --- a/os/arceos/modules/axruntime/src/block/mod.rs +++ b/os/arceos/modules/axruntime/src/block/mod.rs @@ -1,66 +1,85 @@ #[cfg(feature = "fs-ng")] use alloc::vec::Vec; -#[cfg(all( - feature = "irq", - any(all(feature = "fs", not(feature = "fs-ng")), feature = "fs-ng") -))] -use core::ptr::NonNull; -#[cfg(feature = "fs-ng")] -mod root; -#[cfg(any(feature = "fs-ng", test))] -pub(crate) mod volume; +#[cfg(any(all(feature = "fs", not(feature = "fs-ng")), feature = "fs-ng"))] +mod irq_support { + #[cfg(feature = "irq")] + pub(crate) struct BlockIrqState { + handler: ax_driver::block::BlockIrqHandler, + } -#[cfg(all( - feature = "irq", - any(all(feature = "fs", not(feature = "fs-ng")), feature = "fs-ng") -))] -struct BlockIrqState { - handler: ax_driver::block::BlockIrqHandler, - irq_handle: spin::Once, -} + #[cfg(feature = "irq")] + pub(crate) type BlockIrqRegistration = crate::irq::HandlerRegistration; -#[cfg(all( - feature = "irq", - any(all(feature = "fs", not(feature = "fs-ng")), feature = "fs-ng") -))] -pub(crate) struct BlockIrqRegistration { - _state: alloc::boxed::Box, -} + #[cfg(not(feature = "irq"))] + pub(crate) type BlockIrqRegistration = (); -#[cfg(all( - not(feature = "irq"), - any(all(feature = "fs", not(feature = "fs-ng")), feature = "fs-ng") -))] -pub(crate) type BlockIrqRegistration = (); - -#[cfg(all( - feature = "irq", - any(all(feature = "fs", not(feature = "fs-ng")), feature = "fs-ng") -))] -unsafe fn handle_block_irq( - _ctx: axklib::irq::IrqContext, - data: NonNull<()>, -) -> axklib::irq::IrqReturn { - let state = unsafe { data.cast::().as_ref() }; - let _event = state.handler.handle(); - axklib::irq::IrqReturn::Handled -} + #[cfg(feature = "irq")] + unsafe fn handle_block_irq( + _ctx: ax_hal::irq::IrqContext, + data: core::ptr::NonNull<()>, + ) -> ax_hal::irq::IrqReturn { + let state = unsafe { data.cast::().as_ref() }; + let _event = state.handler.handle(); + ax_hal::irq::IrqReturn::Handled + } + + pub(crate) fn register_irq_handler( + block: &mut ax_driver::block::Block, + ) -> Option { + #[cfg(feature = "irq")] + { + let name = alloc::string::String::from(block.name()); + let (irq, handler) = block.take_irq_handler()?; + register_shared_for_block( + block, + name, + irq, + BlockIrqState { handler }, + handle_block_irq, + ax_driver::block::Block::enable_irq, + ax_driver::block::Block::disable_irq, + ) + } -#[cfg(all( - feature = "irq", - any(all(feature = "fs", not(feature = "fs-ng")), feature = "fs-ng") -))] -impl Drop for BlockIrqState { - fn drop(&mut self) { - if let Some(handle) = self.irq_handle.get().copied() - && let Err(err) = axklib::irq::free(handle) + #[cfg(not(feature = "irq"))] { - warn!("failed to free block irq handler: {err:?}"); + let _ = block; + None + } + } + + #[cfg(feature = "irq")] + fn register_shared_for_block( + block: &mut ax_driver::block::Block, + name: alloc::string::String, + irq: usize, + state: BlockIrqState, + handler: ax_hal::irq::RawIrqHandler, + enable_irq: impl FnOnce(&mut ax_driver::block::Block), + disable_irq: impl FnOnce(&mut ax_driver::block::Block), + ) -> Option { + match crate::irq::HandlerRegistration::register_shared(name, irq, state, handler) { + Ok(registration) => { + enable_irq(block); + Some(registration) + } + Err(_) => { + disable_irq(block); + None + } } } } +#[cfg(any(all(feature = "fs", not(feature = "fs-ng")), feature = "fs-ng"))] +pub(crate) use irq_support::{BlockIrqRegistration, register_irq_handler}; + +#[cfg(feature = "fs-ng")] +mod root; +#[cfg(any(feature = "fs-ng", test))] +pub(crate) mod volume; + #[cfg(feature = "fs-ng")] struct FsNgBlockDevice { _irq: Option, @@ -102,40 +121,6 @@ impl ax_fs_ng::FsBlockDevice for FsNgBlockDevice { } } -#[cfg(any(all(feature = "fs", not(feature = "fs-ng")), feature = "fs-ng"))] -pub(crate) fn register_irq_handler( - block: &mut ax_driver::block::Block, -) -> Option { - #[cfg(feature = "irq")] - { - let name = alloc::string::String::from(block.name()); - let (irq_num, handler) = block.take_irq_handler()?; - let mut state = alloc::boxed::Box::new(BlockIrqState { - handler, - irq_handle: spin::Once::new(), - }); - let data = NonNull::from(state.as_mut()).cast(); - match axklib::irq::request_shared(irq_num, handle_block_irq, data) { - Ok(handle) => { - state.irq_handle.call_once(|| handle); - block.enable_irq(); - Some(BlockIrqRegistration { _state: state }) - } - Err(err) => { - warn!("failed to register block irq handler for {name} irq {irq_num}: {err:?}"); - block.disable_irq(); - None - } - } - } - - #[cfg(not(feature = "irq"))] - { - let _ = block; - None - } -} - #[cfg(all(feature = "fs-ng", feature = "plat-dyn"))] pub(crate) fn init_dyn_fs_ng(bootargs: Option<&str>) { init_fs_ng_from_blocks(take_block_devices(), bootargs); diff --git a/os/arceos/modules/axruntime/src/devices.rs b/os/arceos/modules/axruntime/src/devices.rs index 2f38022a69..905ff50f05 100644 --- a/os/arceos/modules/axruntime/src/devices.rs +++ b/os/arceos/modules/axruntime/src/devices.rs @@ -91,6 +91,8 @@ pub(crate) fn init_static_input() { #[cfg(all(feature = "net", feature = "plat-dyn"))] pub(crate) fn init_dyn_net() { + #[cfg(feature = "irq")] + ax_net::set_ethernet_irq_registrar(&crate::irq::NET_IRQ_REGISTRAR); register_unix_namespace(); let config = parse_network_config(); ax_net::init_network(take_dyn_net_drivers(), config); @@ -98,6 +100,8 @@ pub(crate) fn init_dyn_net() { #[cfg(all(feature = "net", not(feature = "plat-dyn")))] pub(crate) fn init_static_net() { + #[cfg(feature = "irq")] + ax_net::set_ethernet_irq_registrar(&crate::irq::NET_IRQ_REGISTRAR); register_unix_namespace(); let config = parse_network_config(); ax_net::init_network(take_static_net_drivers(), config); @@ -181,9 +185,9 @@ pub(crate) fn take_static_net_drivers() -> alloc::vec::Vec> { let mut devices = alloc::vec::Vec::new(); for dev in rdrive::get_list::() { - let (net, name, irq_num) = ax_driver::net::take_rd_net_device(dev) + let (net, name, irq) = ax_driver::net::take_rd_net_device(dev) .unwrap_or_else(|err| panic!("failed to open static net device: {err:?}")); - let driver = ax_net::RdNetDriver::new(name, net, irq_num) + let driver = ax_net::RdNetDriver::new(name, net, irq) .unwrap_or_else(|err| panic!("failed to adapt static net device: {err:?}")); devices .push(alloc::boxed::Box::new(driver) as alloc::boxed::Box); @@ -216,9 +220,9 @@ fn take_dyn_net_drivers() -> alloc::vec::Vec() { - let (net, name, irq_num) = ax_driver::net::take_rd_net_device(dev) + let (net, name, irq) = ax_driver::net::take_rd_net_device(dev) .unwrap_or_else(|err| panic!("failed to open net device: {err:?}")); - let driver = ax_net::RdNetDriver::new(name, net, irq_num) + let driver = ax_net::RdNetDriver::new(name, net, irq) .unwrap_or_else(|err| panic!("failed to adapt net device: {err:?}")); devices .push(alloc::boxed::Box::new(driver) as alloc::boxed::Box); diff --git a/os/arceos/modules/axruntime/src/irq.rs b/os/arceos/modules/axruntime/src/irq.rs new file mode 100644 index 0000000000..af9d647e83 --- /dev/null +++ b/os/arceos/modules/axruntime/src/irq.rs @@ -0,0 +1,128 @@ +use alloc::{boxed::Box, string::String}; +use core::ptr::NonNull; + +use ax_hal::irq::{IrqError, IrqHandle, RawIrqHandler}; + +pub struct Registration { + name: String, + handle: Option, +} + +impl Registration { + pub fn register_shared( + name: impl Into, + irq: usize, + handler: RawIrqHandler, + data: NonNull<()>, + ) -> Result { + let name = name.into(); + match ax_hal::irq::request_shared_irq(irq, handler, data) { + Ok(handle) => { + info!("registered {name} irq {}", handle.irq().0); + Ok(Self { + name, + handle: Some(handle), + }) + } + Err(err) => { + warn!("failed to register {name} irq handler for irq {irq}: {err:?}"); + Err(err) + } + } + } +} + +impl Drop for Registration { + fn drop(&mut self) { + let Some(handle) = self.handle.take() else { + return; + }; + if let Err(err) = ax_hal::irq::free_irq(handle) { + warn!("failed to free {} irq handler: {err:?}", self.name); + } + } +} + +pub struct HandlerRegistration { + _registration: Registration, + _state: Box, +} + +impl HandlerRegistration { + pub fn register_shared( + name: impl Into, + irq: usize, + state: T, + handler: RawIrqHandler, + ) -> Result { + let mut state = Box::new(state); + let data = NonNull::from(state.as_mut()).cast(); + let registration = Registration::register_shared(name, irq, handler, data)?; + Ok(Self { + _registration: registration, + _state: state, + }) + } +} + +#[cfg(feature = "net")] +pub(crate) struct RuntimeNetIrqRegistrar; + +#[cfg(feature = "net")] +pub(crate) static NET_IRQ_REGISTRAR: RuntimeNetIrqRegistrar = RuntimeNetIrqRegistrar; + +#[cfg(feature = "net")] +struct RuntimeNetIrqState { + action: ax_net::EthernetIrqAction, +} + +#[cfg(feature = "net")] +impl ax_net::EthernetIrqRegistration for HandlerRegistration {} + +#[cfg(feature = "net")] +unsafe fn handle_net_irq( + _ctx: ax_hal::irq::IrqContext, + data: NonNull<()>, +) -> ax_hal::irq::IrqReturn { + let state = unsafe { data.cast::().as_ref() }; + match unsafe { state.action.run() } { + ax_net::EthernetIrqOutcome::Handled => ax_hal::irq::IrqReturn::Handled, + ax_net::EthernetIrqOutcome::Wake => ax_hal::irq::IrqReturn::Wake, + } +} + +#[cfg(feature = "net")] +fn map_net_irq_error(err: IrqError) -> ax_net::EthernetIrqRegistrationError { + match err { + IrqError::InvalidIrq | IrqError::InvalidCpu => { + ax_net::EthernetIrqRegistrationError::InvalidIrq + } + IrqError::Busy | IrqError::InIrqContext => ax_net::EthernetIrqRegistrationError::Busy, + IrqError::Unsupported | IrqError::CpuOffline => { + ax_net::EthernetIrqRegistrationError::Unsupported + } + IrqError::NoMemory | IrqError::NotFound | IrqError::Controller => { + ax_net::EthernetIrqRegistrationError::Other + } + } +} + +#[cfg(feature = "net")] +impl ax_net::EthernetIrqRegistrar for RuntimeNetIrqRegistrar { + fn register_shared( + &self, + name: &str, + irq: usize, + action: ax_net::EthernetIrqAction, + ) -> Result, ax_net::EthernetIrqRegistrationError> + { + HandlerRegistration::register_shared( + name, + irq, + RuntimeNetIrqState { action }, + handle_net_irq, + ) + .map(|registration| Box::new(registration) as Box) + .map_err(map_net_irq_error) + } +} diff --git a/os/arceos/modules/axruntime/src/lib.rs b/os/arceos/modules/axruntime/src/lib.rs index 5aa76f44c7..1526aaaae9 100644 --- a/os/arceos/modules/axruntime/src/lib.rs +++ b/os/arceos/modules/axruntime/src/lib.rs @@ -50,6 +50,8 @@ mod klib; #[cfg(any(feature = "fs", feature = "fs-ng", test))] mod block; mod devices; +#[cfg(feature = "irq")] +pub mod irq; mod registers; #[cfg(all(feature = "net", any(feature = "fs", feature = "fs-ng")))] diff --git a/platforms/ax-plat-loongarch64-qemu-virt/Cargo.toml b/platforms/ax-plat-loongarch64-qemu-virt/Cargo.toml index 034e030dd4..20274353d9 100644 --- a/platforms/ax-plat-loongarch64-qemu-virt/Cargo.toml +++ b/platforms/ax-plat-loongarch64-qemu-virt/Cargo.toml @@ -27,7 +27,7 @@ uart_16550 = "0.5" ax-config-macros = { workspace = true } ax-cpu = { workspace = true } -ax-driver = { workspace = true, features = ["plat-static"] } +ax-driver = { workspace = true, features = ["plat-static", "pci"] } ax-plat = { workspace = true } axklib = { workspace = true, features = ["tlsf"] } mmio-api.workspace = true diff --git a/platforms/ax-plat/Cargo.toml b/platforms/ax-plat/Cargo.toml index a4dc0b8e98..28572e0e87 100644 --- a/platforms/ax-plat/Cargo.toml +++ b/platforms/ax-plat/Cargo.toml @@ -11,13 +11,14 @@ license = "Apache-2.0" [features] smp = ["ax-kspin/smp"] -irq = ["dep:irq-framework"] +irq = ["dep:irq-framework", "dep:rdif-intc"] [dependencies] ax-memory-addr = { workspace = true } bitflags = "2.6" ax-crate-interface = { workspace = true } irq-framework = { workspace = true, optional = true } +rdif-intc = { workspace = true, optional = true } const-str = "1.0" ax-plat-macros = { workspace = true } ax-kernel-guard = { workspace = true } diff --git a/platforms/ax-plat/src/irq.rs b/platforms/ax-plat/src/irq.rs index 031c913cca..4015afa86c 100644 --- a/platforms/ax-plat/src/irq.rs +++ b/platforms/ax-plat/src/irq.rs @@ -104,7 +104,12 @@ fn registry() -> &'static Registry { /// Requests an IRQ action through the dynamic IRQ framework. pub fn request_irq(irq: usize, request: IrqRequest) -> Result { - registry().request(IrqNumber(irq), request) + let handle = registry().request(IrqNumber(irq), request)?; + if let Err(err) = registry().enable(handle) { + let _ = registry().free(handle); + return Err(err); + } + Ok(handle) } /// Requests a shared IRQ action. diff --git a/platforms/ax-plat/src/lib.rs b/platforms/ax-plat/src/lib.rs index f67d7322f7..5017cd1cea 100644 --- a/platforms/ax-plat/src/lib.rs +++ b/platforms/ax-plat/src/lib.rs @@ -2,6 +2,8 @@ #![cfg_attr(docsrs, feature(doc_cfg))] #![doc = include_str!("../README.md")] +extern crate alloc; + #[macro_use] extern crate ax_plat_macros; diff --git a/platforms/somehal/src/arch/aarch64/gic/v2.rs b/platforms/somehal/src/arch/aarch64/gic/v2.rs index ef47b00962..edf4155512 100644 --- a/platforms/somehal/src/arch/aarch64/gic/v2.rs +++ b/platforms/somehal/src/arch/aarch64/gic/v2.rs @@ -2,7 +2,7 @@ use alloc::format; use arm_gic_driver::v2::*; use kernutil::StaticCell; -use rdrive::{PlatformDevice, module_driver, probe::OnProbeError, register::FdtInfo}; +use rdrive::{module_driver, probe::OnProbeError, register::ProbeFdt}; use crate::common::ioremap; @@ -28,7 +28,8 @@ module_driver!( ], ); -fn probe_gic(info: FdtInfo<'_>, dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe_gic(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (info, dev) = probe.into_parts(); let mut reg = info.node.regs().into_iter(); let gicd_reg = reg.next().ok_or(OnProbeError::other(format!( "[{}] has no reg", diff --git a/platforms/somehal/src/arch/aarch64/gic/v3.rs b/platforms/somehal/src/arch/aarch64/gic/v3.rs index bbee8c5339..106b937ce0 100644 --- a/platforms/somehal/src/arch/aarch64/gic/v3.rs +++ b/platforms/somehal/src/arch/aarch64/gic/v3.rs @@ -4,7 +4,7 @@ use core::cell::UnsafeCell; use aarch64_cpu::registers::ID_AA64PFR0_EL1; use arm_gic_driver::v3::*; use kernutil::StaticCell; -use rdrive::{PlatformDevice, module_driver, probe::OnProbeError, register::FdtInfo}; +use rdrive::{module_driver, probe::OnProbeError, register::ProbeFdt}; use crate::common::ioremap; @@ -62,7 +62,8 @@ module_driver!( ], ); -fn probe_gic(info: FdtInfo<'_>, dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe_gic(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (info, dev) = probe.into_parts(); let mut reg = info.node.regs().into_iter(); let gicd_reg = reg.next().ok_or(OnProbeError::other(format!( "[{}] has no reg", diff --git a/platforms/somehal/src/arch/aarch64/systick.rs b/platforms/somehal/src/arch/aarch64/systick.rs index e1374a77b6..8217f5d6ec 100644 --- a/platforms/somehal/src/arch/aarch64/systick.rs +++ b/platforms/somehal/src/arch/aarch64/systick.rs @@ -1,7 +1,7 @@ use alloc::vec::Vec; use rdif_intc::Intc; -use rdrive::{PlatformDevice, module_driver, probe::OnProbeError, register::FdtInfo}; +use rdrive::{module_driver, probe::OnProbeError, register::ProbeFdt}; static mut TIMER_IRQ: Option = None; static mut TIMER_IRQ_PARENT: Option = None; @@ -29,7 +29,8 @@ pub(crate) fn setup_systick_irq() { crate::irq::irq_set_enable(id, true); } -fn probe(fdt: FdtInfo<'_>, dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (fdt, dev) = probe.into_parts(); let intc_id = dev.descriptor.irq_parent.unwrap(); let mut intc = rdrive::get::(intc_id).unwrap().lock().unwrap(); diff --git a/platforms/somehal/src/arch/loongarch64/eiointc.rs b/platforms/somehal/src/arch/loongarch64/eiointc.rs index 1baf768fa7..db63db2b51 100644 --- a/platforms/somehal/src/arch/loongarch64/eiointc.rs +++ b/platforms/somehal/src/arch/loongarch64/eiointc.rs @@ -1,7 +1,9 @@ use loongArch64::iocsr::{iocsr_read_d, iocsr_write_d, iocsr_write_w}; use rdif_intc::Interface; use rdrive::{ - DriverGeneric, PlatformDevice, module_driver, probe::OnProbeError, register::FdtInfo, + DriverGeneric, PlatformDevice, module_driver, + probe::OnProbeError, + register::{ProbeAcpi, ProbeFdt}, }; use super::irq_common::{EIOINTC_VECTOR_COUNT, eiointc_reg_bit, fdt_first_cell_vector}; @@ -24,14 +26,20 @@ module_driver!( name: "Loongson EIOINTC", level: ProbeLevel::PreKernel, priority: ProbePriority::INTC, - probe_kinds: &[ProbeKind::Fdt { - compatibles: &[ - "loongson,ls2k2000-eiointc", - "loongson,ls3a5000-eiointc", - "loongson,eiointc", - ], - on_probe: probe_eiointc - }], + probe_kinds: &[ + ProbeKind::Fdt { + compatibles: &[ + "loongson,ls2k2000-eiointc", + "loongson,ls3a5000-eiointc", + "loongson,eiointc", + ], + on_probe: probe_eiointc_fdt + }, + ProbeKind::Acpi { + ids: &[], + on_probe: probe_eiointc_acpi + }, + ], ); pub fn set_irq_enable(irq: usize, enable: bool) { @@ -52,7 +60,18 @@ pub fn complete_irq(irq: usize) { with_eiointc("completing EIOINTC IRQ", |intc| intc.complete_irq(irq)); } -fn probe_eiointc(_info: FdtInfo<'_>, dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe_eiointc_fdt(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + register_eiointc(probe.into_platform_device()) +} + +fn probe_eiointc_acpi(probe: ProbeAcpi<'_>) -> Result<(), OnProbeError> { + if probe.info().root.routing().pch_pics().is_empty() { + return Err(OnProbeError::NotMatch); + } + register_eiointc(probe.into_platform_device()) +} + +fn register_eiointc(dev: PlatformDevice) -> Result<(), OnProbeError> { let intc = EioIntc::new(EIOINTC_VECTOR_COUNT); intc.init(); someboot::irq::irq_set_enable(someboot::irq::IrqId::new(EIOINTC_IRQ), true); diff --git a/platforms/somehal/src/arch/loongarch64/pch_pic.rs b/platforms/somehal/src/arch/loongarch64/pch_pic.rs index e8a639ecf2..e0901d9d5f 100644 --- a/platforms/somehal/src/arch/loongarch64/pch_pic.rs +++ b/platforms/somehal/src/arch/loongarch64/pch_pic.rs @@ -1,6 +1,11 @@ -use rdif_intc::Interface; +use rdif_intc::{AcpiIrqPolarity, AcpiIrqTrigger, Interface}; use rdrive::{ - DriverGeneric, PlatformDevice, module_driver, probe::OnProbeError, register::FdtInfo, + DriverGeneric, PlatformDevice, module_driver, + probe::{ + OnProbeError, + acpi::{AcpiGsiController, AcpiPchPic}, + }, + register::{ProbeAcpi, ProbeFdt}, }; use super::irq_common::{PCH_PIC_VECTOR_COUNT, fdt_first_cell_vector, pch_pic_reg_bit}; @@ -8,6 +13,7 @@ use crate::{common::ioremap, setup::MmioRaw}; const DEFAULT_PCH_PIC_SIZE: usize = 0x400; +const PCH_PIC_ID: usize = 0x00; const PCH_PIC_MASK: usize = 0x20; const PCH_PIC_EDGE: usize = 0x60; const PCH_PIC_POL: usize = 0x3e0; @@ -17,14 +23,20 @@ module_driver!( name: "Loongson PCH-PIC", level: ProbeLevel::PreKernel, priority: ProbePriority::INTC, - probe_kinds: &[ProbeKind::Fdt { - compatibles: &[ - "loongson,ls7a-pch-pic", - "loongson,pch-pic-1.0", - "loongson,pch-pic", - ], - on_probe: probe_pch_pic - }], + probe_kinds: &[ + ProbeKind::Fdt { + compatibles: &[ + "loongson,ls7a-pch-pic", + "loongson,pch-pic-1.0", + "loongson,pch-pic", + ], + on_probe: probe_pch_pic_fdt + }, + ProbeKind::Acpi { + ids: &[], + on_probe: probe_pch_pic_acpi + }, + ], ); pub fn set_irq_enable(irq: usize, enable: bool) { @@ -37,7 +49,8 @@ pub fn set_irq_enable(irq: usize, enable: bool) { }); } -fn probe_pch_pic(info: FdtInfo<'_>, dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe_pch_pic_fdt(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (info, dev) = probe.into_parts(); let reg = info .node .regs() @@ -55,19 +68,70 @@ fn probe_pch_pic(info: FdtInfo<'_>, dev: PlatformDevice) -> Result<(), OnProbeEr .as_node() .get_property("loongson,pic-num-vecs") .and_then(|prop| prop.get_u32()) - .unwrap_or(PCH_PIC_VECTOR_COUNT as u32) as usize; + .map(|count| count as usize); let mmio = ioremap( reg.address, reg.size.unwrap_or(DEFAULT_PCH_PIC_SIZE as u64) as usize, ) .map_err(|err| OnProbeError::other(format!("failed to map PCH-PIC: {err:?}")))?; - let pic = PchPic::new(mmio, base_vector, vector_count); + register_pch_pic(dev, mmio, base_vector, vector_count) +} + +fn probe_pch_pic_acpi(probe: ProbeAcpi<'_>) -> Result<(), OnProbeError> { + let (info, dev) = probe.into_parts(); + let mut registered = false; + + for pch_pic in info.root.routing().pch_pics() { + register_acpi_pch_pic( + PlatformDevice { + descriptor: dev.descriptor.clone(), + }, + *pch_pic, + )?; + registered = true; + } + + if registered { + Ok(()) + } else { + Err(OnProbeError::NotMatch) + } +} + +fn register_acpi_pch_pic(dev: PlatformDevice, info: AcpiPchPic) -> Result<(), OnProbeError> { + let size = if info.mmio_size == 0 { + DEFAULT_PCH_PIC_SIZE + } else { + usize::from(info.mmio_size) + }; + let mmio = ioremap(info.address, size) + .map_err(|err| OnProbeError::other(format!("failed to map ACPI PCH-PIC: {err:?}")))?; + register_pch_pic(dev, mmio, 0, None) +} + +fn register_pch_pic( + dev: PlatformDevice, + mmio: MmioRaw, + base_vector: usize, + vector_count: Option, +) -> Result<(), OnProbeError> { + let detected_vector_count = detect_vector_count(&mmio).unwrap_or(PCH_PIC_VECTOR_COUNT); + let pic = PchPic::new( + mmio, + base_vector, + vector_count.unwrap_or(detected_vector_count), + ); pic.init(); dev.register(rdif_intc::Intc::new(pic)); Ok(()) } +fn detect_vector_count(mmio: &MmioRaw) -> Option { + let count = (((mmio.read::(PCH_PIC_ID) >> 48) & 0xff) as usize).saturating_add(1); + (count <= PCH_PIC_VECTOR_COUNT).then_some(count) +} + fn with_pch_pic(op: &str, f: impl FnOnce(&mut PchPic) -> R) -> Option { if !rdrive::is_initialized() { return None; @@ -150,6 +214,38 @@ impl PchPic { } } + fn configure_input(&mut self, input: usize, route: &rdif_intc::AcpiGsiRoute) { + let (offset, bit) = pch_pic_reg_bit(input); + let edge_addr = PCH_PIC_EDGE + offset; + let pol_addr = PCH_PIC_POL + offset; + + let edge = self.read_w(edge_addr); + let edge = match route.trigger { + AcpiIrqTrigger::Edge => edge | bit, + AcpiIrqTrigger::Level => edge & !bit, + }; + self.write_w(edge_addr, edge); + + let pol = self.read_w(pol_addr); + let pol = match route.polarity { + AcpiIrqPolarity::ActiveHigh => pol & !bit, + AcpiIrqPolarity::ActiveLow => pol | bit, + }; + self.write_w(pol_addr, pol); + } + + fn vector_for_acpi_route(&self, route: &rdif_intc::AcpiGsiRoute) -> Option { + if route.controller != AcpiGsiController::PchPic { + return None; + } + let input = usize::from(route.controller_input); + if input < self.vector_count { + Some(self.base_vector + input) + } else { + None + } + } + fn read_w(&self, offset: usize) -> u32 { self.mmio.read(offset) } @@ -170,6 +266,23 @@ impl DriverGeneric for PchPic { } impl Interface for PchPic { + fn supports_acpi_gsi(&self, route: &rdif_intc::AcpiGsiRoute) -> bool { + route.controller_address == self.mmio.phys_addr().as_usize() as u64 + && self.vector_for_acpi_route(route).is_some() + } + + fn setup_irq_by_acpi(&mut self, route: &rdif_intc::AcpiGsiRoute) -> rdrive::IrqId { + let Some(vector) = self.vector_for_acpi_route(route) else { + warn!( + "unsupported ACPI PCH-PIC route: controller={:?} address={:#x} input={}", + route.controller, route.controller_address, route.controller_input + ); + return self.base_vector.into(); + }; + self.configure_input(usize::from(route.controller_input), route); + vector.into() + } + fn setup_irq_by_fdt(&mut self, irq_prop: &[u32]) -> rdrive::IrqId { let Some(input) = fdt_first_cell_vector(irq_prop) else { warn!("empty PCH-PIC interrupt specifier"); diff --git a/platforms/somehal/src/arch/riscv64/plic.rs b/platforms/somehal/src/arch/riscv64/plic.rs index d06ad6dfc9..d2ab56da60 100644 --- a/platforms/somehal/src/arch/riscv64/plic.rs +++ b/platforms/somehal/src/arch/riscv64/plic.rs @@ -5,9 +5,9 @@ use ax_riscv_plic::{PLICRegs, Plic, PlicIrqHandler}; use kernutil::StaticCell; use rdif_intc::Interface; use rdrive::{ - Device, DriverGeneric, Phandle, PlatformDevice, module_driver, + Device, DriverGeneric, Phandle, module_driver, probe::{OnProbeError, fdt::NodeType}, - register::FdtInfo, + register::{FdtInfo, ProbeFdt}, }; use riscv::register::{sie, sip}; use sbi_rt::HartMask; @@ -130,7 +130,8 @@ pub fn send_ipi_to_cpu(cpu_id: usize) { } } -fn probe_plic(info: FdtInfo<'_>, dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe_plic(probe: ProbeFdt<'_>) -> Result<(), OnProbeError> { + let (info, dev) = probe.into_parts(); let reg = info .node .regs() diff --git a/platforms/somehal/src/arch/x86_64/mod.rs b/platforms/somehal/src/arch/x86_64/mod.rs index bad1f0a312..542bcb36cf 100644 --- a/platforms/somehal/src/arch/x86_64/mod.rs +++ b/platforms/somehal/src/arch/x86_64/mod.rs @@ -2,10 +2,10 @@ use alloc::vec::Vec; use rdif_intc::{AcpiGsiRoute, AcpiIrqPolarity, AcpiIrqTrigger}; use rdrive::{ - DriverGeneric, PlatformDevice, module_driver, + DriverGeneric, module_driver, probe::{ OnProbeError, - acpi::{AcpiId, AcpiInfo, AcpiIoApic}, + acpi::{AcpiId, AcpiIoApic, ProbeAcpi}, }, }; use x2apic::ioapic::{IoApic, IrqFlags, IrqMode}; @@ -50,30 +50,43 @@ impl X86IoApicIntc { } fn remember_route(&mut self, route: AcpiGsiRoute) { - if let Some(existing) = self.routes.iter_mut().find(|r| r.vector == route.vector) { + if let Some(existing) = self.routes.iter_mut().find(|r| { + r.controller_id == route.controller_id + && r.controller_address == route.controller_address + && r.gsi == route.gsi + }) { *existing = route; } else { self.routes.push(route); } } - fn route_for_vector(&self, vector: usize) -> Option { - self.routes + fn routes_for_vector(&self, vector: usize) -> Vec { + let routes: Vec<_> = self + .routes .iter() .copied() - .find(|r| r.vector == vector) - .or_else(|| { - rdrive::probe::acpi::with_acpi(|system| system.routing().resolve_vector(vector)) - .flatten() - }) + .filter(|r| r.vector == vector) + .collect(); + if !routes.is_empty() { + return routes; + } + + rdrive::probe::acpi::with_acpi(|system| system.routing().resolve_vector(vector)) + .flatten() + .into_iter() + .collect() } fn set_vector_enable(&mut self, vector: usize, enable: bool) -> bool { - let Some(route) = self.route_for_vector(vector) else { + let routes = self.routes_for_vector(vector); + if routes.is_empty() { return false; - }; + } - self.set_route_enable(&route, enable); + for route in routes { + self.set_route_enable(&route, enable); + } true } @@ -129,8 +142,8 @@ impl X86IoApic { } fn contains_route(&self, route: &AcpiGsiRoute) -> bool { - self.info.id == route.controller_id - && self.info.address == route.controller_address + u16::from(self.info.id) == route.controller_id + && u64::from(self.info.address) == route.controller_address && self.contains(route.gsi) } @@ -144,14 +157,12 @@ impl X86IoApic { let mut entry = self.ioapic.table_entry(input); entry.set_vector(route.vector as u8); entry.set_mode(IrqMode::Fixed); - entry.set_flags(intx_flags(route.trigger, route.polarity)); + entry.set_flags(intx_flags(route.trigger, route.polarity) | IrqFlags::MASKED); entry.set_dest(0); self.ioapic.set_table_entry(input, entry); if enable { self.ioapic.enable_irq(input); - } else { - self.ioapic.disable_irq(input); } } } @@ -164,6 +175,14 @@ impl DriverGeneric for X86IoApicIntc { } impl rdif_intc::Interface for X86IoApicIntc { + fn supports_acpi_gsi(&self, route: &AcpiGsiRoute) -> bool { + route.controller == rdif_intc::AcpiGsiController::IoApic + && self + .ioapics + .iter() + .any(|ioapic| ioapic.contains_route(route)) + } + fn setup_irq_by_acpi(&mut self, route: &AcpiGsiRoute) -> rdrive::IrqId { self.remember_route(*route); self.set_route_enable(route, false); @@ -171,7 +190,8 @@ impl rdif_intc::Interface for X86IoApicIntc { } } -fn probe_ioapic(info: AcpiInfo<'_>, dev: PlatformDevice) -> Result<(), OnProbeError> { +fn probe_ioapic(probe: ProbeAcpi<'_>) -> Result<(), OnProbeError> { + let (info, dev) = probe.into_parts(); let ioapics = info.root.routing().io_apics(); if ioapics.is_empty() { return Err(OnProbeError::NotMatch);