* Move the legacy PIC and the IO-APIC code into their own source file. No
functional change intended. * Use an appropriately sized sLevelTriggeredInterrupts for each controller type. This also fixes an out of bound access for IO-APICs with more than 32 entries and also returns the right mode in such cases. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@41426 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -57,4 +57,17 @@ arch_int_are_interrupts_enabled_inline(void)
|
||||
arch_int_are_interrupts_enabled_inline()
|
||||
|
||||
|
||||
typedef struct interrupt_controller_s {
|
||||
const char *name;
|
||||
void (*enable_io_interrupt)(int32 num);
|
||||
void (*disable_io_interrupt)(int32 num);
|
||||
void (*configure_io_interrupt)(int32 num, uint32 config);
|
||||
bool (*is_spurious_interrupt)(int32 num);
|
||||
bool (*is_level_triggered_interrupt)(int32 num);
|
||||
bool (*end_of_interrupt)(int32 num);
|
||||
} interrupt_controller;
|
||||
|
||||
|
||||
void arch_int_set_interrupt_controller(const interrupt_controller &controller);
|
||||
|
||||
#endif /* _KERNEL_ARCH_x86_INT_H */
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2011, Michael Lotz, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef _KERNEL_ARCH_x86_IOAPIC_H
|
||||
#define _KERNEL_ARCH_x86_IOAPIC_H
|
||||
|
||||
struct kernel_args;
|
||||
|
||||
void ioapic_map(kernel_args* args);
|
||||
void ioapic_init(kernel_args* args);
|
||||
|
||||
#endif // _KERNEL_ARCH_x86_IOAPIC_H
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright 2011, Michael Lotz, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef _KERNEL_ARCH_x86_PIC_H
|
||||
#define _KERNEL_ARCH_x86_PIC_H
|
||||
|
||||
void pic_init();
|
||||
void pic_disable();
|
||||
|
||||
#endif // _KERNEL_ARCH_x86_PIC_H
|
||||
@@ -39,8 +39,10 @@ KernelMergeObject kernel_arch_x86.o :
|
||||
apm.cpp
|
||||
bios.cpp
|
||||
cpuid.S
|
||||
ioapic.cpp
|
||||
irq_routing_table.cpp
|
||||
msi.cpp
|
||||
pic.cpp
|
||||
syscall.S
|
||||
vm86.cpp
|
||||
|
||||
|
||||
@@ -31,14 +31,13 @@
|
||||
#include <arch/x86/vm86.h>
|
||||
|
||||
#include "interrupts.h"
|
||||
#include "irq_routing_table.h"
|
||||
|
||||
#include <ACPI.h>
|
||||
#include <AutoDeleter.h>
|
||||
#include <safemode.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// interrupt controllers
|
||||
#include <arch/x86/ioapic.h>
|
||||
#include <arch/x86/pic.h>
|
||||
|
||||
|
||||
//#define TRACE_ARCH_INT
|
||||
#ifdef TRACE_ARCH_INT
|
||||
@@ -48,108 +47,6 @@
|
||||
#endif
|
||||
|
||||
|
||||
// Definitions for the PIC 8259 controller
|
||||
// (this is not a complete list, only what we're actually using)
|
||||
|
||||
#define PIC_MASTER_CONTROL 0x20
|
||||
#define PIC_MASTER_MASK 0x21
|
||||
#define PIC_SLAVE_CONTROL 0xa0
|
||||
#define PIC_SLAVE_MASK 0xa1
|
||||
#define PIC_MASTER_INIT1 PIC_MASTER_CONTROL
|
||||
#define PIC_MASTER_INIT2 PIC_MASTER_MASK
|
||||
#define PIC_MASTER_INIT3 PIC_MASTER_MASK
|
||||
#define PIC_MASTER_INIT4 PIC_MASTER_MASK
|
||||
#define PIC_SLAVE_INIT1 PIC_SLAVE_CONTROL
|
||||
#define PIC_SLAVE_INIT2 PIC_SLAVE_MASK
|
||||
#define PIC_SLAVE_INIT3 PIC_SLAVE_MASK
|
||||
#define PIC_SLAVE_INIT4 PIC_SLAVE_MASK
|
||||
|
||||
// the edge/level trigger control registers
|
||||
#define PIC_MASTER_TRIGGER_MODE 0x4d0
|
||||
#define PIC_SLAVE_TRIGGER_MODE 0x4d1
|
||||
|
||||
#define PIC_INIT1 0x10
|
||||
#define PIC_INIT1_SEND_INIT4 0x01
|
||||
#define PIC_INIT3_IR2_IS_SLAVE 0x04
|
||||
#define PIC_INIT3_SLAVE_ID2 0x02
|
||||
#define PIC_INIT4_x86_MODE 0x01
|
||||
|
||||
#define PIC_CONTROL3 0x08
|
||||
#define PIC_CONTROL3_READ_ISR 0x03
|
||||
#define PIC_CONTROL3_READ_IRR 0x02
|
||||
|
||||
#define PIC_NON_SPECIFIC_EOI 0x20
|
||||
|
||||
#define PIC_SLAVE_INT_BASE 8
|
||||
#define PIC_NUM_INTS 0x0f
|
||||
|
||||
|
||||
// ACPI interrupt models
|
||||
#define ACPI_INTERRUPT_MODEL_PIC 0
|
||||
#define ACPI_INTERRUPT_MODEL_APIC 1
|
||||
#define ACPI_INTERRUPT_MODEL_SAPIC 2
|
||||
|
||||
|
||||
// Definitions for a 82093AA IO APIC controller
|
||||
#define IO_APIC_IDENTIFICATION 0x00
|
||||
#define IO_APIC_VERSION 0x01
|
||||
#define IO_APIC_ARBITRATION 0x02
|
||||
#define IO_APIC_REDIRECTION_TABLE 0x10 // entry = base + 2 * index
|
||||
|
||||
// Fields for the version register
|
||||
#define IO_APIC_VERSION_SHIFT 0
|
||||
#define IO_APIC_VERSION_MASK 0xff
|
||||
#define IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT 16
|
||||
#define IO_APIC_MAX_REDIRECTION_ENTRY_MASK 0xff
|
||||
|
||||
// Fields of each redirection table entry
|
||||
#define IO_APIC_DESTINATION_FIELD_SHIFT 56
|
||||
#define IO_APIC_DESTINATION_FIELD_MASK 0x0f
|
||||
#define IO_APIC_INTERRUPT_MASK_SHIFT 16
|
||||
#define IO_APIC_INTERRUPT_MASKED 1
|
||||
#define IO_APIC_INTERRUPT_UNMASKED 0
|
||||
#define IO_APIC_TRIGGER_MODE_SHIFT 15
|
||||
#define IO_APIC_TRIGGER_MODE_EDGE 0
|
||||
#define IO_APIC_TRIGGER_MODE_LEVEL 1
|
||||
#define IO_APIC_REMOTE_IRR_SHIFT 14
|
||||
#define IO_APIC_PIN_POLARITY_SHIFT 13
|
||||
#define IO_APIC_PIN_POLARITY_HIGH_ACTIVE 0
|
||||
#define IO_APIC_PIN_POLARITY_LOW_ACTIVE 1
|
||||
#define IO_APIC_DELIVERY_STATUS_SHIFT 12
|
||||
#define IO_APIC_DELIVERY_STATUS_IDLE 0
|
||||
#define IO_APIC_DELIVERY_STATUS_PENDING 1
|
||||
#define IO_APIC_DESTINATION_MODE_SHIFT 11
|
||||
#define IO_APIC_DESTINATION_MODE_PHYSICAL 0
|
||||
#define IO_APIC_DESTINATION_MODE_LOGICAL 1
|
||||
#define IO_APIC_DELIVERY_MODE_SHIFT 8
|
||||
#define IO_APIC_DELIVERY_MODE_MASK 0x07
|
||||
#define IO_APIC_DELIVERY_MODE_FIXED 0
|
||||
#define IO_APIC_DELIVERY_MODE_LOWEST_PRIO 1
|
||||
#define IO_APIC_DELIVERY_MODE_SMI 2
|
||||
#define IO_APIC_DELIVERY_MODE_NMI 4
|
||||
#define IO_APIC_DELIVERY_MODE_INIT 5
|
||||
#define IO_APIC_DELIVERY_MODE_EXT_INT 7
|
||||
#define IO_APIC_INTERRUPT_VECTOR_SHIFT 0
|
||||
#define IO_APIC_INTERRUPT_VECTOR_MASK 0xff
|
||||
|
||||
typedef struct ioapic_s {
|
||||
volatile uint32 io_register_select;
|
||||
uint32 reserved[3];
|
||||
volatile uint32 io_window_register;
|
||||
} ioapic;
|
||||
|
||||
static ioapic *sIOAPIC = NULL;
|
||||
static uint32 sIOAPICMaxRedirectionEntry = 23;
|
||||
|
||||
typedef struct interrupt_controller_s {
|
||||
const char *name;
|
||||
void (*enable_io_interrupt)(int32 num);
|
||||
void (*disable_io_interrupt)(int32 num);
|
||||
void (*configure_io_interrupt)(int32 num, uint32 config);
|
||||
bool (*is_spurious_interrupt)(int32 num);
|
||||
bool (*end_of_interrupt)(int32 num);
|
||||
} interrupt_controller;
|
||||
|
||||
static const interrupt_controller *sCurrentPIC = NULL;
|
||||
|
||||
static const char *kInterruptNames[] = {
|
||||
@@ -183,9 +80,6 @@ typedef struct {
|
||||
} desc_table;
|
||||
static desc_table* sIDTs[B_MAX_CPU_COUNT];
|
||||
|
||||
static uint32 sLevelTriggeredInterrupts = 0;
|
||||
// binary mask: 1 level, 0 edge
|
||||
|
||||
// table with functions handling respective interrupts
|
||||
typedef void interrupt_handler_function(struct iframe* frame);
|
||||
#define INTERRUPT_HANDLER_TABLE_SIZE 256
|
||||
@@ -256,476 +150,6 @@ x86_get_idt(int32 cpu)
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - PIC
|
||||
|
||||
|
||||
/*! Tests if the interrupt in-service register of the responsible
|
||||
PIC is set for interrupts 7 and 15, and if that's not the case,
|
||||
it must assume it's a spurious interrupt.
|
||||
*/
|
||||
static bool
|
||||
pic_is_spurious_interrupt(int32 num)
|
||||
{
|
||||
if (num != 7)
|
||||
return false;
|
||||
|
||||
// Note, detecting spurious interrupts on line 15 obviously doesn't
|
||||
// work correctly - and since those are extremely rare, anyway, we
|
||||
// just ignore them
|
||||
|
||||
out8(PIC_CONTROL3 | PIC_CONTROL3_READ_ISR, PIC_MASTER_CONTROL);
|
||||
int32 isr = in8(PIC_MASTER_CONTROL);
|
||||
out8(PIC_CONTROL3 | PIC_CONTROL3_READ_IRR, PIC_MASTER_CONTROL);
|
||||
|
||||
return (isr & 0x80) == 0;
|
||||
}
|
||||
|
||||
|
||||
/*! Sends a non-specified EOI (end of interrupt) notice to the PIC in
|
||||
question (or both of them).
|
||||
This clears the PIC interrupt in-service bit.
|
||||
*/
|
||||
static bool
|
||||
pic_end_of_interrupt(int32 num)
|
||||
{
|
||||
if (num < 0 || num > PIC_NUM_INTS)
|
||||
return false;
|
||||
|
||||
// PIC 8259 controlled interrupt
|
||||
if (num >= PIC_SLAVE_INT_BASE)
|
||||
out8(PIC_NON_SPECIFIC_EOI, PIC_SLAVE_CONTROL);
|
||||
|
||||
// we always need to acknowledge the master PIC
|
||||
out8(PIC_NON_SPECIFIC_EOI, PIC_MASTER_CONTROL);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
pic_enable_io_interrupt(int32 num)
|
||||
{
|
||||
// interrupt is specified "normalized"
|
||||
if (num < 0 || num > PIC_NUM_INTS)
|
||||
return;
|
||||
|
||||
// enable PIC 8259 controlled interrupt
|
||||
|
||||
TRACE(("pic_enable_io_interrupt: irq %ld\n", num));
|
||||
|
||||
if (num < PIC_SLAVE_INT_BASE)
|
||||
out8(in8(PIC_MASTER_MASK) & ~(1 << num), PIC_MASTER_MASK);
|
||||
else
|
||||
out8(in8(PIC_SLAVE_MASK) & ~(1 << (num - PIC_SLAVE_INT_BASE)), PIC_SLAVE_MASK);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
pic_disable_io_interrupt(int32 num)
|
||||
{
|
||||
// interrupt is specified "normalized"
|
||||
// never disable slave pic line IRQ 2
|
||||
if (num < 0 || num > PIC_NUM_INTS || num == 2)
|
||||
return;
|
||||
|
||||
// disable PIC 8259 controlled interrupt
|
||||
|
||||
TRACE(("pic_disable_io_interrupt: irq %ld\n", num));
|
||||
|
||||
if (num < PIC_SLAVE_INT_BASE)
|
||||
out8(in8(PIC_MASTER_MASK) | (1 << num), PIC_MASTER_MASK);
|
||||
else
|
||||
out8(in8(PIC_SLAVE_MASK) | (1 << (num - PIC_SLAVE_INT_BASE)), PIC_SLAVE_MASK);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
pic_configure_io_interrupt(int32 num, uint32 config)
|
||||
{
|
||||
uint8 value;
|
||||
int32 localBit;
|
||||
if (num < 0 || num > PIC_NUM_INTS || num == 2)
|
||||
return;
|
||||
|
||||
TRACE(("pic_configure_io_interrupt: irq %ld; config 0x%08lx\n", num, config));
|
||||
|
||||
if (num < PIC_SLAVE_INT_BASE) {
|
||||
value = in8(PIC_MASTER_TRIGGER_MODE);
|
||||
localBit = num;
|
||||
} else {
|
||||
value = in8(PIC_SLAVE_TRIGGER_MODE);
|
||||
localBit = num - PIC_SLAVE_INT_BASE;
|
||||
}
|
||||
|
||||
if (config & B_LEVEL_TRIGGERED)
|
||||
value |= 1 << localBit;
|
||||
else
|
||||
value &= ~(1 << localBit);
|
||||
|
||||
if (num < PIC_SLAVE_INT_BASE)
|
||||
out8(value, PIC_MASTER_TRIGGER_MODE);
|
||||
else
|
||||
out8(value, PIC_SLAVE_TRIGGER_MODE);
|
||||
|
||||
sLevelTriggeredInterrupts = in8(PIC_MASTER_TRIGGER_MODE)
|
||||
| (in8(PIC_SLAVE_TRIGGER_MODE) << 8);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
pic_init(void)
|
||||
{
|
||||
static interrupt_controller picController = {
|
||||
"8259 PIC",
|
||||
&pic_enable_io_interrupt,
|
||||
&pic_disable_io_interrupt,
|
||||
&pic_configure_io_interrupt,
|
||||
&pic_is_spurious_interrupt,
|
||||
&pic_end_of_interrupt
|
||||
};
|
||||
|
||||
// Start initialization sequence for the master and slave PICs
|
||||
out8(PIC_INIT1 | PIC_INIT1_SEND_INIT4, PIC_MASTER_INIT1);
|
||||
out8(PIC_INIT1 | PIC_INIT1_SEND_INIT4, PIC_SLAVE_INIT1);
|
||||
|
||||
// Set start of interrupts to 0x20 for master, 0x28 for slave
|
||||
out8(ARCH_INTERRUPT_BASE, PIC_MASTER_INIT2);
|
||||
out8(ARCH_INTERRUPT_BASE + PIC_SLAVE_INT_BASE, PIC_SLAVE_INIT2);
|
||||
|
||||
// Specify cascading through interrupt 2
|
||||
out8(PIC_INIT3_IR2_IS_SLAVE, PIC_MASTER_INIT3);
|
||||
out8(PIC_INIT3_SLAVE_ID2, PIC_SLAVE_INIT3);
|
||||
|
||||
// Set both to operate in 8086 mode
|
||||
out8(PIC_INIT4_x86_MODE, PIC_MASTER_INIT4);
|
||||
out8(PIC_INIT4_x86_MODE, PIC_SLAVE_INIT4);
|
||||
|
||||
out8(0xfb, PIC_MASTER_MASK); // Mask off all interrupts (except slave pic line IRQ 2).
|
||||
out8(0xff, PIC_SLAVE_MASK); // Mask off interrupts on the slave.
|
||||
|
||||
// determine which interrupts are level or edge triggered
|
||||
|
||||
#if 0
|
||||
// should set everything possible to level triggered
|
||||
out8(0xf8, PIC_MASTER_TRIGGER_MODE);
|
||||
out8(0xde, PIC_SLAVE_TRIGGER_MODE);
|
||||
#endif
|
||||
|
||||
sLevelTriggeredInterrupts = in8(PIC_MASTER_TRIGGER_MODE)
|
||||
| (in8(PIC_SLAVE_TRIGGER_MODE) << 8);
|
||||
|
||||
TRACE(("PIC level trigger mode: 0x%08lx\n", sLevelTriggeredInterrupts));
|
||||
|
||||
// make the pic controller the current one
|
||||
sCurrentPIC = &picController;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
pic_disable()
|
||||
{
|
||||
// Mask off all interrupts on master and slave
|
||||
out8(0xff, PIC_MASTER_MASK);
|
||||
out8(0xff, PIC_SLAVE_MASK);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - I/O APIC
|
||||
|
||||
|
||||
static inline uint32
|
||||
ioapic_read_32(uint8 registerSelect)
|
||||
{
|
||||
sIOAPIC->io_register_select = registerSelect;
|
||||
return sIOAPIC->io_window_register;
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
ioapic_write_32(uint8 registerSelect, uint32 value)
|
||||
{
|
||||
sIOAPIC->io_register_select = registerSelect;
|
||||
sIOAPIC->io_window_register = value;
|
||||
}
|
||||
|
||||
|
||||
static inline uint64
|
||||
ioapic_read_64(uint8 registerSelect)
|
||||
{
|
||||
sIOAPIC->io_register_select = registerSelect + 1;
|
||||
uint64 result = sIOAPIC->io_window_register;
|
||||
result <<= 32;
|
||||
sIOAPIC->io_register_select = registerSelect;
|
||||
result |= sIOAPIC->io_window_register;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
ioapic_write_64(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);
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
ioapic_is_spurious_interrupt(int32 num)
|
||||
{
|
||||
// the spurious interrupt vector is initialized to the max value in smp
|
||||
return num == 0xff - ARCH_INTERRUPT_BASE;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
ioapic_end_of_interrupt(int32 num)
|
||||
{
|
||||
apic_end_of_interrupt();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ioapic_enable_io_interrupt(int32 pin)
|
||||
{
|
||||
if (pin < 0 || pin > (int32)sIOAPICMaxRedirectionEntry)
|
||||
return;
|
||||
|
||||
TRACE(("ioapic_enable_io_interrupt: pin %ld\n", pin));
|
||||
|
||||
uint64 entry = ioapic_read_64(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);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ioapic_disable_io_interrupt(int32 pin)
|
||||
{
|
||||
if (pin < 0 || pin > (int32)sIOAPICMaxRedirectionEntry)
|
||||
return;
|
||||
|
||||
TRACE(("ioapic_disable_io_interrupt: pin %ld\n", pin));
|
||||
|
||||
uint64 entry = ioapic_read_64(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);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ioapic_configure_io_interrupt(int32 pin, uint32 config)
|
||||
{
|
||||
if (pin < 0 || pin > (int32)sIOAPICMaxRedirectionEntry)
|
||||
return;
|
||||
|
||||
TRACE(("ioapic_configure_io_interrupt: pin %ld; config 0x%08lx\n", pin,
|
||||
config));
|
||||
|
||||
uint64 entry = ioapic_read_64(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);
|
||||
} else {
|
||||
entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT);
|
||||
sLevelTriggeredInterrupts &= ~(1 << pin);
|
||||
}
|
||||
|
||||
if (config & B_LOW_ACTIVE_POLARITY)
|
||||
entry |= (IO_APIC_PIN_POLARITY_LOW_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT);
|
||||
else
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ioapic_map(kernel_args* args)
|
||||
{
|
||||
if (args->arch_args.apic == NULL) {
|
||||
dprintf("no local apic available\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (args->arch_args.ioapic == NULL) {
|
||||
dprintf("no ioapic available, not using ioapics for interrupt routing\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// map in the ioapic
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
acpi_set_interrupt_model(acpi_module_info* acpiModule, uint32 interruptModel)
|
||||
{
|
||||
acpi_object_type model;
|
||||
model.object_type = ACPI_TYPE_INTEGER;
|
||||
model.data.integer = interruptModel;
|
||||
|
||||
acpi_objects parameter;
|
||||
parameter.count = 1;
|
||||
parameter.pointer = &model;
|
||||
|
||||
dprintf("setting ACPI interrupt model to %s\n",
|
||||
interruptModel == 0 ? "PIC"
|
||||
: (interruptModel == 1 ? "APIC"
|
||||
: (interruptModel == 2 ? "SAPIC"
|
||||
: "unknown")));
|
||||
|
||||
return acpiModule->evaluate_method(NULL, "\\_PIC", ¶meter, NULL);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ioapic_init(kernel_args* args)
|
||||
{
|
||||
static const interrupt_controller ioapicController = {
|
||||
"82093AA IOAPIC",
|
||||
&ioapic_enable_io_interrupt,
|
||||
&ioapic_disable_io_interrupt,
|
||||
&ioapic_configure_io_interrupt,
|
||||
&ioapic_is_spurious_interrupt,
|
||||
&ioapic_end_of_interrupt
|
||||
};
|
||||
|
||||
if (sIOAPIC == NULL)
|
||||
return;
|
||||
|
||||
#if 0
|
||||
if (get_safemode_boolean(B_SAFEMODE_DISABLE_IOAPIC, false)) {
|
||||
dprintf("ioapic explicitly disabled, not using ioapics 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 "
|
||||
"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");
|
||||
return;
|
||||
}
|
||||
BPrivate::CObjectDeleter<const char, status_t>
|
||||
acpiModulePutter(B_ACPI_MODULE_NAME, put_module);
|
||||
|
||||
// switch to the APIC interrupt model before retrieving the irq routing
|
||||
// table as it will return different settings depending on the model
|
||||
status = acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_APIC);
|
||||
if (status != B_OK) {
|
||||
dprintf("failed to put ACPI into APIC interrupt model, ignoring\n");
|
||||
// don't abort, as the _PIC method is optional and as long as there
|
||||
// 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));
|
||||
|
||||
IRQRoutingTable table;
|
||||
status = prepare_irq_routing(acpiModule, table,
|
||||
sIOAPICMaxRedirectionEntry + 1);
|
||||
if (status != B_OK) {
|
||||
dprintf("IRQ routing preparation failed, not configuring ioapic.\n");
|
||||
acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC);
|
||||
// revert to PIC interrupt model just in case
|
||||
return;
|
||||
}
|
||||
|
||||
print_irq_routing_table(table);
|
||||
|
||||
status = enable_irq_routing(acpiModule, table);
|
||||
if (status != B_OK) {
|
||||
panic("failed to enable IRQ routing");
|
||||
// if it failed early on it might still work in PIC mode
|
||||
acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC);
|
||||
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
|
||||
for (int i = 0; i < table.Count(); i++) {
|
||||
irq_routing_entry& entry = table.ElementAt(i);
|
||||
ioapic_configure_io_interrupt(entry.irq,
|
||||
entry.polarity | entry.trigger_mode);
|
||||
}
|
||||
|
||||
// disable the legacy PIC
|
||||
pic_disable();
|
||||
|
||||
// prefer the ioapic over the normal pic
|
||||
dprintf("using ioapic for interrupt routing\n");
|
||||
sCurrentPIC = &ioapicController;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
@@ -1057,8 +481,7 @@ hardware_interrupt(struct iframe* frame)
|
||||
return;
|
||||
}
|
||||
|
||||
if (vector < 32)
|
||||
levelTriggered = (sLevelTriggeredInterrupts & (1 << vector)) != 0;
|
||||
levelTriggered = sCurrentPIC->is_level_triggered_interrupt(vector);
|
||||
|
||||
if (!levelTriggered) {
|
||||
// if it's not handled by the current pic then it's an apic generated
|
||||
@@ -1452,3 +875,10 @@ arch_int_init_post_device_manager(struct kernel_args *args)
|
||||
{
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
arch_int_set_interrupt_controller(const interrupt_controller &controller)
|
||||
{
|
||||
sCurrentPIC = &controller;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,399 @@
|
||||
/*
|
||||
* Copyright 2008-2011, Michael Lotz, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include <arch/x86/ioapic.h>
|
||||
|
||||
#include <int.h>
|
||||
#include <vm/vm.h>
|
||||
|
||||
#include "irq_routing_table.h"
|
||||
|
||||
#include <ACPI.h>
|
||||
#include <AutoDeleter.h>
|
||||
#include <safemode.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <arch/x86/apic.h>
|
||||
#include <arch/x86/arch_int.h>
|
||||
#include <arch/x86/pic.h>
|
||||
|
||||
|
||||
//#define TRACE_IOAPIC
|
||||
#ifdef TRACE_IOAPIC
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
|
||||
// ACPI interrupt models
|
||||
#define ACPI_INTERRUPT_MODEL_PIC 0
|
||||
#define ACPI_INTERRUPT_MODEL_APIC 1
|
||||
#define ACPI_INTERRUPT_MODEL_SAPIC 2
|
||||
|
||||
|
||||
// Definitions for a 82093AA IO APIC controller
|
||||
#define IO_APIC_IDENTIFICATION 0x00
|
||||
#define IO_APIC_VERSION 0x01
|
||||
#define IO_APIC_ARBITRATION 0x02
|
||||
#define IO_APIC_REDIRECTION_TABLE 0x10 // entry = base + 2 * index
|
||||
|
||||
// Fields for the version register
|
||||
#define IO_APIC_VERSION_SHIFT 0
|
||||
#define IO_APIC_VERSION_MASK 0xff
|
||||
#define IO_APIC_MAX_REDIRECTION_ENTRY_SHIFT 16
|
||||
#define IO_APIC_MAX_REDIRECTION_ENTRY_MASK 0xff
|
||||
|
||||
// Fields of each redirection table entry
|
||||
#define IO_APIC_DESTINATION_FIELD_SHIFT 56
|
||||
#define IO_APIC_DESTINATION_FIELD_MASK 0x0f
|
||||
#define IO_APIC_INTERRUPT_MASK_SHIFT 16
|
||||
#define IO_APIC_INTERRUPT_MASKED 1
|
||||
#define IO_APIC_INTERRUPT_UNMASKED 0
|
||||
#define IO_APIC_TRIGGER_MODE_SHIFT 15
|
||||
#define IO_APIC_TRIGGER_MODE_EDGE 0
|
||||
#define IO_APIC_TRIGGER_MODE_LEVEL 1
|
||||
#define IO_APIC_REMOTE_IRR_SHIFT 14
|
||||
#define IO_APIC_PIN_POLARITY_SHIFT 13
|
||||
#define IO_APIC_PIN_POLARITY_HIGH_ACTIVE 0
|
||||
#define IO_APIC_PIN_POLARITY_LOW_ACTIVE 1
|
||||
#define IO_APIC_DELIVERY_STATUS_SHIFT 12
|
||||
#define IO_APIC_DELIVERY_STATUS_IDLE 0
|
||||
#define IO_APIC_DELIVERY_STATUS_PENDING 1
|
||||
#define IO_APIC_DESTINATION_MODE_SHIFT 11
|
||||
#define IO_APIC_DESTINATION_MODE_PHYSICAL 0
|
||||
#define IO_APIC_DESTINATION_MODE_LOGICAL 1
|
||||
#define IO_APIC_DELIVERY_MODE_SHIFT 8
|
||||
#define IO_APIC_DELIVERY_MODE_MASK 0x07
|
||||
#define IO_APIC_DELIVERY_MODE_FIXED 0
|
||||
#define IO_APIC_DELIVERY_MODE_LOWEST_PRIO 1
|
||||
#define IO_APIC_DELIVERY_MODE_SMI 2
|
||||
#define IO_APIC_DELIVERY_MODE_NMI 4
|
||||
#define IO_APIC_DELIVERY_MODE_INIT 5
|
||||
#define IO_APIC_DELIVERY_MODE_EXT_INT 7
|
||||
#define IO_APIC_INTERRUPT_VECTOR_SHIFT 0
|
||||
#define IO_APIC_INTERRUPT_VECTOR_MASK 0xff
|
||||
|
||||
|
||||
typedef struct ioapic_s {
|
||||
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
|
||||
|
||||
|
||||
// #pragma mark - I/O APIC
|
||||
|
||||
|
||||
static inline uint32
|
||||
ioapic_read_32(uint8 registerSelect)
|
||||
{
|
||||
sIOAPIC->io_register_select = registerSelect;
|
||||
return sIOAPIC->io_window_register;
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
ioapic_write_32(uint8 registerSelect, uint32 value)
|
||||
{
|
||||
sIOAPIC->io_register_select = registerSelect;
|
||||
sIOAPIC->io_window_register = value;
|
||||
}
|
||||
|
||||
|
||||
static inline uint64
|
||||
ioapic_read_64(uint8 registerSelect)
|
||||
{
|
||||
sIOAPIC->io_register_select = registerSelect + 1;
|
||||
uint64 result = sIOAPIC->io_window_register;
|
||||
result <<= 32;
|
||||
sIOAPIC->io_register_select = registerSelect;
|
||||
result |= sIOAPIC->io_window_register;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
ioapic_write_64(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);
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
ioapic_is_spurious_interrupt(int32 num)
|
||||
{
|
||||
// the spurious interrupt vector is initialized to the max value in smp
|
||||
return num == 0xff - ARCH_INTERRUPT_BASE;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
ioapic_is_level_triggered_interrupt(int32 pin)
|
||||
{
|
||||
if (pin < 0 || pin > (int32)sIOAPICMaxRedirectionEntry)
|
||||
return false;
|
||||
|
||||
return (sLevelTriggeredInterrupts & (1 << pin)) != 0;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
ioapic_end_of_interrupt(int32 num)
|
||||
{
|
||||
apic_end_of_interrupt();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ioapic_enable_io_interrupt(int32 pin)
|
||||
{
|
||||
if (pin < 0 || pin > (int32)sIOAPICMaxRedirectionEntry)
|
||||
return;
|
||||
|
||||
TRACE(("ioapic_enable_io_interrupt: pin %ld\n", pin));
|
||||
|
||||
uint64 entry = ioapic_read_64(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);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ioapic_disable_io_interrupt(int32 pin)
|
||||
{
|
||||
if (pin < 0 || pin > (int32)sIOAPICMaxRedirectionEntry)
|
||||
return;
|
||||
|
||||
TRACE(("ioapic_disable_io_interrupt: pin %ld\n", pin));
|
||||
|
||||
uint64 entry = ioapic_read_64(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);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ioapic_configure_io_interrupt(int32 pin, uint32 config)
|
||||
{
|
||||
if (pin < 0 || pin > (int32)sIOAPICMaxRedirectionEntry)
|
||||
return;
|
||||
|
||||
TRACE(("ioapic_configure_io_interrupt: pin %ld; config 0x%08lx\n", pin,
|
||||
config));
|
||||
|
||||
uint64 entry = ioapic_read_64(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);
|
||||
} else {
|
||||
entry |= (IO_APIC_TRIGGER_MODE_EDGE << IO_APIC_TRIGGER_MODE_SHIFT);
|
||||
sLevelTriggeredInterrupts &= ~(1 << pin);
|
||||
}
|
||||
|
||||
if (config & B_LOW_ACTIVE_POLARITY)
|
||||
entry |= (IO_APIC_PIN_POLARITY_LOW_ACTIVE << IO_APIC_PIN_POLARITY_SHIFT);
|
||||
else
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
acpi_set_interrupt_model(acpi_module_info* acpiModule, uint32 interruptModel)
|
||||
{
|
||||
acpi_object_type model;
|
||||
model.object_type = ACPI_TYPE_INTEGER;
|
||||
model.data.integer = interruptModel;
|
||||
|
||||
acpi_objects parameter;
|
||||
parameter.count = 1;
|
||||
parameter.pointer = &model;
|
||||
|
||||
dprintf("setting ACPI interrupt model to %s\n",
|
||||
interruptModel == 0 ? "PIC"
|
||||
: (interruptModel == 1 ? "APIC"
|
||||
: (interruptModel == 2 ? "SAPIC"
|
||||
: "unknown")));
|
||||
|
||||
return acpiModule->evaluate_method(NULL, "\\_PIC", ¶meter, NULL);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ioapic_map(kernel_args* args)
|
||||
{
|
||||
if (args->arch_args.apic == NULL) {
|
||||
dprintf("no local apic available\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (args->arch_args.ioapic == NULL) {
|
||||
dprintf("no ioapic available, not using ioapics 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ioapic_init(kernel_args* args)
|
||||
{
|
||||
static const interrupt_controller ioapicController = {
|
||||
"82093AA IOAPIC",
|
||||
&ioapic_enable_io_interrupt,
|
||||
&ioapic_disable_io_interrupt,
|
||||
&ioapic_configure_io_interrupt,
|
||||
&ioapic_is_spurious_interrupt,
|
||||
&ioapic_is_level_triggered_interrupt,
|
||||
&ioapic_end_of_interrupt
|
||||
};
|
||||
|
||||
if (sIOAPIC == NULL)
|
||||
return;
|
||||
|
||||
#if 0
|
||||
if (get_safemode_boolean(B_SAFEMODE_DISABLE_IOAPIC, false)) {
|
||||
dprintf("ioapic explicitly disabled, not using ioapics 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 "
|
||||
"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");
|
||||
return;
|
||||
}
|
||||
BPrivate::CObjectDeleter<const char, status_t>
|
||||
acpiModulePutter(B_ACPI_MODULE_NAME, put_module);
|
||||
|
||||
// switch to the APIC interrupt model before retrieving the irq routing
|
||||
// table as it will return different settings depending on the model
|
||||
status = acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_APIC);
|
||||
if (status != B_OK) {
|
||||
dprintf("failed to put ACPI into APIC interrupt model, ignoring\n");
|
||||
// don't abort, as the _PIC method is optional and as long as there
|
||||
// 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));
|
||||
|
||||
IRQRoutingTable table;
|
||||
status = prepare_irq_routing(acpiModule, table,
|
||||
sIOAPICMaxRedirectionEntry + 1);
|
||||
if (status != B_OK) {
|
||||
dprintf("IRQ routing preparation failed, not configuring ioapic.\n");
|
||||
acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC);
|
||||
// revert to PIC interrupt model just in case
|
||||
return;
|
||||
}
|
||||
|
||||
print_irq_routing_table(table);
|
||||
|
||||
status = enable_irq_routing(acpiModule, table);
|
||||
if (status != B_OK) {
|
||||
panic("failed to enable IRQ routing");
|
||||
// if it failed early on it might still work in PIC mode
|
||||
acpi_set_interrupt_model(acpiModule, ACPI_INTERRUPT_MODEL_PIC);
|
||||
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
|
||||
for (int i = 0; i < table.Count(); i++) {
|
||||
irq_routing_entry& entry = table.ElementAt(i);
|
||||
ioapic_configure_io_interrupt(entry.irq,
|
||||
entry.polarity | entry.trigger_mode);
|
||||
}
|
||||
|
||||
// disable the legacy PIC
|
||||
pic_disable();
|
||||
|
||||
// prefer the ioapic over the normal pic
|
||||
dprintf("using ioapic for interrupt routing\n");
|
||||
arch_int_set_interrupt_controller(ioapicController);
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* Copyright 2002-2005, Axel Dörfler, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Copyright 2001, Travis Geiselbrecht. All rights reserved.
|
||||
* Distributed under the terms of the NewOS License.
|
||||
*/
|
||||
|
||||
#include <arch/x86/pic.h>
|
||||
|
||||
#include <arch/cpu.h>
|
||||
#include <arch/int.h>
|
||||
|
||||
#include <arch/x86/arch_int.h>
|
||||
|
||||
|
||||
//#define TRACE_PIC
|
||||
#ifdef TRACE_PIC
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
|
||||
// Definitions for the PIC 8259 controller
|
||||
// (this is not a complete list, only what we're actually using)
|
||||
|
||||
#define PIC_MASTER_CONTROL 0x20
|
||||
#define PIC_MASTER_MASK 0x21
|
||||
#define PIC_SLAVE_CONTROL 0xa0
|
||||
#define PIC_SLAVE_MASK 0xa1
|
||||
#define PIC_MASTER_INIT1 PIC_MASTER_CONTROL
|
||||
#define PIC_MASTER_INIT2 PIC_MASTER_MASK
|
||||
#define PIC_MASTER_INIT3 PIC_MASTER_MASK
|
||||
#define PIC_MASTER_INIT4 PIC_MASTER_MASK
|
||||
#define PIC_SLAVE_INIT1 PIC_SLAVE_CONTROL
|
||||
#define PIC_SLAVE_INIT2 PIC_SLAVE_MASK
|
||||
#define PIC_SLAVE_INIT3 PIC_SLAVE_MASK
|
||||
#define PIC_SLAVE_INIT4 PIC_SLAVE_MASK
|
||||
|
||||
// the edge/level trigger control registers
|
||||
#define PIC_MASTER_TRIGGER_MODE 0x4d0
|
||||
#define PIC_SLAVE_TRIGGER_MODE 0x4d1
|
||||
|
||||
#define PIC_INIT1 0x10
|
||||
#define PIC_INIT1_SEND_INIT4 0x01
|
||||
#define PIC_INIT3_IR2_IS_SLAVE 0x04
|
||||
#define PIC_INIT3_SLAVE_ID2 0x02
|
||||
#define PIC_INIT4_x86_MODE 0x01
|
||||
|
||||
#define PIC_CONTROL3 0x08
|
||||
#define PIC_CONTROL3_READ_ISR 0x03
|
||||
#define PIC_CONTROL3_READ_IRR 0x02
|
||||
|
||||
#define PIC_NON_SPECIFIC_EOI 0x20
|
||||
|
||||
#define PIC_SLAVE_INT_BASE 8
|
||||
#define PIC_NUM_INTS 0x0f
|
||||
|
||||
|
||||
static uint16 sLevelTriggeredInterrupts = 0;
|
||||
// binary mask: 1 level, 0 edge
|
||||
|
||||
|
||||
/*! Tests if the interrupt in-service register of the responsible
|
||||
PIC is set for interrupts 7 and 15, and if that's not the case,
|
||||
it must assume it's a spurious interrupt.
|
||||
*/
|
||||
static bool
|
||||
pic_is_spurious_interrupt(int32 num)
|
||||
{
|
||||
if (num != 7)
|
||||
return false;
|
||||
|
||||
// Note, detecting spurious interrupts on line 15 obviously doesn't
|
||||
// work correctly - and since those are extremely rare, anyway, we
|
||||
// just ignore them
|
||||
|
||||
out8(PIC_CONTROL3 | PIC_CONTROL3_READ_ISR, PIC_MASTER_CONTROL);
|
||||
int32 isr = in8(PIC_MASTER_CONTROL);
|
||||
out8(PIC_CONTROL3 | PIC_CONTROL3_READ_IRR, PIC_MASTER_CONTROL);
|
||||
|
||||
return (isr & 0x80) == 0;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
pic_is_level_triggered_interrupt(int32 num)
|
||||
{
|
||||
if (num < 0 || num > PIC_NUM_INTS)
|
||||
return false;
|
||||
|
||||
return (sLevelTriggeredInterrupts & (1 << num)) != 0;
|
||||
}
|
||||
|
||||
|
||||
/*! Sends a non-specified EOI (end of interrupt) notice to the PIC in
|
||||
question (or both of them).
|
||||
This clears the PIC interrupt in-service bit.
|
||||
*/
|
||||
static bool
|
||||
pic_end_of_interrupt(int32 num)
|
||||
{
|
||||
if (num < 0 || num > PIC_NUM_INTS)
|
||||
return false;
|
||||
|
||||
// PIC 8259 controlled interrupt
|
||||
if (num >= PIC_SLAVE_INT_BASE)
|
||||
out8(PIC_NON_SPECIFIC_EOI, PIC_SLAVE_CONTROL);
|
||||
|
||||
// we always need to acknowledge the master PIC
|
||||
out8(PIC_NON_SPECIFIC_EOI, PIC_MASTER_CONTROL);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
pic_enable_io_interrupt(int32 num)
|
||||
{
|
||||
// interrupt is specified "normalized"
|
||||
if (num < 0 || num > PIC_NUM_INTS)
|
||||
return;
|
||||
|
||||
// enable PIC 8259 controlled interrupt
|
||||
|
||||
TRACE(("pic_enable_io_interrupt: irq %ld\n", num));
|
||||
|
||||
if (num < PIC_SLAVE_INT_BASE)
|
||||
out8(in8(PIC_MASTER_MASK) & ~(1 << num), PIC_MASTER_MASK);
|
||||
else
|
||||
out8(in8(PIC_SLAVE_MASK) & ~(1 << (num - PIC_SLAVE_INT_BASE)), PIC_SLAVE_MASK);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
pic_disable_io_interrupt(int32 num)
|
||||
{
|
||||
// interrupt is specified "normalized"
|
||||
// never disable slave pic line IRQ 2
|
||||
if (num < 0 || num > PIC_NUM_INTS || num == 2)
|
||||
return;
|
||||
|
||||
// disable PIC 8259 controlled interrupt
|
||||
|
||||
TRACE(("pic_disable_io_interrupt: irq %ld\n", num));
|
||||
|
||||
if (num < PIC_SLAVE_INT_BASE)
|
||||
out8(in8(PIC_MASTER_MASK) | (1 << num), PIC_MASTER_MASK);
|
||||
else
|
||||
out8(in8(PIC_SLAVE_MASK) | (1 << (num - PIC_SLAVE_INT_BASE)), PIC_SLAVE_MASK);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
pic_configure_io_interrupt(int32 num, uint32 config)
|
||||
{
|
||||
uint8 value;
|
||||
int32 localBit;
|
||||
if (num < 0 || num > PIC_NUM_INTS || num == 2)
|
||||
return;
|
||||
|
||||
TRACE(("pic_configure_io_interrupt: irq %ld; config 0x%08lx\n", num, config));
|
||||
|
||||
if (num < PIC_SLAVE_INT_BASE) {
|
||||
value = in8(PIC_MASTER_TRIGGER_MODE);
|
||||
localBit = num;
|
||||
} else {
|
||||
value = in8(PIC_SLAVE_TRIGGER_MODE);
|
||||
localBit = num - PIC_SLAVE_INT_BASE;
|
||||
}
|
||||
|
||||
if (config & B_LEVEL_TRIGGERED)
|
||||
value |= 1 << localBit;
|
||||
else
|
||||
value &= ~(1 << localBit);
|
||||
|
||||
if (num < PIC_SLAVE_INT_BASE)
|
||||
out8(value, PIC_MASTER_TRIGGER_MODE);
|
||||
else
|
||||
out8(value, PIC_SLAVE_TRIGGER_MODE);
|
||||
|
||||
sLevelTriggeredInterrupts = in8(PIC_MASTER_TRIGGER_MODE)
|
||||
| (in8(PIC_SLAVE_TRIGGER_MODE) << 8);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
pic_init()
|
||||
{
|
||||
static const interrupt_controller picController = {
|
||||
"8259 PIC",
|
||||
&pic_enable_io_interrupt,
|
||||
&pic_disable_io_interrupt,
|
||||
&pic_configure_io_interrupt,
|
||||
&pic_is_spurious_interrupt,
|
||||
&pic_is_level_triggered_interrupt,
|
||||
&pic_end_of_interrupt
|
||||
};
|
||||
|
||||
// Start initialization sequence for the master and slave PICs
|
||||
out8(PIC_INIT1 | PIC_INIT1_SEND_INIT4, PIC_MASTER_INIT1);
|
||||
out8(PIC_INIT1 | PIC_INIT1_SEND_INIT4, PIC_SLAVE_INIT1);
|
||||
|
||||
// Set start of interrupts to 0x20 for master, 0x28 for slave
|
||||
out8(ARCH_INTERRUPT_BASE, PIC_MASTER_INIT2);
|
||||
out8(ARCH_INTERRUPT_BASE + PIC_SLAVE_INT_BASE, PIC_SLAVE_INIT2);
|
||||
|
||||
// Specify cascading through interrupt 2
|
||||
out8(PIC_INIT3_IR2_IS_SLAVE, PIC_MASTER_INIT3);
|
||||
out8(PIC_INIT3_SLAVE_ID2, PIC_SLAVE_INIT3);
|
||||
|
||||
// Set both to operate in 8086 mode
|
||||
out8(PIC_INIT4_x86_MODE, PIC_MASTER_INIT4);
|
||||
out8(PIC_INIT4_x86_MODE, PIC_SLAVE_INIT4);
|
||||
|
||||
out8(0xfb, PIC_MASTER_MASK); // Mask off all interrupts (except slave pic line IRQ 2).
|
||||
out8(0xff, PIC_SLAVE_MASK); // Mask off interrupts on the slave.
|
||||
|
||||
// determine which interrupts are level or edge triggered
|
||||
|
||||
#if 0
|
||||
// should set everything possible to level triggered
|
||||
out8(0xf8, PIC_MASTER_TRIGGER_MODE);
|
||||
out8(0xde, PIC_SLAVE_TRIGGER_MODE);
|
||||
#endif
|
||||
|
||||
sLevelTriggeredInterrupts = in8(PIC_MASTER_TRIGGER_MODE)
|
||||
| (in8(PIC_SLAVE_TRIGGER_MODE) << 8);
|
||||
|
||||
TRACE(("PIC level trigger mode: 0x%08lx\n", sLevelTriggeredInterrupts));
|
||||
|
||||
// make the pic controller the current one
|
||||
arch_int_set_interrupt_controller(picController);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
pic_disable()
|
||||
{
|
||||
// Mask off all interrupts on master and slave
|
||||
out8(0xff, PIC_MASTER_MASK);
|
||||
out8(0xff, PIC_SLAVE_MASK);
|
||||
}
|
||||
Reference in New Issue
Block a user