📄 intr.c
字号:
/*
* Copyright 1999, 2000, 2001, 2002 Lucent Technologies Inc.
* All Rights Reserved.
* Information Sciences Research Center, Bell Labs.
*
* LUCENT TECHNOLOGIES DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE
* OR THE SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE. The
* software is provided "as is" without expressed or implied warranty
* of any kind.
*
* These notices must be retained in any copies of any part of this
* software.
*
*/
/*
* interrupts dispatcher.
*
* This code can be compiled either as a separate component, or as
* inside the nucleus.
* The symbolic variable NUCLEUS is defined when compiling it inside
* the nucleus.
* The symbolic variable INTR_INSIDE has the value 0 if the interrupts
* dispatcher is a separate component, and 1 if it is included inside the
* nucleus.
*
* Note that this source file is included in the nucleus even when the
* interrupts dispatcher is a separate component.
* In that case, the preprocessor ignores the body of this file.
*
* As a separate component, runs in user mode with interrupts disabled.
* In the nucleus, runs in kernel mode with interrupts disabled.
*
* Must call only the scheduler. Must not call any other protection
* domain that enables interrupts.
*/
#include "inside.h"
#if !defined(NUCLEUS) || (defined(NUCLEUS) && (INTR_INSIDE == 1))
/* now we know that if NUCLEUS is defined, then INTR_INSIDE is also 1 */
#include "string.h"
#include "machine/cpu.h"
#if defined(P5064) || defined(P4032)
#include "sbd.h"
#else
#include "qube.h"
#endif
#include "pebble.h"
#include "mem.h"
#include "log.h"
#include "diag.h"
#include "synch.h"
#include "bitmap.h"
#include "nucleus.h"
#ifdef NUCLEUS
#include "nucleus.h"
#endif
/* the nucleus must call kprintf() and kpanic() and not printf(), panic() */
#ifdef NUCLEUS
# define PRINTF kprintf
# define PANIC kpanic
#else
# define PRINTF printf
# define PANIC panic
#endif
#ifndef Cobalt
/* the ICU registers are mapped to uncached memory */
/* inside the nucleus, they are mapped to KSEG1. */
/* in a separate protection domain, they are mapped to an I/O window, */
/* which is mapped to uncached memory. */
#ifdef P4032
# ifdef NUCLEUS
# define icu ((volatile p4032icu *) PA_TO_KVA1(ICU_BASE))
# else
# define icu ((volatile p4032icu *) (ICU_BASE + IO_BASE))
# endif
# define DUP4(hw) DUP4MASK((hw)&INTR_HWMASK)
#elif defined(P5064)
# ifdef NUCLEUS
# define icu ((volatile p5064icu *) PA_TO_KVA1(ICU_BASE))
# else
# define icu ((volatile p5064icu *) (ICU_BASE + IO_BASE))
# endif
# define DUP4(hw) DUP4MASK((hw)&ICU_HWMASK)
#else /* !p4032, !p5064 */
# error no board type defined
#endif /* !p4032 */
#define DUP4MASK(m) ((m) | ((m) << 2) | ((m) << 4) | ((m) < 6))
#else /* Cobalt */
# ifdef NUCLEUS
# define icu ((volatile u_int32_t *) PA_TO_KVA1(ICU_BASE))
# else
# define icu ((volatile u_int32_t *) (ICU_BASE + IO_BASE))
# endif
#endif
static uint intr_mask = 0; /* currently enabled interrupts */
static uint direct_call_mask = 0; /* direct call interrupts mask */
static Func intr2call[NHW_INTR]; /* direct call functions */
static int intr2sem[NHW_INTR]; /* interrupt to semaphore lookup */
static int intr2sem_magic[NHW_INTR]; /* interrupt to semaphore magic */
/* constant */
static const char INV_INTR[] = "invalid interrupt number";
static const char DEF_INTR[] = "interrupt previously defined";
static const char UNDEF_INTR[] = "undefined interrupt";
static const char CREATE_INTR[] = "cannot create semaphore for interrupt";
/* enable/disable a single interrupt on the ICU */
/* use the global bitmask intr_mask for the currently enabled interrupts */
static void
icu_set(int intr_no)
{
#ifdef P4032
if (intr_no < 8)
icu->ctrl.mask = intr_mask & 0xff;
else
icu->ctrl.pcimask = (intr_mask >> 8) & 0xff;
#elif defined(P5064)
if (intr_no < 8)
icu->ctrl.devmask = intr_mask & 0xff;
else if (intr_no < 16)
/* not entirely correct, since write here resets interrupts */
icu->ctrl.clear = (intr_mask >> 8) & 0xff;
else if (intr_no < 24)
icu->ctrl.pcimask = (intr_mask >> 16) & 0xff;
else /* intr_no >= 24 */
icu->ctrl.isamask = (intr_mask >> 24) & 0xff;
#elif defined(Cobalt)
switch (intr_no) {
case HW_INTR_TULIP0:
call_portal(SYS_OR_PSW, 0);
break;
default:
}
#endif
wbflush();
}
/* enable/disable all interrupts on the ICU */
/* use the global bitmask intr_mask for the currently enabled interrupts */
static void
icu_setmask(uint pending)
{
#ifdef P4032
icu->ctrl.mask = intr_mask & 0xff;
/* note: must explicitly clear error interrupts */
icu->ctrl.clear = 0xff;
icu->ctrl.pcimask = (intr_mask >> 8) & 0xf0;
#elif defined(P5064)
icu->ctrl.devmask = intr_mask & 0xff;
/* note: must explicitly clear error interrupts */
icu->ctrl.clear = ICU_ERR_DEBUG | ((pending >> 8) & 0xff);
icu->ctrl.pcimask = (intr_mask >> 16) & 0xff;
icu->ctrl.isamask = (intr_mask >> 24) & 0xff;
#endif
wbflush();
}
/*
* define semaphore for interrupt.
* must call sys_intr_enable later.
*/
int
sys_intr_define(Thread *t, int intr_no)
{
if (intr_no < 0 || intr_no >= NHW_INTR ||
((1 << intr_no) & HW_INTR_INVMASK))
error(INV_INTR);
if (intr2sem[intr_no] != 0 || intr2call[intr_no] != NULL)
error(DEF_INTR);
if ((intr2sem[intr_no] = sem_create(0)) < 0)
error(CREATE_INTR);
/* debug: should use SEM2PORT() here! */
intr2sem_magic[intr_no] = portal_magic(intr2sem[intr_no], 0);
if (intr2sem_magic[intr_no] == -1)
error(CREATE_INTR);
return intr2sem[intr_no];
}
#ifdef NUCLEUS
/* define a direct call interrupt handler */
/* disable the interrupt and if "handler" is NULL */
int
intr_direct_call(int intr_no, Func handler)
{
if (intr_no < 0 || intr_no >= NHW_INTR ||
((1 << intr_no) & HW_INTR_INVMASK)) {
set_error(INV_INTR);
return -1;
}
if (handler != NULL) {
if (intr2sem[intr_no] != 0 || intr2call[intr_no] != NULL) {
set_error(DEF_INTR);
return -1;
}
intr2call[intr_no] = handler;
/* enable the interrupt */
intr_mask |= 1 << intr_no;
direct_call_mask |= 1 << intr_no;
icu_set(intr_no);
} else {
/* handler == NULL */
if (intr2call[intr_no] == NULL) {
set_error(DEF_INTR);
return -1;
}
intr2call[intr_no] = NULL;
/* disable the interrupt */
intr_mask &= ~(1 << intr_no);
direct_call_mask &= ~(1 << intr_no);
icu_set(intr_no);
}
return 0;
}
#endif /* NUCLEUS */
/* get pending interrupts */
static uint
get_pending(void)
{
#ifdef P4032
return ((icu->irr.dev) & 0xff) | ((icu->irr.pci & 0xf0) << 8);
#elif defined(P5064)
return (icu->irr.dev & 0xbf) | ((icu->irr.err & 0xdf) << 8) |
((icu->irr.pci & 0xff) << 16) | ((icu->irr.isa & 0x07) << 24);
#elif defined(Cobalt)
return *icu;
#endif
}
/*
* Enable interrupt #intr_no.
* The hardware interrupt source must be cleared before this call,
* otherwise the interrupt handler will be called again.
*/
int
sys_intr_enable(Thread *t, int intr_no)
{
uint pending;
if (intr_no < 0 || intr_no >= NHW_INTR ||
(1 << intr_no) & HW_INTR_INVMASK)
error(INV_INTR);
if (intr2sem[intr_no] == 0)
error(UNDEF_INTR);
#ifdef DEBUG_IDE
if (intr_no == HW_INTR_IDE0)
write(2, "enabling ide0\r\n", 15);
#endif
pending = get_pending();
if (INTR_LOG) {
put_log(INTR_LOG_ENABLE, intr_no);
put_log(INTR_LOG_PENDING, pending);
}
if (pending & (1 << intr_no)) {
/* this interrupt is pending, so deliver it immediately */
sem_post(intr2sem[intr_no]);
/* and disable it */
intr_mask &= ~(1 << intr_no);
} else { /* interrupt is not pending, so enable it */
intr_mask |= (1 << intr_no);
}
icu_set(intr_no);
return 0;
}
/*
* This is the interrupt dispatcher.
* It can only call the scheduler before disabling interrupt sources.
* It must not enable interrupts (even indirectly) before disabling
* interrupt sources.
*
* The dispatcher must return to the interrupted domain using full
* context restore. Regular return will restore only partical context.
* Must use run() to restore entire context.
*
* This routine MUST NOT call printf(), since the serial driver enables
* interrupts!
*/
static void
intr_dispatcher(Thread *t, int asid, int switched_stack)
{
uint pending = 0, direct_pending, cause;
int i;
cause = get_cause();
/* is it a regular interrupt? */
if (cause & (CR_HINT0 | CR_HINT1 | CR_HINT2 |
CR_HINT3 | CR_HINT4 | CR_HINT5)) {
pending = get_pending();
if (INTR_LOG) {
put_log(INTR_LOG_CAUSE, cause);
put_log(INTR_LOG_HW_PENDING, pending);
put_log(INTR_LOG_MASK, intr_mask);
}
/* only deliver interrupts that are enabled */
pending &= intr_mask;
/* disable further interrupts from pending sources */
intr_mask &= ~pending;
icu_setmask(pending);
if (INTR_LOG) {
put_log(INTR_LOG_PENDING, pending);
}
#ifndef Cobalt
/* debug interrupt is always enabled */
if ((cause & CR_HINT4) && switched_stack)
call_portal(SYS_DEBUG_HANDLER);
#endif
/* timer interrupt is always enabled */
if (cause & CR_HINT5)
call_portal(SYS_TIMER_HANDLER, (pending == 0) ? asid:0);
#ifdef Cobalt /* primary ethernet */
if (cause & CR_HINT1) {
/* disable further interrupts from this source */
call_portal(SYS_AND_PSW, 0);
intr_mask &= ~HW_INTR_TULIP0;
sem_post(intr2sem[HW_INTR_TULIP0]);
}
#endif
/* deliver direct calls first */
direct_pending = pending & direct_call_mask;
if (direct_pending != 0) {
while ((i = intmap_ffs(&direct_pending)) >= 0) {
if (intr2call[i] == NULL)
PANIC("intr2call[%d] is NULL", i);
(*intr2call[i])();
direct_pending &= ~(1 << i);
}
/* enable direct call interrupts again */
direct_pending = pending & direct_call_mask;
intr_mask |= direct_pending;
pending &= ~direct_pending;
icu_setmask(pending);
}
/* deliver pending interrupts */
while ((i = intmap_ffs(&pending)) >= 0) {
pending &= ~(1 << i);
if (intr2sem[i] == 0)
PANIC("intr2sem[%d] is 0", i);
if (pending == 0) {
/* optimize the last call to the scheduler */
sem_post_sched(t, -asid, intr2sem_magic[i]);
} else {
/* not the last call to the scheduler */
sem_post(intr2sem[i]);
}
}
}
sched(t, asid, 0);
}
/* interrupts initialization */
#ifdef NUCLEUS
void
inside_intr_init(void)
{
kprintf("in-nucleus interrupts dispatcher\n");
/* verify that we run in kernel mode, ASID == 0, interrupts disabled */
if (get_asid() != ASID_NUCLEUS)
kpanic("inside_intr_init: asid must be %d; current asid=%d\n",
ASID_NUCLEUS, get_asid());
if (!check_psw(0, 0))
kpanic("inside_intr_init: invalid processor status: %08x\n",
get_psw());
#else
/* interrupt dispatcher in separate component */
int main()
{
printf("interrupts dispatcher active\n");
/* verify that we are running in user mode with interrupts disabled */
if (!check_psw(1,0)) {
printf("ICU: invalid processor status: %08lx\n", get_psw());
task_exit(1);
}
#endif
/* disable all interrupts */
intr_mask = 0;
#ifdef P4032
icu->ctrl.mask = 0; /* disable local devices interrupts */
icu->ctrl.clear = 0xff; /* clear panic interrupts */
icu->ctrl.pcimask = 0; /* disable PCI device interrupts */
/* all on-board devices generate CPU intr #0 */
icu->ctrl.devxbar0 = DUP4(INTR_HWINT0);
icu->ctrl.devxbar1 = DUP4(INTR_HWINT0);
/* all PCI devices generate CPU intr #1 */
icu->ctrl.pcixbar = DUP4(INTR_HWINT1);
#elif defined(P5064)
icu->ctrl.devmask = 0; /* disable local devices interrupts */
icu->ctrl.clear = 0xff; /* clear panic interrupts */
icu->ctrl.pcimask = 0; /* disable PCI devices interrupts */
icu->ctrl.isamask = 0; /* disable ISA devices interrupts */
/* all on-board devices generate CPU intr #0 */
icu->ctrl.devxbar0 = DUP4(ICU_HWINT0); /* com1, kbd, ftp, pcibr */
icu->ctrl.devxbar1 = DUP4(ICU_HWINT0); /* timer, gpio, cent, com2 */
icu->ctrl.isaxbar = DUP4(ICU_HWINT0); /* -, ide1, ide0, isabr */
icu->ctrl.lpcixbar = DUP4(ICU_HWINT0); /* usb, scsi, ether, - */
/* all off-board (PCI) devices generate CPU intr #1 */
icu->ctrl.xpcixbar = DUP4(ICU_HWINT1); /* pci3, pci2, pci1, pci0 */
#endif
/* inside the nucleus */
if (portal_create(SYS_INTR_DEFINE, "smtiii", 0, sys_intr_define, 0) < 0)
PANIC("portal_create for intr_define failed\n");
if (portal_create(SYS_INTR_ENABLE, "smtiii", 0, sys_intr_enable, 0) < 0)
PANIC("portal_create for intr_enable() failed\n");
/* note: register A2 is != 0 if portal code switched stacks */
if (portal_create(EXC_INTR, "eftaii", 0, (Func) intr_dispatcher, 0) < 0)
PANIC("portal_create_excpt for intr_dispatcher failed\n");
#ifndef NUCLEUS
/*
* return to initialization code.
* cannot just "return", since the startup code (crt0.S) calls
* exit when main routine terminates.
*/
call_portal(SYS_RTN_RPC);
return(1);
#endif
}
#endif /* generated code for separate component or inside nucleus */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -