⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 intr.c

📁 pebble
💻 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 + -