Search code examples
cassemblylinux-kernelinterruptkernel-module

Error while raising interrupt 11 with inline asm into kernel module


I 'm trying to raise interrupt 11 with inline asm inside a kernel module LKM

asm("int $0x3B");

But after this line, I read in the dmesg

do_IRQ: 1.59 No irq handler for vector

This is driver code that I have taken from website https://embetronicx.com/tutorials/linux/device-drivers/linux-device-driver-tutorial-part-13-interrupt-example-program-in-linux-kernel/ When I try to execute "cat /dev/etx_device" I got by dmesg

do_IRQ: 1.59 No irq handler for vector

My kernel version is 5.1.20-200.fc29.x86_64 AMD processor. What is the problem?

  cat /proc/interrupts: 
      CPU0       CPU1       
  0:        110          0   IO-APIC   2-edge      timer
  8:          1          0   IO-APIC   8-edge      rtc0
  9:          0          0   IO-APIC   9-fasteoi   acpi
 11:          0          0   IO-APIC  11-edge      etx_device

Solution

  • This used to work on older kernel versions, but fails on later versions. The reason is that the generic IRQ handler do_IRQ() has been changed for better IRQ handling performance. Instead of using the irq_to_desc() function to get the IRQ descriptor, it reads it from the per-CPU data. The descriptor is put there during the physical device initialization. Since this pseudo device driver don't have a physical device, do_IRQ() don't find it there and returns with an error. If we want to simulate IRQ using software interrupt, we must first write the IRQ descriptor to the per-CPU data. Unfortunately, the symbol vector_irq, the array of the IRQ descriptors in the per-CPU data, is not exported to kernel modules during kernel compilation. The only way to change it, is to recompile the whole kernel. If you think it worth the effort, you can add the line:

    EXPORT_SYMBOL (vector_irq);
    

    in the file: arch/x86/kernel/irq.c

    right after all the include lines. After compiling and booting from the newly compiled kernel, change your driver as follows:

    Add an include line:

        #include <asm/hw_irq.h>
    

    change the read function to:

    static ssize_t etx_read(struct file *filp,
                    char __user *buf, size_t len, loff_t *off)
    {
            struct irq_desc *desc;
    
            printk(KERN_INFO "Read function\n");
            desc = irq_to_desc(11);
            if (!desc) return -EINVAL;
            __this_cpu_write(vector_irq[59], desc);
            asm("int $0x3B");  // Corresponding to irq 11
            return 0;
    }