axplat_aarch64_peripherals/
gic.rs

1//! ARM Generic Interrupt Controller (GIC).
2
3use arm_gicv2::{GicCpuInterface, GicDistributor};
4use axplat::irq::{HandlerTable, IrqHandler};
5use axplat::mem::VirtAddr;
6use kspin::SpinNoIrq;
7use lazyinit::LazyInit;
8
9/// The maximum number of IRQs.
10const MAX_IRQ_COUNT: usize = 1024;
11
12static GICD: LazyInit<SpinNoIrq<GicDistributor>> = LazyInit::new();
13
14// per-CPU, no lock
15static GICC: LazyInit<GicCpuInterface> = LazyInit::new();
16
17static IRQ_HANDLER_TABLE: HandlerTable<MAX_IRQ_COUNT> = HandlerTable::new();
18
19/// Enables or disables the given IRQ.
20pub fn set_enable(irq_num: usize, enabled: bool) {
21    trace!("GICD set enable: {} {}", irq_num, enabled);
22    GICD.lock().set_enable(irq_num as _, enabled);
23}
24
25/// Registers an IRQ handler for the given IRQ.
26///
27/// It also enables the IRQ if the registration succeeds. It returns `false`
28/// if the registration failed.
29pub fn register_handler(irq_num: usize, handler: IrqHandler) -> bool {
30    trace!("register handler IRQ {}", irq_num);
31    if IRQ_HANDLER_TABLE.register_handler(irq_num, handler) {
32        set_enable(irq_num, true);
33        return true;
34    }
35    warn!("register handler for IRQ {} failed", irq_num);
36    false
37}
38
39/// Unregisters the IRQ handler for the given IRQ.
40///
41/// It also disables the IRQ if the unregistration succeeds. It returns the
42/// existing handler if it is registered, `None` otherwise.
43pub fn unregister_handler(irq_num: usize) -> Option<IrqHandler> {
44    trace!("unregister handler IRQ {}", irq_num);
45    set_enable(irq_num, false);
46    IRQ_HANDLER_TABLE.unregister_handler(irq_num)
47}
48
49/// Handles the IRQ.
50///
51/// It is called by the common interrupt handler. It should look up in the
52/// IRQ handler table and calls the corresponding handler. If necessary, it
53/// also acknowledges the interrupt controller after handling.
54pub fn handle_irq(_unused: usize) {
55    GICC.handle_irq(|irq_num| {
56        trace!("IRQ {}", irq_num);
57        if !IRQ_HANDLER_TABLE.handle(irq_num as _) {
58            warn!("Unhandled IRQ {}", irq_num);
59        }
60    });
61}
62
63/// Initializes GICD (for the primary CPU only).
64pub fn init_gicd(gicd_base: VirtAddr, gicc_base: VirtAddr) {
65    info!("Initialize GICv2...");
66    GICD.init_once(SpinNoIrq::new(GicDistributor::new(gicd_base.as_mut_ptr())));
67    GICC.init_once(GicCpuInterface::new(gicc_base.as_mut_ptr()));
68    GICD.lock().init();
69}
70
71/// Initializes GICC (for all CPUs).
72///
73/// It must be called after [`init_gicd`].
74pub fn init_gicc() {
75    GICC.init();
76}
77
78/// Default implementation of [`axplat::irq::IrqIf`] using the GIC.
79#[macro_export]
80macro_rules! irq_if_impl {
81    ($name:ident) => {
82        struct $name;
83
84        #[impl_plat_interface]
85        impl axplat::irq::IrqIf for $name {
86            /// Enables or disables the given IRQ.
87            fn set_enable(irq: usize, enabled: bool) {
88                $crate::gic::set_enable(irq, enabled);
89            }
90
91            /// Registers an IRQ handler for the given IRQ.
92            ///
93            /// It also enables the IRQ if the registration succeeds. It returns `false`
94            /// if the registration failed.
95            fn register(irq: usize, handler: axplat::irq::IrqHandler) -> bool {
96                $crate::gic::register_handler(irq, handler)
97            }
98
99            /// Unregisters the IRQ handler for the given IRQ.
100            ///
101            /// It also disables the IRQ if the unregistration succeeds. It returns the
102            /// existing handler if it is registered, `None` otherwise.
103            fn unregister(irq: usize) -> Option<axplat::irq::IrqHandler> {
104                $crate::gic::unregister_handler(irq)
105            }
106
107            /// Handles the IRQ.
108            ///
109            /// It is called by the common interrupt handler. It should look up in the
110            /// IRQ handler table and calls the corresponding handler. If necessary, it
111            /// also acknowledges the interrupt controller after handling.
112            fn handle(irq: usize) {
113                $crate::gic::handle_irq(irq)
114            }
115        }
116    };
117}