axplat_aarch64_peripherals/
gic.rs

1//! ARM Generic Interrupt Controller (GIC).
2
3use arm_gic_driver::v2::{Ack, Gic, IntId, SGITarget, TargetList, TrapOp, VirtAddr};
4use axplat::irq::{HandlerTable, IpiTarget, IrqHandler};
5use kspin::SpinNoIrq;
6use lazyinit::LazyInit;
7
8/// The maximum number of IRQs.
9const MAX_IRQ_COUNT: usize = 1024;
10
11static GIC: LazyInit<SpinNoIrq<Gic>> = LazyInit::new();
12
13static TRAP_OP: LazyInit<TrapOp> = LazyInit::new();
14
15static IRQ_HANDLER_TABLE: HandlerTable<MAX_IRQ_COUNT> = HandlerTable::new();
16
17/// Enables or disables the given IRQ.
18pub fn set_enable(irq_num: usize, enabled: bool) {
19    trace!("GIC set enable: {} {}", irq_num, enabled);
20    let intid = unsafe { IntId::raw(irq_num as u32) };
21    GIC.lock().set_irq_enable(intid, enabled);
22}
23
24/// Registers an IRQ handler for the given IRQ.
25///
26/// It also enables the IRQ if the registration succeeds. It returns `false`
27/// if the registration failed.
28pub fn register_handler(irq_num: usize, handler: IrqHandler) -> bool {
29    trace!("register handler IRQ {}", irq_num);
30    if IRQ_HANDLER_TABLE.register_handler(irq_num, handler) {
31        set_enable(irq_num, true);
32        return true;
33    }
34    warn!("register handler for IRQ {} failed", irq_num);
35    false
36}
37
38/// Unregisters the IRQ handler for the given IRQ.
39///
40/// It also disables the IRQ if the unregistration succeeds. It returns the
41/// existing handler if it is registered, `None` otherwise.
42pub fn unregister_handler(irq_num: usize) -> Option<IrqHandler> {
43    trace!("unregister handler IRQ {}", irq_num);
44    set_enable(irq_num, false);
45    IRQ_HANDLER_TABLE.unregister_handler(irq_num)
46}
47
48/// Handles the IRQ.
49///
50/// It is called by the common interrupt handler. It should look up in the
51/// IRQ handler table and calls the corresponding handler. If necessary, it
52/// also acknowledges the interrupt controller after handling.
53pub fn handle_irq(_unused: usize) {
54    let ack = TRAP_OP.ack();
55    debug!("Handling IRQ: {ack:?}");
56
57    let irq_num = match ack {
58        Ack::Other(intid) => intid,
59        Ack::SGI { intid, cpu_id: _ } => intid,
60    };
61    if !IRQ_HANDLER_TABLE.handle(irq_num.to_u32() as _) {
62        warn!("Unhandled IRQ {:?}", irq_num);
63    }
64    if !ack.is_special() {
65        TRAP_OP.eoi(ack);
66        if TRAP_OP.eoi_mode_ns() {
67            TRAP_OP.dir(ack);
68        }
69    }
70}
71
72/// Initializes GIC
73pub fn init_gic(gicd_base: axplat::mem::VirtAddr, gicc_base: axplat::mem::VirtAddr) {
74    info!("Initialize GICv2...");
75    let gicd_base = VirtAddr::new(gicd_base.into());
76    let gicc_base = VirtAddr::new(gicc_base.into());
77
78    let mut gic = unsafe { Gic::new(gicd_base, gicc_base, None) };
79    gic.init();
80
81    GIC.init_once(SpinNoIrq::new(gic));
82    let cpu = GIC.lock().cpu_interface();
83    TRAP_OP.init_once(cpu.trap_operations());
84}
85
86/// Initializes GICC (for all CPUs).
87///
88/// It must be called after [`init_gic`].
89pub fn init_gicc() {
90    debug!("Initialize GIC CPU Interface...");
91    let mut cpu = GIC.lock().cpu_interface();
92    cpu.init_current_cpu();
93    cpu.set_eoi_mode_ns(false);
94}
95
96/// Sends an inter-processor interrupt (IPI) to the specified target CPU or all CPUs.
97pub fn send_ipi(irq_num: usize, target: IpiTarget) {
98    match target {
99        IpiTarget::Current { cpu_id: _ } => {
100            GIC.lock()
101                .send_sgi(IntId::sgi(irq_num as u32), SGITarget::Current);
102        }
103        IpiTarget::Other { cpu_id } => {
104            let target_list = TargetList::new(&mut [cpu_id].into_iter());
105            GIC.lock().send_sgi(
106                IntId::sgi(irq_num as u32),
107                SGITarget::TargetList(target_list),
108            );
109        }
110        IpiTarget::AllExceptCurrent {
111            cpu_id: _,
112            cpu_num: _,
113        } => {
114            GIC.lock()
115                .send_sgi(IntId::sgi(irq_num as u32), SGITarget::AllOther);
116        }
117    }
118}
119
120/// Default implementation of [`axplat::irq::IrqIf`] using the GIC.
121#[macro_export]
122macro_rules! irq_if_impl {
123    ($name:ident) => {
124        struct $name;
125
126        #[impl_plat_interface]
127        impl axplat::irq::IrqIf for $name {
128            /// Enables or disables the given IRQ.
129            fn set_enable(irq: usize, enabled: bool) {
130                $crate::gic::set_enable(irq, enabled);
131            }
132
133            /// Registers an IRQ handler for the given IRQ.
134            ///
135            /// It also enables the IRQ if the registration succeeds. It returns `false`
136            /// if the registration failed.
137            fn register(irq: usize, handler: axplat::irq::IrqHandler) -> bool {
138                $crate::gic::register_handler(irq, handler)
139            }
140
141            /// Unregisters the IRQ handler for the given IRQ.
142            ///
143            /// It also disables the IRQ if the unregistration succeeds. It returns the
144            /// existing handler if it is registered, `None` otherwise.
145            fn unregister(irq: usize) -> Option<axplat::irq::IrqHandler> {
146                $crate::gic::unregister_handler(irq)
147            }
148
149            /// Handles the IRQ.
150            ///
151            /// It is called by the common interrupt handler. It should look up in the
152            /// IRQ handler table and calls the corresponding handler. If necessary, it
153            /// also acknowledges the interrupt controller after handling.
154            fn handle(irq: usize) {
155                $crate::gic::handle_irq(irq)
156            }
157
158            /// Sends an inter-processor interrupt (IPI) to the specified target CPU or all CPUs.
159            fn send_ipi(irq_num: usize, target: axplat::irq::IpiTarget) {
160                $crate::gic::send_ipi(irq_num, target);
161            }
162        }
163    };
164}