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

No handler registered for IRQ {}

Error message

No handler registered for IRQ {}

What it means

The BCM interrupt controller's local interrupt controller iterates all pending IRQs and looks each up in its handler table; a None entry panics with 'No handler registered for IRQ'. Like the GICv2 variant, this library treats an unclaimed pending interrupt as fatal because it cannot be acknowledged and would re-fire endlessly.

Source

Thrown at 20_timer_callbacks/kernel/src/bsp/device_driver/bcm/bcm2xxx_interrupt_controller/local_ic.rs:150

    fn enable(&self, irq: &Self::IRQNumberType) {
        self.wo_registers.lock(|regs| {
            let enable_bit: u32 = 1 << (irq.get());

            // Writing a 1 to a bit will set the corresponding IRQ enable bit. All other IRQ enable
            // bits are unaffected. So we don't need read and OR'ing here.
            regs.CORE0_TIMER_INTERRUPT_CONTROL.set(enable_bit);
        });
    }

    fn handle_pending_irqs<'irq_context>(
        &'irq_context self,
        _ic: &exception::asynchronous::IRQContext<'irq_context>,
    ) {
        self.handler_table.read(|table| {
            for irq_number in self.pending_irqs() {
                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");
                    }
                }
            }
        })
    }

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

        info!("      Local handler:");

        self.handler_table.read(|table| {
            for (i, opt) in table.iter().enumerate() {
                if let Some(handler) = opt {
                    info!("            {: >3}. {}", i, handler.name());

View on GitHub (pinned to 644474cc09)

Solutions

  1. Register a handler for the reported IRQ number on every core that can receive it (local IC handlers are per-core).
  2. Disable the unwanted local IRQ in the local interrupt controller's enable registers if it should never fire.
  3. Verify the pending IRQ number against the BCM2711/BCM2837 datasheet — BCM IRQ numbering differs from GIC numbering.
  4. Initialize and register handlers identically on each core during SMP bring-up.

Example fix

// before
local_ic.irq_manager.enable(per_core_irq_number); // no handler registered on this core
// after
local_ic.irq_manager.register_handler(per_core_irq_number, &local_timer_handler);
local_ic.irq_manager.enable(per_core_irq_number);
Defensive patterns

Strategy: validation

Validate before calling

// Register the handler on every core before enabling a local IRQ:
for core in 0..NUM_CORES {
    local_ic(core).irq_manager.register_handler(irq, &handler);
    local_ic(core).irq_manager.enable(irq);
}

Type guard

fn irq_is_registered(irq: usize, table: &[Option<IrqDescriptor>]) -> bool {
    table.get(irq).is_some() && table[irq].is_some()
}

Prevention

When it happens

Trigger: pending_irqs() yields an IRQ number (from the local interrupt controller's pending registers) whose handler-table slot was never populated via register_handler() — e.g. a local/per-core interrupt (like a local timer or mailbox IRQ) enabled but not claimed.

Common situations: Enabling a local timer or aux IRQ in the local IC before registering its handler, different IRQ numbering per core causing a valid handler on core 0 to be missing on core 1, or copying GIC IRQ numbers into BCM table indices.

Related errors


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