{install|remove}_io_interrupt_handler() now correctly handle the B_NO_ENABLE_COUNTER flag.

Lots of cleanup:
- moved B_NO_ENABLE_COUNTER flag definition out of int.h to KernelExport.h, as it's
  described in the BeBook (although it's probably not really used that often :))
- int.c no longer has any platform dependent code (+ 0x20 on interrupt numbers is gone);
  it's now entirely handled in the arch/x86/ section.
- the io_vectors[] is now statically initialized, instead of allocated from the heap
- removed {install|remove}_interrupt_handler(); they weren't that useful, arch_smp_init()
  is now calling install_io_interrupt_handler() correctly instead
- introduced a new arch_int.h header file that currently contains NUM_IO_VECTORS only
  (though on x86, it also has ARCH_INTERRUPT_BASE == 0x20).
- changed the return type from {install|remove}_io_interrupt_handler() from "long" to
  "status_t"
- rearranged and cleaned the PIC initialization code, made the PIC code more prominent
- changed comments that talk about a non existing 8239 (the PIC chip is actually 8259)
- moved arch/x86/interrupts.h to the source directory, as it's not used outside
- added BeOS compatible interrupts_enabled() function, that should replace our
  equivalent (and private) are_interrupts_enabled()


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@12477 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2005-04-27 01:08:35 +00:00
parent a38e60f223
commit b20667b35b
12 changed files with 239 additions and 160 deletions
+11 -6
View File
@@ -1,7 +1,8 @@
/* Kernel only exports for kernel add-ons
**
** Distributed under the terms of the OpenBeOS License.
*/
*
* Copyright 2005, Haiku Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _KERNEL_EXPORT_H
#define _KERNEL_EXPORT_H
@@ -15,6 +16,7 @@ extern "C" {
#endif
/*-------------------------------------------------------------*/
/* interrupts and spinlocks */
/* disable/restore interrupts on the current CPU */
@@ -36,16 +38,19 @@ extern void release_spinlock(spinlock *lock);
/* interrupt handling support for device drivers */
typedef int32 (*interrupt_handler)(void *data);
/* Values returned by interrupt handlers */
#define B_UNHANDLED_INTERRUPT 0 /* pass to next handler */
#define B_HANDLED_INTERRUPT 1 /* don't pass on */
#define B_INVOKE_SCHEDULER 2 /* don't pass on; invoke the scheduler */
typedef int32 (*interrupt_handler)(void *data);
/* Flags that can be passed to install_io_interrupt_handler() */
#define B_NO_ENABLE_COUNTER 1
extern long install_io_interrupt_handler(long interrupt_number,
extern status_t install_io_interrupt_handler(long interrupt_number,
interrupt_handler handler, void *data, ulong flags);
extern long remove_io_interrupt_handler(long interrupt_number,
extern status_t remove_io_interrupt_handler(long interrupt_number,
interrupt_handler handler, void *data);
+7 -6
View File
@@ -1,15 +1,16 @@
/*
** Copyright 2002-2004, The Haiku Team. All rights reserved.
** Distributed under the terms of the Haiku License.
**
** Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
** Distributed under the terms of the NewOS License.
*/
* Copyright 2002-2005, Axel Dörfler, [email protected]. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#ifndef KERNEL_ARCH_INT_H
#define KERNEL_ARCH_INT_H
#include <boot/kernel_args.h>
#include <arch_int.h>
#ifdef __cplusplus
@@ -0,0 +1,12 @@
/*
* Copyright 2005, Axel Dörfler, [email protected]. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _KERNEL_ARCH_PPC_INT_H
#define _KERNEL_ARCH_PPC_INT_H
#define NUM_IO_VECTORS 256
#endif /* _KERNEL_ARCH_PPC_INT_H */
@@ -0,0 +1,13 @@
/*
* Copyright 2005, Axel Dörfler, [email protected]. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _KERNEL_ARCH_x86_INT_H
#define _KERNEL_ARCH_x86_INT_H
#define ARCH_INTERRUPT_BASE 0x20
#define NUM_IO_VECTORS (256 - ARCH_INTERRUPT_BASE)
#endif /* _KERNEL_ARCH_x86_INT_H */
+2 -6
View File
@@ -15,10 +15,6 @@
struct kernel_args;
/* adds the handler but don't change whether or not the interrupt is currently enabled */
#define B_NO_ENABLE_COUNTER 1
#ifdef __cplusplus
extern "C" {
#endif
@@ -26,8 +22,8 @@ extern "C" {
status_t int_init(struct kernel_args *args);
status_t int_init_post_vm(struct kernel_args *args);
int int_io_interrupt_handler(int vector);
status_t install_interrupt_handler(long vector, interrupt_handler, void *data);
status_t remove_interrupt_handler(long vector, interrupt_handler, void *data);
bool interrupts_enabled(void);
static inline void
enable_interrupts(void)
+8 -7
View File
@@ -1,20 +1,21 @@
/*
** Copyright 2002-2004, The Haiku Team. All rights reserved.
** Distributed under the terms of the Haiku License.
**
** Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
** Distributed under the terms of the NewOS License.
*/
* Copyright 2002-2005, Axel Dörfler, [email protected]. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#include <arch/cpu.h>
#include <vm.h>
#include <smp.h>
#include <arch/x86/selector.h>
#include <arch/x86/interrupts.h>
#include <tls.h>
#include <boot/kernel_args.h>
#include "interrupts.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
+97 -34
View File
@@ -20,12 +20,49 @@
#include <arch/smp.h>
#include <arch/user_debugger.h>
#include <arch/x86/interrupts.h>
#include <arch/x86/faults.h>
#include <arch/x86/descriptors.h>
#include "interrupts.h"
#include <string.h>
//#define TRACE_ARCH_INT
#ifdef TRACE_ARCH_INT
# 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
#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_NON_SPECIFIC_EOI 0x20
#define PIC_INT_BASE 0x20
#define PIC_SLAVE_INT_BASE 0x28
#define PIC_NUM_INTS 0x0f
const char *kInterruptNames[] = {
/* 0 */ "Divide Error Exception",
/* 1 */ "Debug Exception",
@@ -59,18 +96,6 @@ static desc_table *idt = NULL;
struct iframe_stack gBootFrameStack;
static void
interrupt_ack(int n)
{
if (n >= 0x20 && n < 0x30) {
// 8239 controlled interrupt
if (n > 0x27)
out8(0x20, 0xa0); // EOI to pic 2
out8(0x20, 0x20); // EOI to pic 1
}
}
static void
set_gate(desc_table *gate_addr, addr_t addr, int type, int dpl)
{
@@ -107,30 +132,77 @@ x86_set_task_gate(int32 n, int32 segment)
}
/** 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 void
pic_end_of_interrupt(int num)
{
if (num >= PIC_INT_BASE && num <= PIC_INT_BASE + PIC_NUM_INTS) {
// 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);
}
}
static void
pic_init(void)
{
// 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(PIC_INT_BASE, PIC_MASTER_INIT2);
out8(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.
}
void
arch_int_enable_io_interrupt(int irq)
{
if (irq < 0 || irq >= 0x10)
// interrupt is specified "normalized"
if (irq < 0 || irq > PIC_NUM_INTS)
return;
//dprintf("arch_int_enable_io_interrupt: irq %d\n", irq);
// enable PIC 8259 controlled interrupt
TRACE(("arch_int_enable_io_interrupt: irq %d\n", irq));
/* if this is a external interrupt via 8239, enable it here */
if (irq < 8)
out8(in8(0x21) & ~(1 << irq), 0x21);
out8(in8(PIC_MASTER_MASK) & ~(1 << irq), PIC_MASTER_MASK);
else
out8(in8(0xa1) & ~(1 << (irq - 8)), 0xa1);
out8(in8(PIC_SLAVE_MASK) & ~(1 << (irq - 8)), PIC_SLAVE_MASK);
}
void
arch_int_disable_io_interrupt(int irq)
{
/* never disable slave pic line IRQ 2 */
if (irq < 0 || irq >= 0x10 || irq == 2)
// interrupt is specified "normalized"
// never disable slave pic line IRQ 2
if (irq < 0 || irq > PIC_NUM_INTS || irq == 2)
return;
/* if this is a external interrupt via 8239, disable it here */
// disable PIC 8259 controlled interrupt
if (irq < 8)
out8(in8(0x21) | (1 << irq), 0x21);
else
@@ -282,9 +354,9 @@ i386_handle_trap(struct iframe frame)
}
default:
if (frame.vector >= 0x20) {
interrupt_ack(frame.vector); // ack the 8239 (if applicable)
ret = int_io_interrupt_handler(frame.vector);
if (frame.vector >= ARCH_INTERRUPT_BASE) {
pic_end_of_interrupt(frame.vector);
ret = int_io_interrupt_handler(frame.vector - ARCH_INTERRUPT_BASE);
} else {
panic("i386_handle_trap: unhandled trap 0x%x (%s) at ip 0x%x, thread 0x%x!\n",
frame.vector, kInterruptNames[frame.vector], frame.eip, thread ? thread->id : -1);
@@ -324,16 +396,7 @@ arch_int_init(kernel_args *args)
idt = (desc_table *)args->arch_args.vir_idt;
// setup the interrupt controller
out8(0x11, 0x20); // Start initialization sequence for #1.
out8(0x11, 0xa0); // ...and #2.
out8(0x20, 0x21); // Set start of interrupts for #1 (0x20).
out8(0x28, 0xa1); // Set start of interrupts for #2 (0x28).
out8(0x04, 0x21); // Set #1 to be the master.
out8(0x02, 0xa1); // Set #2 to be the slave.
out8(0x01, 0x21); // Set both to operate in 8086 mode.
out8(0x01, 0xa1);
out8(0xfb, 0x21); // Mask off all interrupts (except slave pic line IRQ 2).
out8(0xff, 0xa1); // Mask off interrupts on the slave.
pic_init();
set_intr_gate(0, &trap0);
set_intr_gate(1, &trap1);
+12 -10
View File
@@ -1,10 +1,11 @@
/*
** Copyright 2002-2004, Axel Dörfler, [email protected]. All rights reserved.
** Distributed under the terms of the Haiku License.
**
** Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
** Distributed under the terms of the NewOS License.
*/
* Copyright 2002-2005, Axel Dörfler, [email protected]. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#include <boot/kernel_args.h>
#include <vm.h>
@@ -120,10 +121,11 @@ arch_smp_init(kernel_args *args)
// set up the local apic on the boot cpu
arch_smp_per_cpu_init(args, 0);
install_interrupt_handler(0xfb, &i386_timer_interrupt, NULL);
install_interrupt_handler(0xfd, &i386_ici_interrupt, NULL);
install_interrupt_handler(0xfe, &i386_smp_error_interrupt, NULL);
install_interrupt_handler(0xff, &i386_spurious_interrupt, NULL);
// I/O interrupts start at ARCH_INTERRUPT_BASE, so all interrupts are shifted
install_io_interrupt_handler(0xfb - ARCH_INTERRUPT_BASE, &i386_timer_interrupt, NULL, 0);
install_io_interrupt_handler(0xfd - ARCH_INTERRUPT_BASE, &i386_ici_interrupt, NULL, 0);
install_io_interrupt_handler(0xfe - ARCH_INTERRUPT_BASE, &i386_smp_error_interrupt, NULL, 0);
install_io_interrupt_handler(0xff - ARCH_INTERRUPT_BASE, &i386_spurious_interrupt, NULL, 0);
}
return B_OK;
}
+19 -13
View File
@@ -1,7 +1,11 @@
/*
** Copyright 2001, Travis Geiselbrecht. All rights reserved.
** Distributed under the terms of the NewOS License.
*/
/*
* Copyright 2005, Axel Dörfler, [email protected]. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Copyright 2001, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#include <boot/stage2.h>
#include <kernel.h>
@@ -16,23 +20,25 @@
#include <arch/timer.h>
#include <arch/x86/interrupts.h>
#include <arch/x86/smp_priv.h>
#include <arch/x86/timer.h>
#define pit_clock_rate 1193180
#define pit_max_timer_interval ((long long)0xffff * 1000000 / pit_clock_rate)
#include "interrupts.h"
#define PIT_CLOCK_RATE 1193180
#define PIT_MAX_TIMER_INTERVAL ((long long)0xffff * 1000000 / PIT_CLOCK_RATE)
static void
set_isa_hardware_timer(long long relative_timeout)
{
unsigned short next_event_clocks;
uint16 next_event_clocks;
if (relative_timeout <= 0)
next_event_clocks = 2;
else if (relative_timeout < pit_max_timer_interval)
next_event_clocks = relative_timeout * pit_clock_rate / 1000000;
else if (relative_timeout < PIT_MAX_TIMER_INTERVAL)
next_event_clocks = relative_timeout * PIT_CLOCK_RATE / 1000000;
else
next_event_clocks = 0xffff;
@@ -84,10 +90,10 @@ arch_timer_clear_hardware_timer(void)
int
arch_init_timer(kernel_args *ka)
arch_init_timer(kernel_args *args)
{
dprintf("arch_init_timer: entry\n");
//dprintf("arch_init_timer: entry\n");
install_io_interrupt_handler(0, &isa_timer_interrupt, NULL, 0);
clear_isa_hardware_timer();
-1
View File
@@ -16,7 +16,6 @@
#include <arch/int.h>
#include <arch/cpu.h>
#include <arch/x86/interrupts.h>
#include <arch/x86/bios.h>
@@ -1,9 +1,17 @@
/*
** Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
** Distributed under the terms of the NewOS License.
*/
#ifndef _NEWOS_KERNEL_ARCH_I386_INTERRUPTS_H
#define _NEWOS_KERNEL_ARCH_I386_INTERRUPTS_H
/*
* Copyright 2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#ifndef _KERNEL_ARCH_x86_INTERRUPTS_H
#define _KERNEL_ARCH_x86_INTERRUPTS_H
#ifdef __cplusplus
extern "C" {
#endif
void trap0();void trap1();void trap2();void trap3();void trap4();void trap5();
void trap6();void trap7();void trap9();void trap10();void trap11();
@@ -17,5 +25,9 @@ void double_fault(); // int 8
void trap99();
void trap251();void trap252();void trap253();void trap254();void trap255();
#ifdef __cplusplus
}
#endif
#endif /* _KERNEL_ARCH_x86_INTERRUPTS_H */
+40 -71
View File
@@ -17,22 +17,29 @@
#include <stdio.h>
#include <malloc.h>
//#define TRACE_INT
#ifdef TRACE_INT
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
#define NUM_IO_VECTORS 256
struct io_handler {
struct io_handler *next;
struct io_handler *prev;
interrupt_handler func;
void *data;
bool use_enable_counter;
};
struct io_vector {
struct io_handler handler_list;
spinlock vector_lock;
int32 enable_count;
};
static struct io_vector *io_vectors = NULL;
static struct io_vector io_vectors[NUM_IO_VECTORS];
cpu_status
@@ -49,6 +56,13 @@ restore_interrupts(cpu_status status)
}
bool
interrupts_enabled(void)
{
return arch_int_are_interrupts_enabled();
}
status_t
int_init(kernel_args *args)
{
@@ -63,13 +77,10 @@ int_init_post_vm(kernel_args *args)
{
int i;
io_vectors = (struct io_vector *)malloc(sizeof(struct io_vector) * NUM_IO_VECTORS);
if (io_vectors == NULL)
panic("int_init_post_vm: could not create io vector table!\n");
/* initialize the vector list */
for (i = 0; i < NUM_IO_VECTORS; i++) {
io_vectors[i].vector_lock = 0; /* initialize spinlock */
io_vectors[i].enable_count = 0;
initque(&io_vectors[i].handler_list); /* initialize handler queue */
}
@@ -77,14 +88,12 @@ int_init_post_vm(kernel_args *args)
}
/** This function is used internally to install a handler on the given vector.
* NB this does NOT take an IRQ, but a system interrupt value.
* As this is intended for system use this function does NOT call
* arch_int_enable_io_interrupt() as it only works for IRQ values
/** Install a handler to be called when an interrupt is triggered
* for the given interrupt number with \a data as the argument.
*/
status_t
install_interrupt_handler(long vector, interrupt_handler handler, void *data)
install_io_interrupt_handler(long vector, interrupt_handler handler, void *data, ulong flags)
{
struct io_handler *io = NULL;
cpu_status state;
@@ -92,25 +101,28 @@ install_interrupt_handler(long vector, interrupt_handler handler, void *data)
if (vector < 0 || vector >= NUM_IO_VECTORS)
return B_BAD_VALUE;
/* find the chain of handlers for this irq.
* NB there can be multiple handlers for the same IRQ, especially for
* PCI drivers. Where we have multiple handlers we will call each in turn
* until one returns a value other than B_UNHANDLED_INTERRUPT.
*/
io = (struct io_handler *)malloc(sizeof(struct io_handler));
if (io == NULL)
return B_NO_MEMORY;
io->func = handler;
io->data = data;
io->use_enable_counter = (flags & B_NO_ENABLE_COUNTER) == 0;
/* Disable the interrupts, get the spinlock for this irq only
* and then insert the handler */
// Disable the interrupts, get the spinlock for this irq only
// and then insert the handler
state = disable_interrupts();
acquire_spinlock(&io_vectors[vector].vector_lock);
insque(io, &io_vectors[vector].handler_list);
// If B_NO_ENABLE_COUNTER is set, we're being asked to not alter
// whether the interrupt should be enabled or not
if (io->use_enable_counter) {
if (io_vectors[vector].enable_count++ == 0)
arch_int_enable_io_interrupt(vector);
}
release_spinlock(&io_vectors[vector].vector_lock);
restore_interrupts(state);
@@ -118,41 +130,14 @@ install_interrupt_handler(long vector, interrupt_handler handler, void *data)
}
/** install a handler to be called when an interrupt is triggered
* for the given irq with data as the argument
*/
long
install_io_interrupt_handler(long irq, interrupt_handler handler, void *data, ulong flags)
{
// ToDo: this is x86 specific
long vector = irq + 0x20;
status_t status = install_interrupt_handler(vector, handler, data);
if (status != B_OK)
return status;
/* If we were passed the bit-flag B_NO_ENABLE_COUNTER then
* we're being asked to not alter whether the interrupt is set
* regardless of setting.
*/
if ((flags & B_NO_ENABLE_COUNTER) == 0)
arch_int_enable_io_interrupt(irq);
return B_OK;
}
/** Removes and interrupt handler.
* Read the notes for install_interrupt_handler!
*/
/** Remove a previously installed interrupt handler */
status_t
remove_interrupt_handler(long vector, interrupt_handler handler, void *data)
remove_io_interrupt_handler(long vector, interrupt_handler handler, void *data)
{
struct io_handler *io = NULL;
status_t status = B_BAD_VALUE;
int state;
struct io_handler *io = NULL;
cpu_status state;
if (vector < 0 || vector >= NUM_IO_VECTORS)
return B_BAD_VALUE;
@@ -171,18 +156,20 @@ remove_interrupt_handler(long vector, interrupt_handler handler, void *data)
/* we have to match both function and data */
if (io->func == handler && io->data == data) {
remque(io);
// Check if we need to disable the interrupt
if (io->use_enable_counter && --io_vectors[vector].enable_count == 0)
arch_int_disable_io_interrupt(vector);
status = B_OK;
break;
}
}
/* to finish we need to release our locks and return
* the value rv
*/
release_spinlock(&io_vectors[vector].vector_lock);
restore_interrupts(state);
/* if the handler could be found and removed, we still have to free it */
// if the handler could be found and removed, we still have to free it
if (status == B_OK)
free(io);
@@ -190,24 +177,6 @@ remove_interrupt_handler(long vector, interrupt_handler handler, void *data)
}
/** remove an interrupt handler previously inserted */
long
remove_io_interrupt_handler(long irq, interrupt_handler handler, void *data)
{
long vector = irq + 0x20;
status_t status = remove_interrupt_handler(vector, handler, data);
if (status < B_OK)
return status;
/* Check if we need to disable interrupts... */
if (io_vectors[vector].handler_list.next != &io_vectors[vector].handler_list)
arch_int_disable_io_interrupt(irq);
return B_OK;
}
/** actually process an interrupt via the handlers registered for that
* vector (irq)
*/