Files
lk/platform/pc/interrupts.c

245 lines
6.1 KiB
C
Raw Normal View History

2009-03-26 02:32:01 -04:00
/*
* Copyright (c) 2009 Corey Tabaka
* Copyright (c) 2015 Intel Corporation
2009-03-26 02:32:01 -04:00
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
2009-03-26 02:32:01 -04:00
*/
#include <sys/types.h>
#include <lk/debug.h>
#include <lk/err.h>
#include <lk/reg.h>
#include <lk/trace.h>
#include <assert.h>
2009-03-26 02:32:01 -04:00
#include <kernel/thread.h>
#include <platform/interrupts.h>
#include <arch/ops.h>
#include <arch/x86.h>
#include <arch/x86/apic.h>
#include <kernel/spinlock.h>
2009-03-26 02:32:01 -04:00
#include "platform_p.h"
#include <platform/pc.h>
2009-03-26 02:32:01 -04:00
#if WITH_LIB_ACPI_LITE
#include <lib/acpi_lite.h>
#endif
#define LOCAL_TRACE 0
// TODO: handle ioapics
static spin_lock_t lock;
2009-03-26 02:32:01 -04:00
#define INTC_TYPE_INTERNAL 0
#define INTC_TYPE_PIC 1
#define INTC_TYPE_MSI 2
2009-03-26 02:32:01 -04:00
struct int_vector {
int_handler handler;
void *arg;
struct {
uint allocated : 1;
uint type : 2; // INTC_TYPE
uint edge : 1; // edge vs level
} flags;
2009-03-26 02:32:01 -04:00
};
static struct int_vector int_table[INT_VECTORS];
void platform_init_interrupts(void) {
pic_init();
#if WITH_SMP
lapic_init();
#endif
2009-03-26 02:32:01 -04:00
// initialize all of the vectors
for (int i = 0; i < INT_VECTORS; i++) {
if (i >= INT_PIC1_BASE && i <= INT_PIC2_BASE + 8) {
int_table[i].flags.type = INTC_TYPE_PIC;
}
if (i >= INT_DYNAMIC_START && i <= INT_DYNAMIC_END) {
int_table[i].flags.allocated = false;
} else {
int_table[i].flags.allocated = true;
}
}
2009-03-26 02:32:01 -04:00
}
status_t mask_interrupt(unsigned int vector) {
if (vector >= INT_VECTORS)
return ERR_INVALID_ARGS;
2009-03-26 02:32:01 -04:00
LTRACEF("vector %#x\n", vector);
2009-03-26 02:32:01 -04:00
spin_lock_saved_state_t state;
spin_lock_irqsave(&lock, state);
2009-03-26 02:32:01 -04:00
if (int_table[vector].flags.type == INTC_TYPE_PIC) {
pic_enable(vector, false);
}
2009-03-26 02:32:01 -04:00
spin_unlock_irqrestore(&lock, state);
2009-03-26 02:32:01 -04:00
return NO_ERROR;
2009-03-26 02:32:01 -04:00
}
status_t unmask_interrupt(unsigned int vector) {
if (vector >= INT_VECTORS)
return ERR_INVALID_ARGS;
2009-03-26 02:32:01 -04:00
LTRACEF("vector %#x\n", vector);
2009-03-26 02:32:01 -04:00
spin_lock_saved_state_t state;
spin_lock_irqsave(&lock, state);
2009-03-26 02:32:01 -04:00
if (int_table[vector].flags.type == INTC_TYPE_PIC) {
pic_enable(vector, true);
}
2009-03-26 02:32:01 -04:00
spin_unlock_irqrestore(&lock, state);
2009-03-26 02:32:01 -04:00
return NO_ERROR;
2009-03-26 02:32:01 -04:00
}
enum handler_return platform_irq(x86_iframe_t *frame);
enum handler_return platform_irq(x86_iframe_t *frame) {
// get the current vector
unsigned int vector = frame->vector;
2009-03-26 02:32:01 -04:00
DEBUG_ASSERT(vector >= 0x20);
struct int_vector *handler = &int_table[vector];
// edge triggered interrupts are acked beforehand
if (handler->flags.edge) {
if (handler->flags.type == INTC_TYPE_MSI) {
lapic_eoi(vector);
} else {
pic_eoi(vector);
}
}
2009-03-26 02:32:01 -04:00
// call the registered interrupt handler
enum handler_return ret = INT_NO_RESCHEDULE;
if (handler->handler) {
ret = handler->handler(handler->arg);
}
// level triggered ack
if (!handler->flags.edge) {
if (handler->flags.type == INTC_TYPE_MSI) {
lapic_eoi(vector);
} else {
pic_eoi(vector);
}
}
2009-03-26 02:32:01 -04:00
return ret;
2009-03-26 02:32:01 -04:00
}
static void register_int_handler_etc(unsigned int vector, int_handler handler, void *arg, bool edge, uint type) {
ASSERT(vector < INT_VECTORS);
spin_lock_saved_state_t state;
spin_lock_irqsave(&lock, state);
int_table[vector].arg = arg;
int_table[vector].handler = handler;
int_table[vector].flags.allocated = true;
int_table[vector].flags.edge = edge;
int_table[vector].flags.type = type;
spin_unlock_irqrestore(&lock, state);
}
void register_int_handler(unsigned int vector, int_handler handler, void *arg) {
register_int_handler_etc(vector, handler, arg, false, INTC_TYPE_PIC);
}
void register_int_handler_msi(unsigned int vector, int_handler handler, void *arg, bool edge) {
register_int_handler_etc(vector, handler, arg, edge, INTC_TYPE_MSI);
}
void platform_mask_irqs(void) {
pic_mask_interrupts();
}
status_t platform_pci_int_to_vector(unsigned int pci_int, unsigned int *vector) {
LTRACEF("pci_int %u\n", pci_int);
// pci interrupts are relative to PIC style irq #s so simply add INT_BASE to it
uint out_vector = pci_int + INT_BASE;
if (out_vector > INT_VECTORS) {
return ERR_INVALID_ARGS;
}
*vector = out_vector;
return NO_ERROR;
}
status_t platform_allocate_interrupts(size_t count, uint align_log2, bool msi, unsigned int *vector) {
LTRACEF("count %zu align %u msi %d\n", count, align_log2, msi);
if (align_log2 > 0) {
PANIC_UNIMPLEMENTED;
}
2009-03-26 02:32:01 -04:00
spin_lock_saved_state_t state;
spin_lock_irqsave(&lock, state);
2009-03-26 02:32:01 -04:00
// find a free interrupt
status_t err = ERR_NOT_FOUND;
for (unsigned int i = 0; i < INT_VECTORS; i++) {
if (!int_table[i].flags.allocated) {
int_table[i].flags.allocated = true;
*vector = i;
LTRACEF("found irq %#x\n", i);
err = NO_ERROR;
break;
}
}
2009-03-26 02:32:01 -04:00
spin_unlock_irqrestore(&lock, state);
return err;
2009-03-26 02:32:01 -04:00
}
status_t platform_compute_msi_values(unsigned int vector, unsigned int cpu, bool edge,
uint64_t *msi_address_out, uint16_t *msi_data_out) {
// only handle edge triggered at the moment
DEBUG_ASSERT(edge);
*msi_data_out = (vector & 0xff) | (0<<15); // edge triggered
*msi_address_out = 0xfee00000 | (cpu << 12);
return NO_ERROR;
}
// Try to detect the ioapic(s) from ACPI and initialize them
#if WITH_LIB_ACPI_LITE
static void io_apic_callback(const void *_entry, size_t entry_len, void *cookie) {
const struct acpi_madt_io_apic_entry *entry = _entry;
static int index = 0;
ioapic_init(index++, entry->io_apic_address, entry->io_apic_id, entry->global_system_interrupt_base);
}
#endif
void platform_init_interrupts_postvm(void) {
#if WITH_SMP
// Bring up the local apic on the first cpu
// Doesn't need ACPI to detect its presence
lapic_init_postvm();
#endif
#if WITH_LIB_ACPI_LITE
// Now that we've scanned ACPI, try to initialize the ioapic(s)
acpi_process_madt_entries_etc(ACPI_MADT_TYPE_IO_APIC, &io_apic_callback, NULL);
#endif
}