* Prepared everything to eventually support multiple IO-APICs.

* Added some TODOs regarding register write order that might have bad side
  effects.
* Some cleanup.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@41428 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz
2011-05-11 02:50:36 +00:00
parent 20537052a2
commit 5d01e61a91
+194 -108
View File
@@ -78,57 +78,88 @@
#define IO_APIC_INTERRUPT_VECTOR_MASK 0xff
typedef struct ioapic_s {
struct ioapic_registers {
volatile uint32 io_register_select;
uint32 reserved[3];
volatile uint32 io_window_register;
} ioapic;
};
static ioapic *sIOAPIC = NULL;
static uint32 sIOAPICMaxRedirectionEntry = 23;
static uint64 sLevelTriggeredInterrupts = 0;
// binary mask: 1 level, 0 edge
struct ioapic {
uint8 number;
uint8 max_redirection_entry;
uint8 global_interrupt_base;
uint8 global_interrupt_last;
uint64 level_triggered_mask;
area_id register_area;
ioapic_registers* registers;
ioapic* next;
};
static ioapic sIOAPICs = { 0, 0, 0, 0, 0, -1, NULL, NULL };
// #pragma mark - I/O APIC
static inline uint32
ioapic_read_32(uint8 registerSelect)
static inline struct ioapic*
find_ioapic(int32 gsi)
{
sIOAPIC->io_register_select = registerSelect;
return sIOAPIC->io_window_register;
if (gsi < 0)
return NULL;
struct ioapic* current = &sIOAPICs;
while (current != NULL) {
if (gsi >= current->global_interrupt_base
&& gsi <= current->global_interrupt_last) {
return current;
}
current = current->next;
}
return NULL;
}
static inline uint32
ioapic_read_32(struct ioapic& ioapic, uint8 registerSelect)
{
ioapic.registers->io_register_select = registerSelect;
return ioapic.registers->io_window_register;
}
static inline void
ioapic_write_32(uint8 registerSelect, uint32 value)
ioapic_write_32(struct ioapic& ioapic, uint8 registerSelect, uint32 value)
{
sIOAPIC->io_register_select = registerSelect;
sIOAPIC->io_window_register = value;
ioapic.registers->io_register_select = registerSelect;
ioapic.registers->io_window_register = value;
}
static inline uint64
ioapic_read_64(uint8 registerSelect)
ioapic_read_64(struct ioapic& ioapic, uint8 registerSelect)
{
sIOAPIC->io_register_select = registerSelect + 1;
uint64 result = sIOAPIC->io_window_register;
ioapic.registers->io_register_select = registerSelect + 1;
uint64 result = ioapic.registers->io_window_register;
result <<= 32;
sIOAPIC->io_register_select = registerSelect;
result |= sIOAPIC->io_window_register;
ioapic.registers->io_register_select = registerSelect;
result |= ioapic.registers->io_window_register;
return result;
}
static inline void
ioapic_write_64(uint8 registerSelect, uint64 value)
ioapic_write_64(struct ioapic& ioapic, uint8 registerSelect, uint64 value)
{
sIOAPIC->io_register_select = registerSelect;
sIOAPIC->io_window_register = (uint32)value;
sIOAPIC->io_register_select = registerSelect + 1;
sIOAPIC->io_window_register = (uint32)(value >> 32);
ioapic.registers->io_register_select = registerSelect;
ioapic.registers->io_window_register = (uint32)value;
ioapic.registers->io_register_select = registerSelect + 1;
ioapic.registers->io_window_register = (uint32)(value >> 32);
}
@@ -141,12 +172,14 @@ ioapic_is_spurious_interrupt(int32 num)
static bool
ioapic_is_level_triggered_interrupt(int32 pin)
ioapic_is_level_triggered_interrupt(int32 gsi)
{
if (pin < 0 || pin > (int32)sIOAPICMaxRedirectionEntry)
struct ioapic* ioapic = find_ioapic(gsi);
if (ioapic == NULL)
return false;
return (sLevelTriggeredInterrupts & (1 << pin)) != 0;
uint8 pin = gsi - ioapic->global_interrupt_base;
return (ioapic->level_triggered_mask & (1 << pin)) != 0;
}
@@ -159,55 +192,67 @@ ioapic_end_of_interrupt(int32 num)
static void
ioapic_enable_io_interrupt(int32 pin)
ioapic_enable_io_interrupt(int32 gsi)
{
if (pin < 0 || pin > (int32)sIOAPICMaxRedirectionEntry)
struct ioapic* ioapic = find_ioapic(gsi);
if (ioapic == NULL)
return;
TRACE(("ioapic_enable_io_interrupt: pin %ld\n", pin));
uint8 pin = gsi - ioapic->global_interrupt_base;
TRACE(("ioapic_enable_io_interrupt: gsi %ld -> io-apic %u pin %u\n",
gsi, ioapic->number, pin));
uint64 entry = ioapic_read_64(IO_APIC_REDIRECTION_TABLE + pin * 2);
uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2);
entry &= ~(1 << IO_APIC_INTERRUPT_MASK_SHIFT);
entry |= IO_APIC_INTERRUPT_UNMASKED << IO_APIC_INTERRUPT_MASK_SHIFT;
ioapic_write_64(IO_APIC_REDIRECTION_TABLE + pin * 2, entry);
ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry);
// TODO: Take writing order into account! We must not unmask the entry
// before the other half is valid.
}
static void
ioapic_disable_io_interrupt(int32 pin)
ioapic_disable_io_interrupt(int32 gsi)
{
if (pin < 0 || pin > (int32)sIOAPICMaxRedirectionEntry)
struct ioapic* ioapic = find_ioapic(gsi);
if (ioapic == NULL)
return;
TRACE(("ioapic_disable_io_interrupt: pin %ld\n", pin));
uint8 pin = gsi - ioapic->global_interrupt_base;
TRACE(("ioapic_disable_io_interrupt: gsi %ld -> io-apic %u pin %u\n",
gsi, ioapic->number, pin));
uint64 entry = ioapic_read_64(IO_APIC_REDIRECTION_TABLE + pin * 2);
uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2);
entry &= ~(1 << IO_APIC_INTERRUPT_MASK_SHIFT);
entry |= IO_APIC_INTERRUPT_MASKED << IO_APIC_INTERRUPT_MASK_SHIFT;
ioapic_write_64(IO_APIC_REDIRECTION_TABLE + pin * 2, entry);
ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry);
// TODO: Take writing order into account! We must not modify the entry
// before it is masked.
}
static void
ioapic_configure_io_interrupt(int32 pin, uint32 config)
ioapic_configure_io_interrupt(int32 gsi, uint32 config)
{
if (pin < 0 || pin > (int32)sIOAPICMaxRedirectionEntry)
struct ioapic* ioapic = find_ioapic(gsi);
if (ioapic == NULL)
return;
TRACE(("ioapic_configure_io_interrupt: pin %ld; config 0x%08lx\n", pin,
config));
uint8 pin = gsi - ioapic->global_interrupt_base;
TRACE(("ioapic_configure_io_interrupt: gsi %ld -> io-apic %u pin %u; "
"config 0x%08lx\n", gsi, ioapic->number, pin, config));
uint64 entry = ioapic_read_64(IO_APIC_REDIRECTION_TABLE + pin * 2);
uint64 entry = ioapic_read_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2);
entry &= ~((1 << IO_APIC_TRIGGER_MODE_SHIFT)
| (1 << IO_APIC_PIN_POLARITY_SHIFT)
| (IO_APIC_INTERRUPT_VECTOR_MASK << IO_APIC_INTERRUPT_VECTOR_SHIFT));
if (config & B_LEVEL_TRIGGERED) {
entry |= (IO_APIC_TRIGGER_MODE_LEVEL << IO_APIC_TRIGGER_MODE_SHIFT);
sLevelTriggeredInterrupts |= (1 << pin);
ioapic->level_triggered_mask |= (1 << pin);
} else {
entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT);
sLevelTriggeredInterrupts &= ~(1 << pin);
ioapic->level_triggered_mask &= ~(1 << pin);
}
if (config & B_LOW_ACTIVE_POLARITY)
@@ -216,7 +261,82 @@ ioapic_configure_io_interrupt(int32 pin, uint32 config)
entry |= (IO_APIC_PIN_POLARITY_HIGH_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT);
entry |= (pin + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT;
ioapic_write_64(IO_APIC_REDIRECTION_TABLE + pin * 2, entry);
ioapic_write_64(*ioapic, IO_APIC_REDIRECTION_TABLE + pin * 2, entry);
}
static status_t
ioapic_map_ioapic(struct ioapic& ioapic, phys_addr_t physicalAddress)
{
ioapic.register_area = vm_map_physical_memory(B_SYSTEM_TEAM, "io-apic",
(void**)&ioapic.registers, ioapic.registers != NULL ? B_EXACT_ADDRESS
: B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE, B_KERNEL_READ_AREA
| B_KERNEL_WRITE_AREA, physicalAddress, true);
if (ioapic.register_area < 0) {
panic("mapping io-apic %u failed", ioapic.number);
return ioapic.register_area;
}
uint32 version = ioapic_read_32(ioapic, IO_APIC_VERSION);
if (version == 0xffffffff) {
dprintf("io-apic %u seems inaccessible, not using it\n",
ioapic.number);
vm_delete_area(B_SYSTEM_TEAM, ioapic.register_area, true);
ioapic.register_area = -1;
ioapic.registers = NULL;
return B_ERROR;
}
ioapic.max_redirection_entry
= ((version >> IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT)
& IO_APIC_MAX_REDIRECTION_ENTRY_MASK);
ioapic.global_interrupt_last
= ioapic.global_interrupt_base + ioapic.max_redirection_entry;
dprintf("io-apic %u has range %u-%u, %u entries\n", ioapic.number,
ioapic.global_interrupt_base, ioapic.global_interrupt_last,
ioapic.max_redirection_entry + 1);
return B_OK;
}
static status_t
ioapic_initialize_ioapic(struct ioapic& ioapic, uint64 targetAPIC)
{
// program the interrupt vectors of the io-apic
ioapic.level_triggered_mask = 0;
for (uint8 i = 0; i <= ioapic.max_redirection_entry; i++) {
// initialize everything to deliver to the boot CPU in physical mode
// and masked until explicitly enabled through enable_io_interrupt()
uint64 entry = (targetAPIC << IO_APIC_DESTINATION_FIELD_SHIFT)
| (IO_APIC_INTERRUPT_MASKED << IO_APIC_INTERRUPT_MASK_SHIFT)
| (IO_APIC_DESTINATION_MODE_PHYSICAL << IO_APIC_DESTINATION_MODE_SHIFT)
| ((i + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT);
if (i == 0) {
// make redirection entry 0 into an external interrupt
entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT)
| (IO_APIC_PIN_POLARITY_HIGH_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT)
| (IO_APIC_DELIVERY_MODE_EXT_INT << IO_APIC_DELIVERY_MODE_SHIFT);
} else if (i < 16) {
// make 1-15 ISA interrupts
entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT)
| (IO_APIC_PIN_POLARITY_HIGH_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT)
| (IO_APIC_DELIVERY_MODE_FIXED << IO_APIC_DELIVERY_MODE_SHIFT);
} else {
// and the rest are PCI interrupts
entry |= (IO_APIC_TRIGGER_MODE_LEVEL << IO_APIC_TRIGGER_MODE_SHIFT)
| (IO_APIC_PIN_POLARITY_LOW_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT)
| (IO_APIC_DELIVERY_MODE_FIXED << IO_APIC_DELIVERY_MODE_SHIFT);
ioapic.level_triggered_mask |= (1 << i);
}
ioapic_write_64(ioapic, IO_APIC_REDIRECTION_TABLE + 2 * i, entry);
}
return B_OK;
}
@@ -250,19 +370,14 @@ ioapic_map(kernel_args* args)
}
if (args->arch_args.ioapic == NULL) {
dprintf("no ioapic available, not using ioapics for interrupt routing\n");
dprintf("no io-apic available, not using io-apics for interrupt "
"routing\n");
return;
}
// map in the (first) IO-APIC
sIOAPIC = (ioapic *)args->arch_args.ioapic;
if (vm_map_physical_memory(B_SYSTEM_TEAM, "ioapic", (void**)&sIOAPIC,
B_EXACT_ADDRESS, B_PAGE_SIZE,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
args->arch_args.ioapic_phys, true) < 0) {
panic("mapping the ioapic failed");
return;
}
// map in the first IO-APIC
sIOAPICs.registers = (ioapic_registers*)args->arch_args.ioapic;
ioapic_map_ioapic(sIOAPICs, args->arch_args.ioapic_phys);
}
@@ -279,36 +394,30 @@ ioapic_init(kernel_args* args)
&ioapic_end_of_interrupt
};
if (sIOAPIC == NULL)
if (sIOAPICs.register_area < 0 || sIOAPICs.registers == NULL)
return;
#if 0
if (get_safemode_boolean(B_SAFEMODE_DISABLE_IOAPIC, false)) {
dprintf("ioapic explicitly disabled, not using ioapics for interrupt "
"routing\n");
dprintf("io-apics explicitly disabled, not using io-apics for "
"interrupt routing\n");
return;
}
#else
// TODO: This can be removed once IO-APIC code is broadly tested
if (!get_safemode_boolean(B_SAFEMODE_ENABLE_IOAPIC, false)) {
dprintf("ioapic not enabled, not using ioapics for interrupt "
dprintf("io-apics not enabled, not using io-apics for interrupt "
"routing\n");
return;
}
#endif
uint32 version = ioapic_read_32(IO_APIC_VERSION);
if (version == 0xffffffff) {
dprintf("ioapic seems inaccessible, not using it\n");
return;
}
// load acpi module
status_t status;
acpi_module_info* acpiModule;
status = get_module(B_ACPI_MODULE_NAME, (module_info**)&acpiModule);
if (status != B_OK) {
dprintf("acpi module not available, not configuring ioapic\n");
dprintf("acpi module not available, not configuring io-apics\n");
return;
}
BPrivate::CObjectDeleter<const char, status_t>
@@ -323,18 +432,13 @@ ioapic_init(kernel_args* args)
// aren't different routings based on it this is non-fatal
}
sLevelTriggeredInterrupts = 0;
sIOAPICMaxRedirectionEntry
= ((version >> IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT)
& IO_APIC_MAX_REDIRECTION_ENTRY_MASK);
TRACE(("ioapic has %lu entries\n", sIOAPICMaxRedirectionEntry + 1));
// TODO: read out all IO-APICs from ACPI and set them up
IRQRoutingTable table;
status = prepare_irq_routing(acpiModule, table,
sIOAPICMaxRedirectionEntry + 1);
sIOAPICs.max_redirection_entry + 1);
if (status != B_OK) {
dprintf("IRQ routing preparation failed, not configuring ioapic.\n");
dprintf("IRQ routing preparation failed, not configuring io-apics\n");
acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC);
// revert to PIC interrupt model just in case
return;
@@ -342,6 +446,21 @@ ioapic_init(kernel_args* args)
print_irq_routing_table(table);
// use the boot CPU as the target for all interrupts
uint64 targetAPIC = args->arch_args.cpu_apic_id[0];
struct ioapic* current = &sIOAPICs;
while (current != NULL) {
status = ioapic_initialize_ioapic(*current, targetAPIC);
if (status != B_OK) {
panic("failed to initialize io-apic %u", current->number);
acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC);
return;
}
current = current->next;
}
status = enable_irq_routing(acpiModule, table);
if (status != B_OK) {
panic("failed to enable IRQ routing");
@@ -350,40 +469,7 @@ ioapic_init(kernel_args* args)
return;
}
// use the boot CPU as the target for all interrupts
uint64 targetAPIC = args->arch_args.cpu_apic_id[0];
// program the interrupt vectors of the ioapic
for (uint32 i = 0; i <= sIOAPICMaxRedirectionEntry; i++) {
// initialize everything to deliver to the boot CPU in physical mode
// and masked until explicitly enabled through enable_io_interrupt()
uint64 entry = (targetAPIC << IO_APIC_DESTINATION_FIELD_SHIFT)
| (IO_APIC_INTERRUPT_MASKED << IO_APIC_INTERRUPT_MASK_SHIFT)
| (IO_APIC_DESTINATION_MODE_PHYSICAL << IO_APIC_DESTINATION_MODE_SHIFT)
| ((i + ARCH_INTERRUPT_BASE) << IO_APIC_INTERRUPT_VECTOR_SHIFT);
if (i == 0) {
// make redirection entry 0 into an external interrupt
entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT)
| (IO_APIC_PIN_POLARITY_HIGH_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT)
| (IO_APIC_DELIVERY_MODE_EXT_INT << IO_APIC_DELIVERY_MODE_SHIFT);
} else if (i < 16) {
// make 1-15 ISA interrupts
entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT)
| (IO_APIC_PIN_POLARITY_HIGH_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT)
| (IO_APIC_DELIVERY_MODE_FIXED << IO_APIC_DELIVERY_MODE_SHIFT);
} else {
// and the rest are PCI interrupts
entry |= (IO_APIC_TRIGGER_MODE_LEVEL << IO_APIC_TRIGGER_MODE_SHIFT)
| (IO_APIC_PIN_POLARITY_LOW_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT)
| (IO_APIC_DELIVERY_MODE_FIXED << IO_APIC_DELIVERY_MODE_SHIFT);
sLevelTriggeredInterrupts |= (1 << i);
}
ioapic_write_64(IO_APIC_REDIRECTION_TABLE + 2 * i, entry);
}
// configure io apic interrupts from PCI routing table
// configure IO-APIC interrupts from PCI routing table
for (int i = 0; i < table.Count(); i++) {
irq_routing_entry& entry = table.ElementAt(i);
ioapic_configure_io_interrupt(entry.irq,
@@ -394,6 +480,6 @@ ioapic_init(kernel_args* args)
pic_disable();
// prefer the ioapic over the normal pic
dprintf("using ioapic for interrupt routing\n");
dprintf("using io-apics for interrupt routing\n");
arch_int_set_interrupt_controller(ioapicController);
}