IOAPIC: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
m (volatile is required, otherwise the optimizer may optimize the code away...)
(re-writing alot of it.)
Line 9: Line 9:
The register IOREGSEL is an MMIO register select register that is used to access all the other I/O APIC registers. IOREGSEL only selects the register and does not actually write or read the register. The IOWIN register is the 'data' register. Once the IOREGSEL register has been set, the IOWIN register can be used to write or read the register in the IOREGSEL.
The register IOREGSEL is an MMIO register select register that is used to access all the other I/O APIC registers. IOREGSEL only selects the register and does not actually write or read the register. The IOWIN register is the 'data' register. Once the IOREGSEL register has been set, the IOWIN register can be used to write or read the register in the IOREGSEL.


write_ioapic_register is a basic function that writes a 32-bit value (val) to an 8-bit IOAPIC register offset (offset).
eg:
read_ioapic_register is a basic function that reads a 32-bit value from an 8-bit IOAPIC register offset (offset).
// set IOREGSEL

void io_apic_set_reg(uint32 address, uint8 reg)
void write_ioapic_register(const ptr_t apic_base, const uint8_t offset, const uint32_t val)
{
{
/* tell IOREGSEL where we want to write to */
*(volatile uint32*)(address) = (uint32)reg; // set IOREGSEL
*(uint32_t*)(apic_base) = offset;
/* write the value to IOWIN */
*(uint32_t*)(apic_base + 0x10) = val;
}
}
uint32_t read_ioapic_register(const ptr_t apic_base, const uint8_t offset)
// write a 64-bit value to IO APIC (two 32-bit values)
void io_apic write_64(uint32 address, uint8 reg, uint32 val_hi, uint32 val_lo)
{
{
/* tell IOREGSEL where we want to read from */
io_apic_set_reg(address, reg); // set reg (bits 0-31)
*(uint32_t*)(apic_base) = offset;
*(volatile uint32*)(address + 0x10) = (uint32)(val_lo); // write val (bits 0-31)
/* return the data from IOWIN */
io_apic_set_reg(address, reg+1); // set reg (bits 32-63)
return *(uint32_t*)(apic_base + 0x10);
*(volatile uint32*)(address + 0x10) = (uint32)(val_hi); // write val (bits 32-63)
}
// read a 32-bit value from IO APIC
uint32 io_apic_read_32(uint32 address, uint8 reg)
{
io_apic_set_reg(address, reg); // set reg
return *(volatile uint32*)(address + 0x10);
}
}


note: ptr_t is the size of the default memory reference, if a 32-bit Protected Mode setup, it's equivalent to uint32_t, and uint64_t on 64-bit Long Mode environments.
Before proceeding too far into getting IO interrupts enabled, the IO interrupts should be disabled in the IO Redirection Table (registers 0x10-0x3F). This can be accomplished by setting bit 16 (Interrupt Mask) in the IOREDTBL(x) registers. The register 0x01 bits 16-23 (Maximum Redirection Entry) will tell you how many Redirection Entries there is in the current IO APIC. This value will be used to loop through the IO Redirections in order to mask them all to start with.
note: (apic_base) is the memory base address for a selected IOAPIC, these can be found by enumerating them from the MP Tables.

uint8 io_apic_read_max_redirects(uint32 address)
{
io_apic_set_reg(address, 1);
return (uint8)((*(volatile uint32*)(address + 0x10) >> 16) & 0xFF);
}
void io_apic_mask_all_redirects(uint32 address)
{
uint16 i;
for(i = 0; i < io_apic_read_max_redirects(address); i++)
{
io_apic_write_64(address, 0x10 + (i * 2), 0x00000000, (0x00010000 | 0x20)); // mask all, vect: 0x20
}
}


== External Links ==
== External Links ==

Revision as of 00:51, 23 January 2009

Basic Info

The Intel I/O Advanced Programmable Interrupt Controller is used to distribute external interrupts in a more advanced manner than that of the standard PIC. With the I/O APIC, interrupts can be distributed to physical or logical (clusters) of processors and can be prioritized.

Detecting I/O APIC

In order to detect the existence of an I/O APIC (or multiple ones), the Intel Multi-Processor or ACPI tables must be parsed. In the MP tables, configuration tables with the entry identification of 0x02 are for I/O APICs. Parsing will tell how many (if any) I/O APICs exist and what their ID and MMIO address is. For more information of parsing the MP tables, see the External MP Tables Links section below.

Programming the I/O APIC

IOREGSEL and IOWIN

The register IOREGSEL is an MMIO register select register that is used to access all the other I/O APIC registers. IOREGSEL only selects the register and does not actually write or read the register. The IOWIN register is the 'data' register. Once the IOREGSEL register has been set, the IOWIN register can be used to write or read the register in the IOREGSEL.

write_ioapic_register is a basic function that writes a 32-bit value (val) to an 8-bit IOAPIC register offset (offset). read_ioapic_register is a basic function that reads a 32-bit value from an 8-bit IOAPIC register offset (offset).

void write_ioapic_register(const ptr_t apic_base, const uint8_t offset, const uint32_t val)
{
    /* tell IOREGSEL where we want to write to */
    *(uint32_t*)(apic_base) = offset;
    /* write the value to IOWIN */
    *(uint32_t*)(apic_base + 0x10) = val;
}

uint32_t read_ioapic_register(const ptr_t apic_base, const uint8_t offset)
{
    /* tell IOREGSEL where we want to read from */
    *(uint32_t*)(apic_base) = offset;
    /* return the data from IOWIN */
    return *(uint32_t*)(apic_base + 0x10);
}
note: ptr_t is the size of the default memory reference, if a 32-bit Protected Mode setup, it's equivalent to uint32_t, and uint64_t on 64-bit Long Mode environments.
note: (apic_base) is the memory base address for a selected IOAPIC, these can be found by enumerating them from the MP Tables.

External Links

MP Tables

I/O APIC