operating-systemacpiapic

How to figure out the interrupt source on I/O APIC?


I understand that I/O APIC chip has 24 PINs, usually single chip system will map PIN 0~23 IRQ 32~55 respectively. Furthermore I could edit the related RTEs to allocate interrupt handler functions.

But how can I figure out the I/O APIC interrupt source on each PINs?

I understand that it is related to ACPI, but on detail how should I do this, is it mapped on some ACPI table? or I should use AML to check it??

Thank you very much!!


Solution

  • The general steps (for a modern OS) are:

    Preparation

    a) Parse the ACPI "APIC/MADT" table to determine if PIC chips exist (PCAT_COMPAT flag), how many IO APICs there are, and how many inputs each IO APIC has. If ACPI doesn't exist, you might want to try searching for/parsing older "MultiProcessor Spec." table and extracting the same information; however, if ACPI does exist it's possible that the "MultiProcessor Spec." table is designed to provide a "minimum stub" that's contains no real information (so you must check ACPI first and prefer using ACPI if it exists), and it may not be worth the hassle of supporting systems that don't support ACPI (especially if the operating system requires a 64-bit CPU, etc).

    b) Parse the ACPI "FADT" to determine if MSI may (or must not) be enabled

    c) Determine if the OS will use PIC alone, IO APICs alone, or IO APIC plus MSI. Note that this can (should?) take into account the operating system's own boot parameters and/or configuration (e.g. so if there's a compatibility problem the end user can work around the problem).

    d) If PIC chips exist; mask all IRQs in the PIC chips, then reconfigure the PIC chips (to set whatever "base vector number" you want them to use - e.g. maybe so that the master PIC is interrupt vectors 32 to 39 and the slave is vectors 40 to 47). If IO APIC/s exist, mask all IRQs in each IO APIC. Note: if the PIC chips exist they both have a "spurious IRQ" that can't be masked, so if you don't want to use PIC chips it's still a good idea to reconfigure the PIC chips such that their spurious IRQs (and the interrupt handlers for them) aren't going to be in the way.

    e) Use an ACPI AML interpreter to execute the _PIC object; to inform ACPI/AML that you will be using either IO APIC or PIC. Note that "OS uses PIC" is the default for backward compatibility, so this step could be skipped if you're not using IO APIC.

    f) Configure the local APIC in each CPU (not covered here).

    Devices

    Before starting a device driver for a device:

    a) Figure out the device's details (e.g. use "class, subclass and programming interface" fields from PCI configuration space to figure out what the device is) and check if you actually have a device driver for it; and decide if you want the device to use PCI IRQs or MSI.

    b1) If the device will be using PCI IRQs and if the OS is using PIC chips (and not IO APICs); get the "Interrupt Line" field from the device's PCI configuration space and determine which interrupt vector it will be by adding the corresponding PIC chip's "base interrupt vector" to it.

    b2) If the device will be using PCI IRQs (and not MSI) and if the OS is using IO APIC and not PIC; determine which "interrupt pin at the PCI slot" the device uses by reading the "Interupt Pin" field from the device's PCI configuration space. Then use an ACPI AML interpreter to execute the _PRT object and get a current (not forgetting that PCI-E supports "hot-plug") PCI IRQ routing table. Use this table (and the PCI device's "bus:device:function" address and which "interrupt pin" it uses) to determine where the PCI IRQ is connected (e.g. which global interrupt, which determines which input of which IO APIC). Then; if you haven't already (because the same interrupt line is shared by a different device) use some kind of "interrupt vector manager" to allocate an interrupt vector for the PCI IRQ, and configure the IO APIC input to generate that interrupt vector. Note that (for IO APIC and MSI) "interrupt vector" determines "IRQ priority", so so for high speed/latency sensitive devices (e.g. network card) you'll want interrupt vectors that imply "high IRQ priority" and for a slower/less latency sensitive devices (e.g. USB controller) you'll want to use interrupt vectors that imply "lower IRQ priority".

    b3) If the device will be using MSI; determine how many consecutive interrupt vectors the device wants; then use some kind of "interrupt vector manager" to try allocate as many consecutive interrupt vectors as the device wants. Note that it is possible to give the device less interrupts than it wants.

    c) Regardless of how it happened, you now know which interrupt vector/s the device will use. Start the device driver that's suitable for the device, and tell the device driver which interrupt vectors its device will use (and which MMIO regions, etc).

    Note: There's more advanced ways to assign interrupt vectors than "first come first served"; and there's probably no technical reason why you can't re-evaluate/re-assign interrupt vectors later as some kind of dynamic optimization scheme (e.g. re-allocating interrupt vectors so they're given to frequently used PCI devices instead of idle/unused PCI devices).