xref: /DragonOS/kernel/src/exception/irqdomain.rs (revision f3b05a97ec061e766247b18dc12e2a413b977b14)
1 use core::fmt::Debug;
2 
3 use alloc::{
4     string::{String, ToString},
5     sync::{Arc, Weak},
6     vec::Vec,
7 };
8 use hashbrown::HashMap;
9 use system_error::SystemError;
10 
11 use crate::{
12     driver::{base::device::Device, open_firmware::device_node::DeviceNode},
13     exception::{irqdata::IrqLineStatus, irqdesc::irq_desc_manager, manage::irq_manager},
14     libs::{rwlock::RwLock, spinlock::SpinLock},
15 };
16 
17 use super::{
18     irqchip::{IrqChipGeneric, IrqGcFlags},
19     irqdata::IrqData,
20     HardwareIrqNumber, IrqNumber,
21 };
22 
23 static mut IRQ_DOMAIN_MANAGER: Option<Arc<IrqDomainManager>> = None;
24 
25 /// 获取中断域管理器的引用
26 #[inline(always)]
27 pub fn irq_domain_manager() -> &'static Arc<IrqDomainManager> {
28     unsafe { IRQ_DOMAIN_MANAGER.as_ref().unwrap() }
29 }
30 
31 pub(super) fn irq_domain_manager_init() {
32     unsafe {
33         IRQ_DOMAIN_MANAGER = Some(Arc::new(IrqDomainManager::new()));
34     }
35 }
36 /// 中断域管理器
37 pub struct IrqDomainManager {
38     domains: SpinLock<Vec<Arc<IrqDomain>>>,
39     inner: RwLock<InnerIrqDomainManager>,
40 }
41 
42 impl IrqDomainManager {
43     pub fn new() -> IrqDomainManager {
44         IrqDomainManager {
45             domains: SpinLock::new(Vec::new()),
46             inner: RwLock::new(InnerIrqDomainManager {
47                 default_domain: None,
48             }),
49         }
50     }
51 
52     /// 创建一个新的irqdomain, 并将其添加到irqdomain管理器中
53     ///
54     /// ## 参数
55     ///
56     /// - `name` - 中断域的名字
57     /// - `ops` - 中断域的操作
58     /// - `first_irq` - 起始软件中断号
59     /// - `first_hwirq` - 起始硬件中断号
60     /// - `irq_size` - 中断号的数量
61     ///
62     /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/kernel/irq/irqdomain.c?fi=__irq_domain_add#139
63     pub fn create_and_add(
64         &self,
65         name: String,
66         ops: &'static dyn IrqDomainOps,
67         first_irq: IrqNumber,
68         first_hwirq: HardwareIrqNumber,
69         irq_size: u32,
70     ) -> Option<Arc<IrqDomain>> {
71         let domain = IrqDomain::new(
72             None,
73             Some(name),
74             ops,
75             IrqDomainFlags::NAME_ALLOCATED,
76             IrqDomainBusToken::Any,
77             first_irq + irq_size,
78             first_hwirq + irq_size,
79         )?;
80 
81         self.add_domain(domain.clone());
82 
83         self.domain_associate_many(&domain, first_irq, first_hwirq, irq_size);
84 
85         return Some(domain);
86     }
87 
88     fn add_domain(&self, domain: Arc<IrqDomain>) {
89         self.domains.lock_irqsave().push(domain);
90     }
91 
92     #[allow(dead_code)]
93     pub fn remove_domain(&self, domain: &Arc<IrqDomain>) {
94         let mut domains = self.domains.lock_irqsave();
95         let index = domains
96             .iter()
97             .position(|x| Arc::ptr_eq(x, domain))
98             .expect("domain not found");
99         domains.remove(index);
100     }
101 
102     /// 获取默认的中断域
103     #[allow(dead_code)]
104     pub fn default_domain(&self) -> Option<Arc<IrqDomain>> {
105         self.inner.read().default_domain.clone()
106     }
107 
108     /// 设置默认的中断域
109     ///
110     /// 在创建IRQ映射的时候,如果没有指定中断域,就会使用默认的中断域
111     pub fn set_default_domain(&self, domain: Arc<IrqDomain>) {
112         self.inner.write_irqsave().default_domain = Some(domain);
113     }
114 
115     /// 将指定范围的硬件中断号与软件中断号一一对应的关联起来
116     ///
117     /// ## 参数
118     ///
119     /// - `domain` - 中断域
120     /// - `first_irq` - 起始软件中断号
121     /// - `first_hwirq` - 起始硬件中断号
122     /// - `count` - 数量
123     pub fn domain_associate_many(
124         &self,
125         domain: &Arc<IrqDomain>,
126         first_irq: IrqNumber,
127         first_hwirq: HardwareIrqNumber,
128         count: u32,
129     ) {
130         for i in 0..count {
131             if let Err(e) = self.domain_associate(domain, first_irq + i, first_hwirq + i) {
132                 kwarn!("domain associate failed: {:?}, domain '{:?}' didn't like hwirq {} to virq {} mapping.", e, domain.name(), (first_hwirq + i).data(), (first_irq + i).data());
133             }
134         }
135     }
136 
137     /// 将一个硬件中断号与一个软件中断号关联起来
138     ///
139     /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/kernel/irq/irqdomain.c#562
140     pub fn domain_associate(
141         &self,
142         domain: &Arc<IrqDomain>,
143         irq: IrqNumber,
144         hwirq: HardwareIrqNumber,
145     ) -> Result<(), SystemError> {
146         if hwirq >= domain.revmap.read_irqsave().hwirq_max {
147             kwarn!(
148                 "hwirq {} is out of range for domain {:?}",
149                 hwirq.data(),
150                 domain.name()
151             );
152             return Err(SystemError::EINVAL);
153         }
154         let irq_data = irq_desc_manager()
155             .lookup(irq)
156             .ok_or_else(|| {
157                 kwarn!("irq_desc not found for irq {}", irq.data());
158                 SystemError::EINVAL
159             })?
160             .irq_data();
161         if irq_data.domain().is_some() {
162             kwarn!(
163                 "irq {} is already associated with domain {:?}",
164                 irq.data(),
165                 irq_data.domain().unwrap().name()
166             );
167             return Err(SystemError::EINVAL);
168         }
169 
170         let mut irq_data_guard = irq_data.inner();
171         irq_data_guard.set_hwirq(hwirq);
172         irq_data_guard.set_domain(Some(domain.clone()));
173         drop(irq_data_guard);
174         let r = domain.ops.map(&domain, hwirq, irq);
175         if let Err(e) = r {
176             if e != SystemError::ENOSYS {
177                 if e != SystemError::EPERM {
178                     kinfo!("domain associate failed: {:?}, domain '{:?}' didn't like hwirq {} to virq {} mapping.", e, domain.name(), hwirq.data(), irq.data());
179                 }
180                 let mut irq_data_guard = irq_data.inner();
181                 irq_data_guard.set_domain(None);
182                 irq_data_guard.set_hwirq(HardwareIrqNumber::new(0));
183                 return Err(e);
184             }
185         }
186 
187         if domain.name().is_none() {
188             let chip = irq_data.chip_info_read_irqsave().chip();
189             domain.set_name(chip.name().to_string());
190         }
191 
192         self.irq_domain_set_mapping(&domain, hwirq, irq_data);
193 
194         irq_manager().irq_clear_status_flags(irq, IrqLineStatus::IRQ_NOREQUEST)?;
195 
196         return Ok(());
197     }
198 
199     fn irq_domain_set_mapping(
200         &self,
201         domain: &Arc<IrqDomain>,
202         hwirq: HardwareIrqNumber,
203         irq_data: Arc<IrqData>,
204     ) {
205         if domain.no_map() {
206             return;
207         }
208 
209         domain.revmap.write_irqsave().insert(hwirq, irq_data);
210     }
211     /// 递归调用 domain_ops->activate 以激活中断
212     ///
213     /// ## 参数
214     ///
215     /// - irq_data: 与中断关联的最外层 irq_data
216     /// - reserve: 如果为true,则仅预留一个中断向量,而不是分配一个
217     ///
218     /// 这是调用 domain_ops->activate 以编程中断控制器的第二步,以便中断实际上可以被传递。
219     pub fn activate_irq(&self, irq_data: &Arc<IrqData>, reserve: bool) -> Result<(), SystemError> {
220         let mut r = Ok(());
221         if !irq_data.common_data().status().is_activated() {
222             r = self.do_activate_irq(Some(irq_data.clone()), reserve);
223         }
224 
225         if !r.is_ok() {
226             irq_data.common_data().status().set_activated();
227         }
228 
229         return r;
230     }
231 
232     #[inline(never)]
233     fn do_activate_irq(
234         &self,
235         irq_data: Option<Arc<IrqData>>,
236         reserve: bool,
237     ) -> Result<(), SystemError> {
238         let mut r = Ok(());
239 
240         if irq_data.is_some() && irq_data.as_ref().unwrap().domain().is_some() {
241             let domain = irq_data.as_ref().unwrap().domain().unwrap();
242 
243             let irq_data = irq_data.unwrap();
244 
245             let parent_data = irq_data.parent_data().map(|x| x.upgrade()).flatten();
246             if let Some(parent_data) = parent_data.clone() {
247                 r = self.do_activate_irq(Some(parent_data), reserve);
248             }
249 
250             if r.is_err() {
251                 let tmpr = domain.ops.activate(&domain, &irq_data, reserve);
252                 if let Err(e) = tmpr {
253                     if e != SystemError::ENOSYS && parent_data.is_some() {
254                         self.do_deactivate_irq(parent_data);
255                     }
256                 }
257             }
258         }
259 
260         return r;
261     }
262 
263     fn do_deactivate_irq(&self, irq_data: Option<Arc<IrqData>>) {
264         if let Some(irq_data) = irq_data {
265             if let Some(domain) = irq_data.domain() {
266                 domain.ops.deactivate(&domain, &irq_data);
267                 let pp = irq_data.parent_data().map(|x| x.upgrade()).flatten();
268 
269                 if pp.is_some() {
270                     self.do_deactivate_irq(pp);
271                 }
272             }
273         }
274     }
275 }
276 
277 struct InnerIrqDomainManager {
278     default_domain: Option<Arc<IrqDomain>>,
279 }
280 
281 /// 中断域
282 ///
283 /// 用于把硬件中断号翻译为软件中断号的映射的对象
284 ///
285 /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#164
286 #[allow(dead_code)]
287 #[derive(Debug)]
288 pub struct IrqDomain {
289     /// 中断域的名字 (二选一)
290     name: Option<&'static str>,
291     allocated_name: SpinLock<Option<String>>,
292     /// 中断域的操作
293     ops: &'static dyn IrqDomainOps,
294     inner: SpinLock<InnerIrqDomain>,
295     /// 中断号反向映射
296     revmap: RwLock<IrqDomainRevMap>,
297 }
298 
299 #[allow(dead_code)]
300 #[derive(Debug)]
301 struct InnerIrqDomain {
302     /// host per irq_domain flags
303     flags: IrqDomainFlags,
304     /// The number of mapped interrupts
305     mapcount: u32,
306     bus_token: IrqDomainBusToken,
307     /// 指向 generic chip 列表的指针。
308     /// 有一个辅助函数用于为中断控制器驱动程序设置一个或
309     /// 多个 generic chip,该函数使用此指针并依赖于 generic chip 库。
310     generic_chip: Option<Arc<IrqDomainChipGeneric>>,
311     /// Pointer to a device that the domain represent, and that will be
312     /// used for power management purposes.
313     device: Option<Arc<dyn Device>>,
314     /// Pointer to parent irq_domain to support hierarchy irq_domains
315     parent: Option<Weak<IrqDomain>>,
316 }
317 
318 impl IrqDomain {
319     #[allow(dead_code)]
320     pub fn new(
321         name: Option<&'static str>,
322         allocated_name: Option<String>,
323         ops: &'static dyn IrqDomainOps,
324         flags: IrqDomainFlags,
325         bus_token: IrqDomainBusToken,
326         irq_max: IrqNumber,
327         hwirq_max: HardwareIrqNumber,
328     ) -> Option<Arc<Self>> {
329         if name.is_none() && allocated_name.is_none() {
330             return None;
331         }
332 
333         let x = IrqDomain {
334             name,
335             allocated_name: SpinLock::new(allocated_name),
336             ops,
337             inner: SpinLock::new(InnerIrqDomain {
338                 flags,
339                 mapcount: 0,
340                 bus_token,
341                 generic_chip: None,
342                 device: None,
343                 parent: None,
344             }),
345             revmap: RwLock::new(IrqDomainRevMap {
346                 map: HashMap::new(),
347                 hwirq_max,
348                 irq_max,
349             }),
350         };
351 
352         return Some(Arc::new(x));
353     }
354 
355     /// 中断域是否不对中断号进行转换
356     pub fn no_map(&self) -> bool {
357         self.inner
358             .lock_irqsave()
359             .flags
360             .contains(IrqDomainFlags::NO_MAP)
361     }
362 
363     #[allow(dead_code)]
364     fn set_hwirq_max(&self, hwirq_max: HardwareIrqNumber) {
365         self.revmap.write_irqsave().hwirq_max = hwirq_max;
366     }
367 
368     pub fn name(&self) -> Option<String> {
369         if let Some(name) = self.name {
370             return Some(name.to_string());
371         }
372         return self.allocated_name.lock_irqsave().clone();
373     }
374 
375     pub fn set_name(&self, name: String) {
376         *self.allocated_name.lock_irqsave() = Some(name);
377     }
378 
379     /// The number of mapped interrupts
380     pub fn map_count(&self) -> u32 {
381         self.revmap.read().map.len() as u32
382     }
383 }
384 
385 /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#190
386 #[allow(dead_code)]
387 #[derive(Debug)]
388 struct IrqDomainRevMap {
389     map: HashMap<HardwareIrqNumber, Arc<IrqData>>,
390     hwirq_max: HardwareIrqNumber,
391     irq_max: IrqNumber,
392 }
393 
394 impl IrqDomainRevMap {
395     fn insert(&mut self, hwirq: HardwareIrqNumber, irq_data: Arc<IrqData>) {
396         self.map.insert(hwirq, irq_data);
397     }
398 
399     #[allow(dead_code)]
400     fn remove(&mut self, hwirq: HardwareIrqNumber) {
401         self.map.remove(&hwirq);
402     }
403 
404     #[allow(dead_code)]
405     fn lookup(&self, hwirq: HardwareIrqNumber) -> Option<Arc<IrqData>> {
406         self.map.get(&hwirq).cloned()
407     }
408 }
409 
410 bitflags! {
411     pub struct IrqDomainFlags: u32 {
412         /// Irq domain is hierarchical
413         const HIERARCHY = (1 << 0);
414         /// Irq domain name was allocated dynamically
415         const NAME_ALLOCATED = (1 << 1);
416         /// Irq domain is an IPI domain with virq per cpu
417         const IPI_PER_CPU = (1 << 2);
418         /// Irq domain is an IPI domain with single virq
419         const IPI_SINGLE = (1 << 3);
420         /// Irq domain implements MSIs
421         const MSI = (1 << 4);
422         /// Irq domain implements MSI remapping
423         const MSI_REMAP = (1 << 5);
424         /// Quirk to handle MSI implementations which do not provide masking
425         const MSI_NOMASK_QUIRK = (1 << 6);
426         /// Irq domain doesn't translate anything
427         const NO_MAP = (1 << 7);
428         /// Flags starting from IRQ_DOMAIN_FLAG_NONCORE are reserved
429         /// for implementation specific purposes and ignored by the core code
430         const NONCORE = (1 << 16);
431     }
432 }
433 
434 /// 如果多个域有相同的设备节点,但服务于不同的目的(例如,一个域用于PCI/MSI,另一个用于有线IRQs),
435 /// 它们可以使用特定于总线的token进行区分。预计大多数域只会携带`DomainBusAny`。
436 ///
437 /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#78
438 #[allow(dead_code)]
439 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
440 pub enum IrqDomainBusToken {
441     Any = 0,
442     Wired,
443     GenericMsi,
444     PciMsi,
445     PlatformMsi,
446     Nexus,
447     Ipi,
448     FslMcMsi,
449     TiSciIntaMsi,
450     Wakeup,
451     VmdMsi,
452 }
453 
454 /// IrqDomain的操作方法
455 ///
456 /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#107
457 pub trait IrqDomainOps: Debug + Send + Sync {
458     /// 匹配一个中断控制器设备节点到一个主机。
459     fn match_node(
460         &self,
461         irq_domain: &Arc<IrqDomain>,
462         device_node: &Arc<DeviceNode>,
463         bus_token: IrqDomainBusToken,
464     ) -> bool;
465 
466     /// 创建或更新一个虚拟中断号与一个硬件中断号之间的映射。
467     /// 对于给定的映射,这只会被调用一次。
468     ///
469     /// 如果没有实现这个方法,那么就会返回`ENOSYS`
470     fn map(
471         &self,
472         _irq_domain: &Arc<IrqDomain>,
473         _hwirq: HardwareIrqNumber,
474         _virq: IrqNumber,
475     ) -> Result<(), SystemError> {
476         Err(SystemError::ENOSYS)
477     }
478 
479     /// 删除一个虚拟中断号与一个硬件中断号之间的映射。
480     fn unmap(&self, irq_domain: &Arc<IrqDomain>, virq: IrqNumber);
481 
482     fn activate(
483         &self,
484         _domain: &Arc<IrqDomain>,
485         _irq_data: &Arc<IrqData>,
486         _reserve: bool,
487     ) -> Result<(), SystemError> {
488         Err(SystemError::ENOSYS)
489     }
490 
491     fn deactivate(&self, _domain: &Arc<IrqDomain>, _irq_data: &Arc<IrqData>) {}
492 }
493 
494 #[allow(dead_code)]
495 #[derive(Debug)]
496 pub struct IrqDomainChipGeneric {
497     inner: SpinLock<InnerIrqDomainChipGeneric>,
498 }
499 
500 #[allow(dead_code)]
501 #[derive(Debug)]
502 struct InnerIrqDomainChipGeneric {
503     irqs_per_chip: u32,
504     flags_to_clear: IrqGcFlags,
505     flags_to_set: IrqGcFlags,
506     gc_flags: IrqGcFlags,
507     gc: Vec<Arc<IrqChipGeneric>>,
508 }
509