rust-embedded/rust-raspberrypi-OS-tutorials · error

No handler registered for IRQ {}

Error message

No handler registered for IRQ {}

What it means

The GICv2 interrupt controller's handle_pending_irqs looks up the pending IRQ number in the registered handler table; if the slot is None, no driver ever registered a handler for that interrupt, so the kernel panics. This library throws it because delivering an IRQ nobody claimed to handle would leave it perpetually asserted (spurious re-fires), which is unrecoverable in this design.

Source

Thrown at 20_timer_callbacks/kernel/src/bsp/device_driver/arm/gicv2.rs:209

    }

    fn handle_pending_irqs<'irq_context>(
        &'irq_context self,
        ic: &exception::asynchronous::IRQContext<'irq_context>,
    ) {
        // Extract the highest priority pending IRQ number from the Interrupt Acknowledge Register
        // (IAR).
        let irq_number = self.gicc.pending_irq_number(ic);

        // Guard against spurious interrupts.
        if irq_number > GICv2::MAX_IRQ_NUMBER {
            return;
        }

        // Call the IRQ handler. Panic if there is none.
        self.handler_table.read(|table| {
            match table[irq_number] {
                None => panic!("No handler registered for IRQ {}", irq_number),
                Some(descriptor) => {
                    // Call the IRQ handler. Panics on failure.
                    descriptor.handler().handle().expect("Error handling IRQ");
                }
            }
        });

        // Signal completion of handling.
        self.gicc.mark_comleted(irq_number as u32, ic);
    }

    fn print_handler(&self) {
        use crate::info;

        info!("      Peripheral handler:");

        self.handler_table.read(|table| {
            for (i, opt) in table.iter().skip(32).enumerate() {

View on GitHub (pinned to 644474cc09)

Solutions

  1. Call register_handler() with an IRQHandler descriptor for the reported IRQ number before enabling it in the GIC.
  2. Disable the unexpected IRQ at the GIC distributor (GICD_ICENABLER) if it should not fire, or mask it at the peripheral.
  3. Cross-check the IRQ number printed in the panic against the device datasheet and the BSP's defined IRQ numbers.
  4. Add a default/no-op handler for interrupts you intend to ignore but not mask.

Example fix

// before
interrupt_controller.irq_manager.enable(irq_number); // enabled but no handler registered
// after
interrupt_controller.irq_manager.register_handler(irq_number, &timer_handler);
interrupt_controller.irq_manager.enable(irq_number);
Defensive patterns

Strategy: validation

Validate before calling

// Before enabling an IRQ, assert a handler is registered:
fn can_enable(irq: usize, table: &[Option<IrqDescriptor>; N]) -> bool {
    table.get(irq).map_or(false, |slot| slot.is_some())
}

Type guard

fn has_handler(irq: usize, table: &[Option<IrqDescriptor>]) -> bool {
    matches!(table.get(irq), Some(Some(_)))
}

Prevention

When it happens

Trigger: A pending interrupt read from GICD_ISENABLER/GICC IAR has an ID whose entry in the IRQ handler table is None — i.e. the IRQ was enabled at the GIC/distributor level but register_handler() was never called for it.

Common situations: Enabling an IRQ in the GIC distributor before registering its handler, BSP interrupt-controller init enabling default IRQs, adding a new device that fires an IRQ but forgetting to register its handler, or IRQ number mismatches between datasheet and code.

Related errors


AI-assisted analysis of rust-embedded/rust-raspberrypi-OS-tutorials@644474cc09 (2026-09-06). Data as JSON: /api/errors/59726003563d052d. Report an issue: GitHub.